From f5761cd94708a533eff7d3d5a8c0b05952eabc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20R=C3=B6hling?= Date: Mon, 11 Nov 2024 15:18:55 +0100 Subject: [PATCH] New upstream version 3.31.0 --- .clang-tidy | 14 +- Auxiliary/bash-completion/cmake | 36 +- Auxiliary/cmake-mode.el | 2 +- Auxiliary/vim/syntax/cmake.vim | 1 + CMakeLists.txt | 4 +- CONTRIBUTING.rst | 8 +- CTestCustom.cmake.in | 3 +- CompileFlags.cmake | 8 +- Help/command/DEPRECATED_POLICY_VERSIONS.txt | 8 + Help/command/add_custom_command.rst | 103 +- Help/command/cmake_parse_arguments.rst | 161 +- Help/command/cmake_pkg_config.rst | 263 ++ Help/command/execute_process.rst | 22 +- Help/command/file.rst | 131 +- Help/command/find_package.rst | 52 +- Help/command/find_program.rst | 20 + Help/command/get_property.rst | 27 +- Help/command/if.rst | 10 +- Help/command/include_external_msproject.rst | 9 +- Help/command/install.rst | 97 +- Help/command/project.rst | 31 +- Help/command/target_link_libraries.rst | 5 + Help/cpack_gen/archive.rst | 8 +- Help/cpack_gen/deb.rst | 36 + Help/cpack_gen/ifw.rst | 10 + Help/cpack_gen/rpm.rst | 10 +- Help/cpack_gen/wix.rst | 8 +- Help/dev/README.rst | 7 +- Help/dev/documentation.rst | 18 +- Help/dev/experimental.rst | 41 + Help/dev/integration-testing.rst | 46 + Help/dev/maint.rst | 15 +- Help/dev/review.rst | 4 +- Help/dev/source.rst | 10 + Help/dev/testing.rst | 126 +- Help/dev/try_compile-linker-language.rst | 6 - Help/envvar/CMAKE_CONFIG_DIR.rst | 18 + Help/envvar/CMAKE_EXPORT_BUILD_DATABASE.rst | 17 + Help/envvar/CMAKE_INSTALL_PARALLEL_LEVEL.rst | 11 + Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst | 2 +- Help/generator/Visual Studio 12 2013.rst | 59 +- Help/guide/user-interaction/index.rst | 2 + Help/manual/cmake-commands.7.rst | 1 + Help/manual/cmake-env-variables.7.rst | 5 +- Help/manual/cmake-file-api.7.rst | 4 + Help/manual/cmake-generator-expressions.7.rst | 8 + Help/manual/cmake-modules.7.rst | 2 +- Help/manual/cmake-policies.7.rst | 17 + Help/manual/cmake-presets.7.rst | 66 +- Help/manual/cmake-properties.7.rst | 4 + Help/manual/cmake-toolchains.7.rst | 16 +- Help/manual/cmake-variables.7.rst | 11 + Help/manual/cmake.1.rst | 43 +- Help/manual/ctest.1.rst | 9 + Help/manual/presets/example.json | 8 +- Help/manual/presets/schema.json | 1870 ++++++++++++--- .../CMAKE_REQUIRED_LINK_DIRECTORIES.txt | 5 + Help/policy/CMP0156.rst | 5 + Help/policy/CMP0171.rst | 26 + Help/policy/CMP0172.rst | 39 + Help/policy/CMP0173.rst | 22 + Help/policy/CMP0174.rst | 37 + Help/policy/CMP0175.rst | 40 + Help/policy/CMP0176.rst | 27 + Help/policy/CMP0177.rst | 38 + Help/policy/CMP0178.rst | 37 + Help/policy/CMP0179.rst | 28 + Help/policy/CMP0180.rst | 36 + .../VS_GLOBAL_SECTION_POST_section.rst | 2 +- .../VS_GLOBAL_SECTION_PRE_section.rst | 2 +- Help/prop_gbl/INSTALL_PARALLEL.rst | 20 +- Help/prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE.rst | 20 + Help/prop_tgt/COMPILE_FLAGS.rst | 7 +- Help/prop_tgt/EXPORT_BUILD_DATABASE.rst | 15 + Help/prop_tgt/LINK_LIBRARIES.rst | 5 + Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst | 87 + Help/prop_tgt/MACOSX_FRAMEWORK_INFO_PLIST.rst | 8 + Help/prop_tgt/UNITY_BUILD.rst | 3 + Help/prop_tgt/VS_DEBUGGER_COMMAND.rst | 2 +- .../VS_DEBUGGER_COMMAND_ARGUMENTS.rst | 2 +- Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst | 2 +- .../VS_DEBUGGER_WORKING_DIRECTORY.rst | 2 +- Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst | 2 +- Help/prop_tgt/VS_FRAMEWORK_REFERENCES.rst | 12 + Help/prop_tgt/VS_KEYWORD.rst | 2 +- Help/release/3.29.rst | 4 + Help/release/3.31.rst | 259 ++ Help/release/index.rst | 1 + .../CMAKE_AIX_SHARED_LIBRARY_ARCHIVE.rst | 10 + Help/variable/CMAKE_CFG_INTDIR.rst | 2 +- Help/variable/CMAKE_EXECUTABLE_SUFFIX.rst | 6 +- Help/variable/CMAKE_EXPORT_BUILD_DATABASE.rst | 86 + .../CMAKE_FIND_PACKAGE_SORT_ORDER.rst | 19 +- .../CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.rst | 2 +- .../variable/CMAKE_HOST_EXECUTABLE_SUFFIX.rst | 12 + Help/variable/CMAKE_LANG_COMPILER_ID.rst | 3 +- ...AKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE.rst | 10 + Help/variable/CMAKE_LANG_HOST_COMPILER.rst | 5 + Help/variable/CMAKE_LANG_HOST_COMPILER_ID.rst | 10 + .../CMAKE_LANG_HOST_COMPILER_VERSION.rst | 10 + .../CMAKE_LANG_STANDARD_LINK_DIRECTORIES.rst | 14 + .../CMAKE_LINK_DEPENDS_USE_LINKER.rst | 5 +- .../CMAKE_LINK_LIBRARIES_STRATEGY.rst | 11 + Help/variable/CMAKE_MAKE_PROGRAM.rst | 7 +- Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst | 4 +- .../variable/CMAKE_POLICY_WARNING_CMPNNNN.rst | 2 + .../CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX.rst | 9 + Help/variable/CMAKE_SYSTEM_NAME.rst | 1 + Help/variable/CMAKE_TLS_VERIFY.rst | 7 +- Help/variable/CMAKE_TLS_VERSION.rst | 5 + Help/variable/CMAKE_VS_DEVENV_COMMAND.rst | 11 +- ...CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst | 2 +- Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst | 10 +- .../CMAKE_VS_NUGET_PACKAGE_RESTORE.rst | 7 +- Help/variable/CMAKE_WINDOWS_KMDF_VERSION.rst | 13 + Help/variable/CTEST_TLS_VERIFY.rst | 6 + Help/variable/WASI.rst | 7 + Modules/AndroidTestUtilities.cmake | 3 +- Modules/CMakeASMInformation.cmake | 1 + Modules/CMakeASM_MARMASMInformation.cmake | 1 + Modules/CMakeASM_MASMInformation.cmake | 1 + Modules/CMakeAddNewLanguage.txt | 16 +- Modules/CMakeCInformation.cmake | 2 + Modules/CMakeCSharpInformation.cmake | 3 + Modules/CMakeCUDACompiler.cmake.in | 2 + Modules/CMakeCUDACompilerId.cu.in | 28 + Modules/CMakeCUDAInformation.cmake | 2 + Modules/CMakeCXXInformation.cmake | 2 + Modules/CMakeCommonLanguageInclude.cmake | 9 - Modules/CMakeDetermineCSharpCompiler.cmake | 4 +- Modules/CMakeDetermineCompilerId.cmake | 51 +- Modules/CMakeDetermineFortranCompiler.cmake | 4 + Modules/CMakeDetermineHIPCompiler.cmake | 2 +- Modules/CMakeFindBinUtils.cmake | 94 +- Modules/CMakeFindFrameworks.cmake | 22 + Modules/CMakeFortranCompilerId.F.in | 5 + Modules/CMakeFortranInformation.cmake | 2 + Modules/CMakeGenericSystem.cmake | 7 + Modules/CMakeHIPCompiler.cmake.in | 2 + Modules/CMakeHIPCompilerId.hip.in | 28 + Modules/CMakeHIPInformation.cmake | 2 + Modules/CMakeISPCInformation.cmake | 2 + Modules/CMakeJavaInformation.cmake | 2 + Modules/CMakeOBJCInformation.cmake | 2 + Modules/CMakeOBJCXXInformation.cmake | 2 + Modules/CMakePackageConfigHelpers.cmake | 7 + Modules/CMakeParseImplicitLinkInfo.cmake | 24 +- Modules/CMakePushCheckState.cmake | 32 +- Modules/CMakeRCInformation.cmake | 2 + Modules/CMakeSwiftInformation.cmake | 11 +- Modules/CPack.cmake | 14 + Modules/CheckCCompilerFlag.cmake | 31 +- Modules/CheckCSourceCompiles.cmake | 30 +- Modules/CheckCSourceRuns.cmake | 24 +- Modules/CheckCXXCompilerFlag.cmake | 43 +- Modules/CheckCXXSourceCompiles.cmake | 30 +- Modules/CheckCXXSourceRuns.cmake | 24 +- Modules/CheckCXXSymbolExists.cmake | 2 + Modules/CheckCompilerFlag.cmake | 28 +- Modules/CheckFortranCompilerFlag.cmake | 31 +- Modules/CheckFortranFunctionExists.cmake | 22 +- Modules/CheckFortranSourceCompiles.cmake | 45 +- Modules/CheckFortranSourceRuns.cmake | 31 +- Modules/CheckFunctionExists.cmake | 19 +- Modules/CheckIncludeFile.cmake | 11 + Modules/CheckIncludeFileCXX.cmake | 11 + Modules/CheckIncludeFiles.cmake | 11 + Modules/CheckLibraryExists.cmake | 20 +- Modules/CheckOBJCCompilerFlag.cmake | 31 +- Modules/CheckOBJCSourceCompiles.cmake | 30 +- Modules/CheckOBJCSourceRuns.cmake | 26 +- Modules/CheckOBJCXXCompilerFlag.cmake | 31 +- Modules/CheckOBJCXXSourceCompiles.cmake | 30 +- Modules/CheckOBJCXXSourceRuns.cmake | 26 +- Modules/CheckPrototypeDefinition.cmake | 11 + Modules/CheckSourceCompiles.cmake | 54 +- Modules/CheckSourceRuns.cmake | 22 +- Modules/CheckStructHasMember.cmake | 2 + Modules/CheckSymbolExists.cmake | 11 + Modules/CheckTypeSize.cmake | 11 + Modules/CheckVariableExists.cmake | 10 + Modules/Compiler/Clang-CUDA.cmake | 4 +- Modules/Compiler/Clang.cmake | 14 +- Modules/Compiler/GNU.cmake | 54 - Modules/Compiler/IAR-C.cmake | 4 +- Modules/Compiler/IAR-CXX.cmake | 6 +- Modules/Compiler/IntelLLVM.cmake | 2 +- Modules/Compiler/LFortran-Fortran.cmake | 14 + Modules/Compiler/MSVC.cmake | 2 +- Modules/Compiler/NVHPC-CXX.cmake | 7 + Modules/Compiler/SunPro-C.cmake | 4 + Modules/Compiler/SunPro-CXX.cmake | 4 + Modules/Compiler/Tasking-C.cmake | 6 +- Modules/Compiler/Tasking-CXX.cmake | 6 +- Modules/ExternalData.cmake | 18 +- Modules/ExternalProject.cmake | 22 +- Modules/ExternalProject/download.cmake.in | 2 +- Modules/ExternalProject/extractfile.cmake.in | 2 +- Modules/ExternalProject/gitclone.cmake.in | 2 +- Modules/ExternalProject/gitupdate.cmake.in | 2 +- Modules/ExternalProject/hgclone.cmake.in | 2 +- Modules/ExternalProject/mkdirs.cmake.in | 2 +- .../shared_internal_commands.cmake | 75 +- Modules/ExternalProject/stepscript.cmake.in | 2 +- Modules/ExternalProject/verify.cmake.in | 2 +- Modules/FindAVIFile.cmake | 5 +- Modules/FindBLAS.cmake | 14 +- Modules/FindBZip2.cmake | 6 +- Modules/FindCUDA.cmake | 4 +- Modules/FindCUDAToolkit.cmake | 48 +- Modules/FindCxxTest.cmake | 2 + Modules/FindDevIL.cmake | 6 +- Modules/FindEXPAT.cmake | 29 +- Modules/FindFontconfig.cmake | 4 +- Modules/FindGSL.cmake | 9 +- Modules/FindGnuTLS.cmake | 4 +- Modules/FindHDF5.cmake | 1 - Modules/FindIconv.cmake | 2 + Modules/FindImageMagick.cmake | 24 +- Modules/FindJPEG.cmake | 4 +- Modules/FindLAPACK.cmake | 20 +- Modules/FindLibXml2.cmake | 4 +- Modules/FindLibXslt.cmake | 8 +- Modules/FindLibinput.cmake | 4 +- Modules/FindMPI.cmake | 3 +- Modules/FindMatlab.cmake | 54 +- Modules/FindOpenMP.cmake | 86 +- Modules/FindOpenSSL.cmake | 4 +- Modules/FindProtobuf.cmake | 26 +- Modules/FindPython.cmake | 2 +- Modules/FindPython/Support.cmake | 2 +- Modules/FindPython2.cmake | 2 +- Modules/FindPython3.cmake | 2 +- Modules/FindPythonInterp.cmake | 2 +- Modules/FindPythonLibs.cmake | 7 +- Modules/FindSquish.cmake | 4 + Modules/FindTCL.cmake | 5 +- Modules/FindXCTest.cmake | 2 + Modules/FindwxWidgets.cmake | 2 +- Modules/FortranCInterface/CMakeLists.txt | 4 + Modules/FortranCInterface/Detect.cmake | 6 +- Modules/GoogleTest.cmake | 345 ++- Modules/GoogleTestAddTests.cmake | 176 +- .../Internal/ApplePlatformSelection.cmake.in | 2 + .../CMakeASM-ATTLinkerInformation.cmake | 10 + .../Internal/CMakeASMLinkerInformation.cmake | 35 + .../CMakeASM_MARMASMLinkerInformation.cmake | 10 + .../CMakeASM_MASMLinkerInformation.cmake | 10 + .../CMakeASM_NASMLinkerInformation.cmake | 10 + .../Internal/CMakeCLinkerInformation.cmake | 39 + .../Internal/CMakeCUDALinkerInformation.cmake | 39 + .../Internal/CMakeCXXLinkerInformation.cmake | 39 + .../CMakeCommonLinkerInformation.cmake | 18 + .../CMakeFortranLinkerInformation.cmake | 39 + .../Internal/CMakeHIPLinkerInformation.cmake | 39 + .../Internal/CMakeOBJCLinkerInformation.cmake | 39 + .../CMakeOBJCXXLinkerInformation.cmake | 39 + .../CMakeSwiftLinkerInformation.cmake | 39 + Modules/Internal/CPack/CPackDeb.cmake | 8 +- Modules/Internal/CPack/CPackRPM.cmake | 45 +- Modules/Internal/CPack/ISScript.template.in | 2 +- Modules/Internal/CheckFlagCommonConfig.cmake | 3 +- Modules/Internal/CheckSourceCompiles.cmake | 9 + Modules/Internal/CheckSourceRuns.cmake | 8 + Modules/Linker/AIX-ASM.cmake | 6 + Modules/Linker/AIX-C.cmake | 6 + Modules/Linker/AIX-CXX.cmake | 6 + Modules/Linker/AIX-Fortran.cmake | 6 + Modules/Linker/AIX.cmake | 9 + Modules/Linker/AppleClang-ASM.cmake | 3 + Modules/Linker/AppleClang-C.cmake | 3 + Modules/Linker/AppleClang-CXX.cmake | 3 + Modules/Linker/AppleClang-OBJC.cmake | 3 + Modules/Linker/AppleClang-OBJCXX.cmake | 3 + Modules/Linker/AppleClang.cmake | 10 + Modules/Linker/GNU-ASM.cmake | 6 + Modules/Linker/GNU-C.cmake | 6 + Modules/Linker/GNU-CUDA.cmake | 6 + Modules/Linker/GNU-CXX.cmake | 6 + Modules/Linker/GNU-Fortran.cmake | 6 + Modules/Linker/GNU-HIP.cmake | 6 + Modules/Linker/GNU.cmake | 74 + Modules/Linker/GNUgold-ASM.cmake | 6 + Modules/Linker/GNUgold-C.cmake | 6 + Modules/Linker/GNUgold-CUDA.cmake | 6 + Modules/Linker/GNUgold-CXX.cmake | 6 + Modules/Linker/GNUgold-Fortran.cmake | 6 + Modules/Linker/GNUgold-HIP.cmake | 6 + Modules/Linker/GNUgold.cmake | 18 + Modules/Linker/LLD-ASM.cmake | 6 + Modules/Linker/LLD-C.cmake | 6 + Modules/Linker/LLD-CUDA.cmake | 6 + Modules/Linker/LLD-CXX.cmake | 6 + Modules/Linker/LLD-Fortran.cmake | 6 + Modules/Linker/LLD-HIP.cmake | 6 + Modules/Linker/LLD-OBJC.cmake | 6 + Modules/Linker/LLD-OBJCXX.cmake | 6 + Modules/Linker/LLD.cmake | 10 + Modules/Linker/MOLD-ASM.cmake | 6 + Modules/Linker/MOLD-C.cmake | 6 + Modules/Linker/MOLD-CUDA.cmake | 6 + Modules/Linker/MOLD-CXX.cmake | 6 + Modules/Linker/MOLD-Fortran.cmake | 6 + Modules/Linker/MOLD-HIP.cmake | 6 + Modules/Linker/MOLD-OBJC.cmake | 6 + Modules/Linker/MOLD-OBJCXX.cmake | 6 + Modules/Linker/Solaris-ASM.cmake | 6 + Modules/Linker/Solaris-C.cmake | 6 + Modules/Linker/Solaris-CXX.cmake | 6 + Modules/Linker/Solaris-Fortran.cmake | 6 + Modules/Linker/Solaris.cmake | 9 + Modules/MacOSXFrameworkInfo.plist.in | 2 + Modules/Platform/AIX-GNU.cmake | 9 +- Modules/Platform/AIX-LLVMFlang-Fortran.cmake | 2 + Modules/Platform/AIX-XL.cmake | 9 +- Modules/Platform/AIX.cmake | 1 + Modules/Platform/Android-Determine.cmake | 2 +- Modules/Platform/Apple-Clang.cmake | 4 +- Modules/Platform/CYGWIN-GNU.cmake | 27 - Modules/Platform/Darwin.cmake | 5 - Modules/Platform/FreeBSD.cmake | 27 - Modules/Platform/Linker/AIX-AIX-ASM.cmake | 6 + Modules/Platform/Linker/AIX-AIX-C.cmake | 6 + Modules/Platform/Linker/AIX-AIX-CXX.cmake | 6 + Modules/Platform/Linker/AIX-AIX-Fortran.cmake | 6 + Modules/Platform/Linker/AIX-AIX.cmake | 10 + Modules/Platform/Linker/AIX-ASM.cmake | 5 + Modules/Platform/Linker/AIX-C.cmake | 5 + Modules/Platform/Linker/AIX-CXX.cmake | 5 + Modules/Platform/Linker/AIX-Fortran.cmake | 5 + Modules/Platform/Linker/Apple-ASM.cmake | 5 + .../Linker/Apple-AppleClang-ASM.cmake | 6 + .../Platform/Linker/Apple-AppleClang-C.cmake | 6 + .../Linker/Apple-AppleClang-CUDA.cmake | 6 + .../Linker/Apple-AppleClang-CXX.cmake | 6 + .../Linker/Apple-AppleClang-Fortran.cmake | 6 + .../Linker/Apple-AppleClang-OBJC.cmake | 6 + .../Linker/Apple-AppleClang-OBJCXX.cmake | 6 + .../Platform/Linker/Apple-AppleClang.cmake | 16 + Modules/Platform/Linker/Apple-C.cmake | 5 + Modules/Platform/Linker/Apple-CUDA.cmake | 5 + Modules/Platform/Linker/Apple-CXX.cmake | 5 + Modules/Platform/Linker/Apple-Fortran.cmake | 5 + Modules/Platform/Linker/Apple-LLD-C.cmake | 4 + Modules/Platform/Linker/Apple-LLD-CXX.cmake | 4 + Modules/Platform/Linker/Apple-LLD-OBJC.cmake | 4 + .../Platform/Linker/Apple-LLD-OBJCXX.cmake | 4 + Modules/Platform/Linker/Apple-MOLD-C.cmake | 4 + Modules/Platform/Linker/Apple-MOLD-CXX.cmake | 4 + Modules/Platform/Linker/Apple-MOLD-OBJC.cmake | 4 + .../Platform/Linker/Apple-MOLD-OBJCXX.cmake | 4 + Modules/Platform/Linker/Apple-OBJC.cmake | 5 + Modules/Platform/Linker/Apple-OBJCXX.cmake | 5 + .../Linker/BSD-Linker-Initialize.cmake | 22 + Modules/Platform/Linker/CYGWIN-ASM.cmake | 5 + Modules/Platform/Linker/CYGWIN-C.cmake | 5 + Modules/Platform/Linker/CYGWIN-CXX.cmake | 5 + Modules/Platform/Linker/CYGWIN-Fortran.cmake | 5 + Modules/Platform/Linker/CYGWIN-GNU-ASM.cmake | 6 + Modules/Platform/Linker/CYGWIN-GNU-C.cmake | 6 + Modules/Platform/Linker/CYGWIN-GNU-CXX.cmake | 6 + .../Platform/Linker/CYGWIN-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/CYGWIN-GNU.cmake | 14 + Modules/Platform/Linker/CYGWIN-LLD-ASM.cmake | 6 + Modules/Platform/Linker/CYGWIN-LLD-C.cmake | 6 + Modules/Platform/Linker/CYGWIN-LLD-CXX.cmake | 6 + .../Platform/Linker/CYGWIN-LLD-Fortran.cmake | 6 + Modules/Platform/Linker/CYGWIN-LLD.cmake | 16 + Modules/Platform/Linker/DragonFly-ASM.cmake | 4 + Modules/Platform/Linker/DragonFly-C.cmake | 4 + Modules/Platform/Linker/DragonFly-CXX.cmake | 4 + .../Platform/Linker/DragonFly-Fortran.cmake | 4 + .../Platform/Linker/DragonFly-GNU-ASM.cmake | 4 + Modules/Platform/Linker/DragonFly-GNU-C.cmake | 4 + .../Platform/Linker/DragonFly-GNU-CXX.cmake | 4 + .../Linker/DragonFly-GNU-Fortran.cmake | 4 + .../Platform/Linker/DragonFly-LLD-ASM.cmake | 4 + Modules/Platform/Linker/DragonFly-LLD-C.cmake | 4 + .../Platform/Linker/DragonFly-LLD-CXX.cmake | 4 + .../Linker/DragonFly-LLD-Fortran.cmake | 4 + Modules/Platform/Linker/FreeBSD-ASM.cmake | 15 + Modules/Platform/Linker/FreeBSD-C.cmake | 15 + Modules/Platform/Linker/FreeBSD-CXX.cmake | 15 + Modules/Platform/Linker/FreeBSD-Fortran.cmake | 15 + Modules/Platform/Linker/FreeBSD-GNU-ASM.cmake | 6 + Modules/Platform/Linker/FreeBSD-GNU-C.cmake | 6 + Modules/Platform/Linker/FreeBSD-GNU-CXX.cmake | 6 + .../Platform/Linker/FreeBSD-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/FreeBSD-GNU.cmake | 14 + Modules/Platform/Linker/FreeBSD-LLD-ASM.cmake | 6 + Modules/Platform/Linker/FreeBSD-LLD-C.cmake | 6 + Modules/Platform/Linker/FreeBSD-LLD-CXX.cmake | 6 + .../Platform/Linker/FreeBSD-LLD-Fortran.cmake | 6 + Modules/Platform/Linker/FreeBSD-LLD.cmake | 16 + Modules/Platform/Linker/GNU-ASM.cmake | 5 + Modules/Platform/Linker/GNU-C.cmake | 5 + Modules/Platform/Linker/GNU-CXX.cmake | 5 + Modules/Platform/Linker/GNU-Fortran.cmake | 5 + Modules/Platform/Linker/GNU-GNU-ASM.cmake | 6 + Modules/Platform/Linker/GNU-GNU-C.cmake | 6 + Modules/Platform/Linker/GNU-GNU-CXX.cmake | 6 + Modules/Platform/Linker/GNU-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/GNU-GNU.cmake | 14 + Modules/Platform/Linker/GNU.cmake | 51 + Modules/Platform/Linker/Linux-ASM.cmake | 5 + Modules/Platform/Linker/Linux-C.cmake | 5 + Modules/Platform/Linker/Linux-CUDA.cmake | 5 + Modules/Platform/Linker/Linux-CXX.cmake | 5 + Modules/Platform/Linker/Linux-Fortran.cmake | 5 + Modules/Platform/Linker/Linux-GNU-ASM.cmake | 6 + Modules/Platform/Linker/Linux-GNU-C.cmake | 6 + Modules/Platform/Linker/Linux-GNU-CUDA.cmake | 6 + Modules/Platform/Linker/Linux-GNU-CXX.cmake | 6 + .../Platform/Linker/Linux-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/Linux-GNU-HIP.cmake | 6 + Modules/Platform/Linker/Linux-GNU.cmake | 14 + .../Platform/Linker/Linux-GNUgold-ASM.cmake | 4 + Modules/Platform/Linker/Linux-GNUgold-C.cmake | 4 + .../Platform/Linker/Linux-GNUgold-CUDA.cmake | 4 + .../Platform/Linker/Linux-GNUgold-CXX.cmake | 4 + .../Linker/Linux-GNUgold-Fortran.cmake | 4 + .../Platform/Linker/Linux-GNUgold-HIP.cmake | 4 + Modules/Platform/Linker/Linux-HIP.cmake | 5 + Modules/Platform/Linker/Linux-LLD-ASM.cmake | 6 + Modules/Platform/Linker/Linux-LLD-C.cmake | 6 + Modules/Platform/Linker/Linux-LLD-CUDA.cmake | 6 + Modules/Platform/Linker/Linux-LLD-CXX.cmake | 6 + .../Platform/Linker/Linux-LLD-Fortran.cmake | 6 + Modules/Platform/Linker/Linux-LLD-HIP.cmake | 6 + Modules/Platform/Linker/Linux-LLD.cmake | 16 + Modules/Platform/Linker/Linux-MOLD-ASM.cmake | 6 + Modules/Platform/Linker/Linux-MOLD-C.cmake | 6 + Modules/Platform/Linker/Linux-MOLD-CUDA.cmake | 6 + Modules/Platform/Linker/Linux-MOLD-CXX.cmake | 6 + .../Platform/Linker/Linux-MOLD-Fortran.cmake | 6 + Modules/Platform/Linker/Linux-MOLD-HIP.cmake | 6 + Modules/Platform/Linker/Linux-MOLD.cmake | 16 + Modules/Platform/Linker/MSYS-ASM.cmake | 4 + Modules/Platform/Linker/MSYS-C.cmake | 4 + Modules/Platform/Linker/MSYS-CXX.cmake | 4 + Modules/Platform/Linker/MSYS-Fortran.cmake | 4 + Modules/Platform/Linker/MSYS-GNU-ASM.cmake | 4 + Modules/Platform/Linker/MSYS-GNU-C.cmake | 4 + Modules/Platform/Linker/MSYS-GNU-CXX.cmake | 4 + .../Platform/Linker/MSYS-GNU-Fortran.cmake | 4 + Modules/Platform/Linker/MSYS-LLD-ASM.cmake | 4 + Modules/Platform/Linker/MSYS-LLD-C.cmake | 4 + Modules/Platform/Linker/MSYS-LLD-CXX.cmake | 4 + .../Platform/Linker/MSYS-LLD-Fortran.cmake | 4 + Modules/Platform/Linker/MirBSD-ASM.cmake | 4 + Modules/Platform/Linker/MirBSD-C.cmake | 4 + Modules/Platform/Linker/MirBSD-CXX.cmake | 4 + Modules/Platform/Linker/MirBSD-Fortran.cmake | 4 + Modules/Platform/Linker/MirBSD-GNU-ASM.cmake | 4 + Modules/Platform/Linker/MirBSD-GNU-C.cmake | 4 + Modules/Platform/Linker/MirBSD-GNU-CXX.cmake | 4 + .../Platform/Linker/MirBSD-GNU-Fortran.cmake | 4 + Modules/Platform/Linker/NetBSD-ASM.cmake | 5 + Modules/Platform/Linker/NetBSD-C.cmake | 5 + Modules/Platform/Linker/NetBSD-CXX.cmake | 5 + Modules/Platform/Linker/NetBSD-Fortran.cmake | 5 + Modules/Platform/Linker/NetBSD-GNU-ASM.cmake | 6 + Modules/Platform/Linker/NetBSD-GNU-C.cmake | 6 + Modules/Platform/Linker/NetBSD-GNU-CXX.cmake | 6 + .../Platform/Linker/NetBSD-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/NetBSD-GNU.cmake | 14 + Modules/Platform/Linker/OpenBSD-ASM.cmake | 15 + Modules/Platform/Linker/OpenBSD-C.cmake | 15 + Modules/Platform/Linker/OpenBSD-CXX.cmake | 15 + Modules/Platform/Linker/OpenBSD-Fortran.cmake | 15 + Modules/Platform/Linker/OpenBSD-GNU-ASM.cmake | 6 + Modules/Platform/Linker/OpenBSD-GNU-C.cmake | 6 + Modules/Platform/Linker/OpenBSD-GNU-CXX.cmake | 6 + .../Platform/Linker/OpenBSD-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/OpenBSD-GNU.cmake | 14 + Modules/Platform/Linker/OpenBSD-LLD-ASM.cmake | 6 + Modules/Platform/Linker/OpenBSD-LLD-C.cmake | 6 + Modules/Platform/Linker/OpenBSD-LLD-CXX.cmake | 6 + .../Platform/Linker/OpenBSD-LLD-Fortran.cmake | 6 + Modules/Platform/Linker/OpenBSD-LLD.cmake | 16 + Modules/Platform/Linker/SerenityOS-ASM.cmake | 5 + Modules/Platform/Linker/SerenityOS-C.cmake | 5 + Modules/Platform/Linker/SerenityOS-CXX.cmake | 5 + .../Platform/Linker/SerenityOS-GNU-ASM.cmake | 6 + .../Platform/Linker/SerenityOS-GNU-C.cmake | 6 + .../Platform/Linker/SerenityOS-GNU-CXX.cmake | 6 + Modules/Platform/Linker/SerenityOS-GNU.cmake | 14 + .../Platform/Linker/SerenityOS-LLD-ASM.cmake | 6 + .../Platform/Linker/SerenityOS-LLD-C.cmake | 6 + .../Platform/Linker/SerenityOS-LLD-CXX.cmake | 6 + Modules/Platform/Linker/SerenityOS-LLD.cmake | 16 + Modules/Platform/Linker/SunOS-ASM.cmake | 5 + Modules/Platform/Linker/SunOS-C.cmake | 5 + Modules/Platform/Linker/SunOS-CXX.cmake | 5 + Modules/Platform/Linker/SunOS-Fortran.cmake | 5 + Modules/Platform/Linker/SunOS-GNU-ASM.cmake | 6 + Modules/Platform/Linker/SunOS-GNU-C.cmake | 6 + Modules/Platform/Linker/SunOS-GNU-CXX.cmake | 6 + .../Platform/Linker/SunOS-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/SunOS-GNU.cmake | 14 + .../Platform/Linker/SunOS-Solaris-ASM.cmake | 6 + Modules/Platform/Linker/SunOS-Solaris-C.cmake | 6 + .../Platform/Linker/SunOS-Solaris-CXX.cmake | 6 + .../Linker/SunOS-Solaris-Fortran.cmake | 6 + Modules/Platform/Linker/SunOS-Solaris.cmake | 27 + Modules/Platform/Linker/Windows-ASM.cmake | 15 + Modules/Platform/Linker/Windows-C.cmake | 15 + Modules/Platform/Linker/Windows-CUDA.cmake | 5 + Modules/Platform/Linker/Windows-CXX.cmake | 15 + Modules/Platform/Linker/Windows-Fortran.cmake | 15 + Modules/Platform/Linker/Windows-GNU-ASM.cmake | 6 + Modules/Platform/Linker/Windows-GNU-C.cmake | 6 + Modules/Platform/Linker/Windows-GNU-CXX.cmake | 6 + .../Platform/Linker/Windows-GNU-Fortran.cmake | 6 + Modules/Platform/Linker/Windows-GNU-HIP.cmake | 6 + Modules/Platform/Linker/Windows-GNU.cmake | 14 + Modules/Platform/Linker/Windows-HIP.cmake | 15 + Modules/Platform/Linker/Windows-LLD-ASM.cmake | 6 + Modules/Platform/Linker/Windows-LLD-C.cmake | 6 + Modules/Platform/Linker/Windows-LLD-CXX.cmake | 6 + Modules/Platform/Linker/Windows-LLD-HIP.cmake | 6 + Modules/Platform/Linker/Windows-LLD.cmake | 24 + .../Platform/Linker/Windows-MSVC-ASM.cmake | 6 + Modules/Platform/Linker/Windows-MSVC-C.cmake | 6 + .../Platform/Linker/Windows-MSVC-CUDA.cmake | 6 + .../Platform/Linker/Windows-MSVC-CXX.cmake | 6 + .../Linker/Windows-MSVC-Fortran.cmake | 6 + .../Platform/Linker/Windows-MSVC-HIP.cmake | 6 + Modules/Platform/Linker/Windows-MSVC.cmake | 35 + Modules/Platform/Linux-Intel.cmake | 5 + Modules/Platform/Linux.cmake | 28 +- Modules/Platform/NetBSD.cmake | 27 - Modules/Platform/SunOS.cmake | 15 - Modules/Platform/WASI-Initialize.cmake | 1 + Modules/Platform/WASI.cmake | 1 + Modules/Platform/Windows-Clang.cmake | 10 - Modules/Platform/Windows-GNU.cmake | 26 - Modules/Platform/Windows-IntelLLVM.cmake | 8 - Modules/Platform/Windows-MSVC.cmake | 16 +- .../WindowsKernelModeDriver-MSVC-C.cmake | 1 + .../WindowsKernelModeDriver-MSVC-CXX.cmake | 1 + .../Platform/WindowsKernelModeDriver.cmake | 64 + Modules/Platform/iOS-Initialize.cmake | 2 +- Modules/SystemInformation.in | 1 + Modules/UseJava.cmake | 2 + Modules/UseJava/ClearClassFiles.cmake | 2 +- Modules/UseJava/javaTargets.cmake.in | 2 +- Modules/UseSWIG.cmake | 14 +- README.rst | 11 +- Source/CMakeLists.txt | 30 + Source/CMakeVersion.cmake | 6 +- Source/CPack/IFW/cmCPackIFWInstaller.cxx | 43 +- Source/CPack/IFW/cmCPackIFWInstaller.h | 4 + Source/CPack/WiX/cmCPackWIXGenerator.cxx | 2 +- Source/CPack/cmCPackArchiveGenerator.cxx | 6 +- Source/CPack/cmCPackDebGenerator.cxx | 19 +- Source/CPack/cmCPackGenerator.cxx | 8 +- Source/CPack/cmCPackInnoSetupGenerator.cxx | 8 +- Source/CPack/cmCPackNSISGenerator.cxx | 2 +- Source/CPack/cmCPackNuGetGenerator.cxx | 2 +- Source/CPack/cmCPackPKGGenerator.cxx | 26 +- Source/CPack/cmCPackRPMGenerator.cxx | 8 +- Source/CPack/cpack.cxx | 2 +- Source/CTest/cmCTestBuildHandler.cxx | 5 +- Source/CTest/cmCTestCoverageHandler.cxx | 2 +- Source/CTest/cmCTestCurl.cxx | 15 +- Source/CTest/cmCTestMemCheckHandler.cxx | 6 +- Source/CTest/cmCTestScriptHandler.cxx | 21 +- Source/CTest/cmCTestScriptHandler.h | 2 +- Source/CTest/cmCTestSubmitHandler.cxx | 17 +- Source/CTest/cmCTestTestHandler.cxx | 16 +- Source/CTest/cmCTestTestHandler.h | 2 +- Source/Checks/Curses/CMakeLists.txt | 2 +- Source/Checks/cm_cxx_features.cmake | 2 + Source/Checks/cm_cxx_filesystem.cxx | 2 +- Source/CursesDialog/ccmake.cxx | 2 +- Source/CursesDialog/cmCursesForm.cxx | 2 +- Source/CursesDialog/cmCursesMainForm.cxx | 4 +- Source/Modules/CMakeBuildUtilities.cmake | 10 +- Source/cmAddCustomCommandCommand.cxx | 233 +- Source/cmAddDependenciesCommand.cxx | 1 + Source/cmAddTestCommand.cxx | 48 +- Source/cmArgumentParser.cxx | 4 +- Source/cmArgumentParser.h | 8 +- Source/cmBinUtilsLinuxELFLinker.cxx | 80 +- Source/cmBinUtilsLinuxELFLinker.h | 3 +- Source/cmBinUtilsMacOSMachOLinker.cxx | 4 +- Source/cmBuildDatabase.cxx | 581 +++++ Source/cmBuildDatabase.h | 61 + Source/cmCMakePath.cxx | 12 +- Source/cmCMakePath.h | 3 +- Source/cmCMakePkgConfigCommand.cxx | 722 ++++++ Source/cmCMakePkgConfigCommand.h | 13 + Source/cmCMakePresetsErrors.cxx | 18 +- Source/cmCMakePresetsErrors.h | 4 + Source/cmCMakePresetsGraph.cxx | 13 +- Source/cmCMakePresetsGraph.h | 1 + Source/cmCMakePresetsGraphReadJSON.cxx | 23 +- ...kePresetsGraphReadJSONConfigurePresets.cxx | 2 + Source/cmCTest.cxx | 122 +- Source/cmCTest.h | 23 - Source/cmCommands.cxx | 2 + Source/cmComputeLinkDepends.cxx | 305 ++- Source/cmComputeLinkDepends.h | 47 +- Source/cmComputeLinkInformation.cxx | 37 +- Source/cmComputeLinkInformation.h | 2 +- Source/cmCryptoHash.cxx | 2 +- Source/cmCurl.cxx | 9 + Source/cmCustomCommand.h | 5 + Source/cmDebuggerVariables.cxx | 4 +- Source/cmDebuggerVariables.h | 2 +- Source/cmDebuggerVariablesHelper.cxx | 15 +- Source/cmDepends.cxx | 4 +- Source/cmDependsC.cxx | 4 +- Source/cmDependsCompiler.cxx | 2 +- Source/cmDependsJavaParserHelper.cxx | 2 +- Source/cmDyndepCollation.cxx | 227 +- Source/cmELF.cxx | 10 +- Source/cmELF.h | 3 + Source/cmExecProgramCommand.cxx | 4 +- Source/cmExecuteProcessCommand.cxx | 24 +- Source/cmExperimental.cxx | 21 +- Source/cmExperimental.h | 2 + Source/cmExportAndroidMKGenerator.cxx | 165 ++ Source/cmExportAndroidMKGenerator.h | 73 + Source/cmExportBuildAndroidMKGenerator.cxx | 195 +- Source/cmExportBuildAndroidMKGenerator.h | 41 +- Source/cmExportBuildCMakeConfigGenerator.cxx | 343 +++ Source/cmExportBuildCMakeConfigGenerator.h | 52 + Source/cmExportBuildFileGenerator.cxx | 462 +--- Source/cmExportBuildFileGenerator.h | 63 +- Source/cmExportCMakeConfigGenerator.cxx | 686 ++++++ Source/cmExportCMakeConfigGenerator.h | 109 + Source/cmExportCommand.cxx | 20 +- Source/cmExportFileGenerator.cxx | 1107 +-------- Source/cmExportFileGenerator.h | 217 +- Source/cmExportInstallAndroidMKGenerator.cxx | 116 +- Source/cmExportInstallAndroidMKGenerator.h | 64 +- .../cmExportInstallCMakeConfigGenerator.cxx | 477 ++++ Source/cmExportInstallCMakeConfigGenerator.h | 75 + Source/cmExportInstallFileGenerator.cxx | 907 +++---- Source/cmExportInstallFileGenerator.h | 122 +- .../cmExportInstallPackageInfoGenerator.cxx | 197 ++ Source/cmExportInstallPackageInfoGenerator.h | 66 + Source/cmExportPackageInfoGenerator.cxx | 452 ++++ Source/cmExportPackageInfoGenerator.h | 114 + Source/cmExportTryCompileFileGenerator.cxx | 21 +- Source/cmExportTryCompileFileGenerator.h | 31 +- Source/cmExtraCodeBlocksGenerator.cxx | 2 +- Source/cmExtraSublimeTextGenerator.cxx | 2 +- Source/cmFileAPI.cxx | 23 +- Source/cmFileAPI.h | 3 + Source/cmFileCommand.cxx | 206 +- Source/cmFindBase.cxx | 3 +- Source/cmFindCommon.cxx | 31 +- Source/cmFindCommon.h | 6 +- Source/cmFindPackageCommand.cxx | 4 +- Source/cmFindProgramCommand.cxx | 2 +- Source/cmGeneratorExpression.cxx | 25 +- Source/cmGeneratorExpression.h | 8 +- Source/cmGeneratorExpressionDAGChecker.cxx | 22 +- Source/cmGeneratorExpressionDAGChecker.h | 12 + Source/cmGeneratorExpressionNode.cxx | 19 +- Source/cmGeneratorTarget.cxx | 632 ++++- Source/cmGeneratorTarget.h | 90 +- .../cmGeneratorTarget_CompatibleInterface.cxx | 6 +- .../cmGeneratorTarget_IncludeDirectories.cxx | 2 +- Source/cmGeneratorTarget_Options.cxx | 118 +- .../cmGeneratorTarget_TransitiveProperty.cxx | 14 +- Source/cmGlobalGenerator.cxx | 340 ++- Source/cmGlobalGenerator.h | 48 + Source/cmGlobalNinjaGenerator.cxx | 101 +- Source/cmGlobalNinjaGenerator.h | 3 + Source/cmGlobalUnixMakefileGenerator3.cxx | 39 + Source/cmGlobalUnixMakefileGenerator3.h | 3 + Source/cmGlobalVisualStudio10Generator.cxx | 9 - Source/cmGlobalVisualStudio12Generator.cxx | 114 - Source/cmGlobalVisualStudio12Generator.h | 10 - Source/cmGlobalVisualStudio7Generator.cxx | 20 - Source/cmGlobalVisualStudioGenerator.cxx | 10 - Source/cmGlobalVisualStudioGenerator.h | 5 - ...cmGlobalVisualStudioVersionedGenerator.cxx | 13 +- Source/cmGlobalXCodeGenerator.cxx | 11 +- Source/cmGraphVizWriter.cxx | 4 +- Source/cmHexFileConverter.cxx | 12 +- Source/cmIncludeCommand.cxx | 1 + Source/cmInstallAndroidMKExportGenerator.cxx | 30 + Source/cmInstallAndroidMKExportGenerator.h | 36 + .../cmInstallCMakeConfigExportGenerator.cxx | 43 + Source/cmInstallCMakeConfigExportGenerator.h | 42 + Source/cmInstallCommand.cxx | 276 ++- Source/cmInstallCommandArguments.cxx | 79 +- Source/cmInstallCommandArguments.h | 8 +- Source/cmInstallExportGenerator.cxx | 151 +- Source/cmInstallExportGenerator.h | 23 +- .../cmInstallPackageInfoExportGenerator.cxx | 36 + Source/cmInstallPackageInfoExportGenerator.h | 36 + Source/cmInstallScriptHandler.cxx | 167 ++ Source/cmInstallScriptHandler.h | 43 + Source/cmInstallTargetGenerator.cxx | 6 +- Source/cmJSONHelpers.h | 181 +- Source/cmJSONState.h | 1 + Source/cmLinkItemGraphVisitor.cxx | 6 +- Source/cmLinkLineComputer.cxx | 15 +- Source/cmLinkLineComputer.h | 4 +- Source/cmLinkLineDeviceComputer.cxx | 2 +- Source/cmList.cxx | 2 +- Source/cmListFileCache.cxx | 247 +- Source/cmLoadCommandCommand.cxx | 4 +- Source/cmLocalGenerator.cxx | 112 +- Source/cmLocalUnixMakefileGenerator3.cxx | 15 + Source/cmLocalVisualStudio7Generator.cxx | 36 +- Source/cmLocalVisualStudio7Generator.h | 1 + Source/cmMakefile.cxx | 716 +++--- Source/cmMakefile.h | 31 +- Source/cmMakefileLibraryTargetGenerator.cxx | 8 +- Source/cmMakefileTargetGenerator.cxx | 44 +- Source/cmMessageCommand.cxx | 2 +- Source/cmMessenger.cxx | 133 +- Source/cmNinjaNormalTargetGenerator.cxx | 17 +- Source/cmNinjaTargetGenerator.cxx | 91 +- Source/cmNinjaTargetGenerator.h | 3 + Source/cmNinjaUtilityTargetGenerator.cxx | 4 - Source/cmOutputRequiredFilesCommand.cxx | 2 +- Source/cmParseArgumentsCommand.cxx | 79 +- Source/cmPkgConfigParser.cxx | 151 ++ Source/cmPkgConfigParser.h | 93 + Source/cmPkgConfigResolver.cxx | 870 +++++++ Source/cmPkgConfigResolver.h | 172 ++ Source/cmPolicies.cxx | 4 +- Source/cmPolicies.h | 32 +- Source/cmProcessOutput.cxx | 6 +- Source/cmProcessOutput.h | 4 +- Source/cmProjectCommand.cxx | 8 +- Source/cmQtAutoGenGlobalInitializer.cxx | 6 +- Source/cmQtAutoGenInitializer.cxx | 72 +- Source/cmQtAutoGenerator.cxx | 12 +- Source/cmQtAutoMocUic.cxx | 86 +- Source/cmSearchPath.cxx | 10 +- Source/cmSourceFile.cxx | 2 +- Source/cmSourceFile.h | 3 - Source/cmStateDirectory.h | 4 - Source/cmStateSnapshot.cxx | 2 + Source/cmString.cxx | 2 +- Source/cmString.hxx | 2 +- Source/cmStringAlgorithms.cxx | 42 +- Source/cmStringAlgorithms.h | 36 +- Source/cmStringReplaceHelper.cxx | 2 +- Source/cmSystemTools.cxx | 112 +- Source/cmSystemTools.h | 12 +- Source/cmTarget.cxx | 40 +- Source/cmTarget.h | 12 +- Source/cmTargetTraceDependencies.cxx | 11 + Source/cmTest.cxx | 1 + Source/cmTest.h | 13 +- Source/cmTestGenerator.cxx | 27 +- Source/cmTimestamp.cxx | 2 +- Source/cmVSSetupHelper.cxx | 19 +- Source/cmVSSetupHelper.h | 10 +- Source/cmValue.cxx | 10 +- Source/cmValue.h | 29 +- Source/cmVisualStudio10TargetGenerator.cxx | 321 ++- Source/cmVisualStudio10TargetGenerator.h | 9 +- Source/cmWorkerPool.cxx | 15 +- Source/cmXCOFF.cxx | 192 +- Source/cmXcFramework.cxx | 3 + Source/cmake.cxx | 21 +- Source/cmake.h | 9 + Source/cmakemain.cxx | 150 +- Source/cmcmd.cxx | 270 ++- Source/kwsys/Directory.cxx | 8 +- Source/kwsys/ProcessUNIX.c | 2 +- Source/kwsys/RegularExpression.cxx | 56 +- Source/kwsys/RegularExpression.hxx.in | 6 +- Source/kwsys/SystemInformation.cxx | 96 +- Source/kwsys/SystemTools.cxx | 14 +- Templates/CTestScript.cmake.in | 33 - Templates/MSBuild/FlagTables/v142_CL.json | 2 +- Templates/MSBuild/FlagTables/v143_CL.json | 2 +- Tests/AliasTarget/CMakeLists.txt | 3 +- Tests/Architecture/CMakeLists.txt | 2 +- Tests/ArgumentExpansion/CMakeLists.txt | 2 +- Tests/Assembler/CMakeLists.txt | 2 +- Tests/BuildDepends/CMakeLists.txt | 2 +- Tests/BuildDepends/Project/CMakeLists.txt | 2 +- .../Project/External/CMakeLists.txt | 2 +- Tests/BundleGeneratorTest/CMakeLists.txt | 3 +- Tests/BundleTest/CMakeLists.txt | 2 +- Tests/BundleUtilities/CMakeLists.txt | 2 +- Tests/CFBundleTest/CMakeLists.txt | 7 +- .../add_compile_definitions/CMakeLists.txt | 2 +- .../add_compile_options/CMakeLists.txt | 2 +- .../target_compile_definitions/CMakeLists.txt | 3 +- .../target_compile_features/CMakeLists.txt | 2 +- .../target_compile_options/CMakeLists.txt | 3 +- .../target_include_directories/CMakeLists.txt | 2 +- .../target_link_directories/CMakeLists.txt | 1 - .../target_link_libraries/CMakeLists.txt | 4 +- Tests/CMakeLib/CMakeLists.txt | 6 +- Tests/CMakeLib/testArgumentParser.cxx | 102 +- Tests/CMakeLib/testAssert.cxx | 56 + Tests/CMakeLib/testCMFilesystemPath.cxx | 42 +- Tests/CMakeLib/testCMakePath.cxx | 21 +- Tests/CMakeLib/testCommon.h | 43 +- Tests/CMakeLib/testConfig.h.in | 7 + Tests/CMakeLib/testDebuggerAdapter.cxx | 8 +- Tests/CMakeLib/testDebuggerAdapterPipe.cxx | 7 +- .../testDebuggerBreakpointManager.cxx | 9 +- Tests/CMakeLib/testDebuggerThread.cxx | 5 +- Tests/CMakeLib/testDebuggerVariables.cxx | 11 +- .../CMakeLib/testDebuggerVariablesHelper.cxx | 21 +- .../CMakeLib/testDebuggerVariablesManager.cxx | 5 +- Tests/CMakeLib/testJSONHelpers.cxx | 120 +- Tests/CMakeLib/testList.cxx | 48 +- Tests/CMakeLib/testOptional.cxx | 11 +- Tests/CMakeLib/testRange.cxx | 9 +- Tests/CMakeLib/testString.cxx | 254 +- Tests/CMakeLib/testVisualStudioSlnParser.cxx | 6 +- Tests/CMakeLib/testVisualStudioSlnParser.h.in | 7 - Tests/CMakeLib/testXMLParser.cxx | 4 +- Tests/CMakeLib/testXMLParser.h.in | 6 - Tests/CMakeLists.txt | 155 +- Tests/CMakeOnly/AllFindModules/CMakeLists.txt | 2 +- Tests/CMakeOnly/CMakeLists.txt | 18 - .../CheckCXXCompilerFlag/CMakeLists.txt | 2 +- .../CheckCXXSymbolExists/CMakeLists.txt | 3 +- Tests/CMakeOnly/CheckLanguage/CMakeLists.txt | 2 +- .../CheckStructHasMember/CMakeLists.txt | 2 +- .../CheckSymbolExists/CMakeLists.txt | 3 +- Tests/CMakeOnly/CompilerIdC/CMakeLists.txt | 21 - .../CMakeOnly/CompilerIdCSharp/CMakeLists.txt | 21 - Tests/CMakeOnly/CompilerIdCUDA/CMakeLists.txt | 14 - Tests/CMakeOnly/CompilerIdCXX/CMakeLists.txt | 21 - .../CompilerIdFortran/CMakeLists.txt | 21 - Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt | 14 - .../CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt | 14 - .../LinkInterfaceLoop/CMakeLists.txt | 2 +- .../MajorVersionSelection/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/CMakeOnly/TargetScope/CMakeLists.txt | 2 +- Tests/CMakeOnly/find_library/CMakeLists.txt | 2 +- Tests/CMakeOnly/find_path/CMakeLists.txt | 2 +- Tests/CMakeTests/CMakeLists.txt | 1 - Tests/CMakeTests/EndStuffTestScript.cmake | 2 +- Tests/CMakeTests/PushCheckStateTest.cmake.in | 9 +- Tests/CMakeTests/VersionTest.cmake.in | 2 +- Tests/COnly/CMakeLists.txt | 2 +- Tests/CPackComponents/CMakeLists.txt | 2 +- Tests/CPackComponentsPrefix/CMakeLists.txt | 2 +- Tests/CPackIFWGenerator/ApplicationIcon.png | Bin 0 -> 2335 bytes Tests/CPackIFWGenerator/BundleIcon.icns | Bin 0 -> 33452 bytes Tests/CPackIFWGenerator/CMakeLists.txt | 54 + .../RunCPackVerifyResult.cmake | 93 + Tests/CPackIFWGenerator/SplashScreen.png | Bin 0 -> 909 bytes Tests/CPackIFWGenerator/install.ico | Bin 0 -> 838 bytes Tests/CPackIFWGenerator/main.cpp | 4 + Tests/CPackTestAllGenerators/CMakeLists.txt | 2 +- Tests/CPackUseDefaultVersion/CMakeLists.txt | 2 +- Tests/CPackUseProjectVersion/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/CSharpLinkToCxx/CMakeLists.txt | 2 +- Tests/CSharpOnly/CMakeLists.txt | 2 +- Tests/CTestConfig/CMakeLists.txt | 2 +- .../TestProject/CMakeLists.txt | 2 +- Tests/CTestCoverageCollectGCOV/test.cmake.in | 2 +- Tests/CTestLimitDashJ/CMakeLists.txt | 2 +- Tests/CTestTest/SmallAndFast/CMakeLists.txt | 2 +- Tests/CTestTest2/test.cmake.in | 2 +- Tests/CTestTestBadExe/CMakeLists.txt | 2 +- Tests/CTestTestBadExe/test.cmake.in | 2 +- Tests/CTestTestBadGenerator/CMakeLists.txt | 2 +- Tests/CTestTestBadGenerator/test.cmake.in | 2 +- Tests/CTestTestChecksum/test.cmake.in | 2 +- Tests/CTestTestCostSerial/CMakeLists.txt | 2 +- Tests/CTestTestCostSerial/test.cmake.in | 2 +- Tests/CTestTestCrash/CMakeLists.txt | 2 +- Tests/CTestTestCrash/test.cmake.in | 2 +- Tests/CTestTestCycle/CMakeLists.txt | 2 +- Tests/CTestTestCycle/test.cmake.in | 2 +- Tests/CTestTestDepends/CMakeLists.txt | 2 +- Tests/CTestTestDepends/test.cmake.in | 2 +- .../test.cmake.in | 2 +- Tests/CTestTestFdSetSize/CMakeLists.txt | 2 +- Tests/CTestTestFdSetSize/test.cmake.in | 2 +- .../CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/CTestTestLaunchers/test.cmake.in | 2 +- .../CTestTestMissingDependsExe/CMakeLists.txt | 2 +- Tests/CTestTestParallel/CMakeLists.txt | 2 +- Tests/CTestTestParallel/test.cmake.in | 2 +- Tests/CTestTestScheduler/CMakeLists.txt | 2 +- Tests/CTestTestScheduler/test.cmake.in | 2 +- Tests/CTestTestSerialInDepends/CMakeLists.txt | 2 +- Tests/CTestTestStopTime/CMakeLists.txt | 2 +- Tests/CTestTestStopTime/GetDate.cmake | 2 +- Tests/CTestTestStopTime/test.cmake.in | 2 +- Tests/CTestTestSubdir/CMakeLists.txt | 2 +- Tests/CTestTestSubdir/test.cmake.in | 2 +- Tests/CTestTestTimeout/CMakeLists.txt | 2 +- Tests/CTestTestTimeout/test.cmake.in | 2 +- Tests/CTestTestUpload/CMakeLists.txt | 2 +- Tests/CTestTestUpload/test.cmake.in | 2 +- Tests/CTestTestVerboseOutput/CMakeLists.txt | 2 +- Tests/CTestTestVerboseOutput/test.cmake.in | 2 +- .../CMakeLists.txt | 2 +- Tests/CheckFortran.cmake | 2 +- Tests/CommandLineTest/CMakeLists.txt | 2 +- Tests/CompatibleInterface/CMakeLists.txt | 2 +- Tests/CompileCommandOutput/CMakeLists.txt | 2 +- Tests/CompileDefinitions/CMakeLists.txt | 2 +- Tests/CompileFeatures/CMakeLists.txt | 2 +- Tests/CompileOptions/CMakeLists.txt | 2 +- Tests/Complex/CMakeLists.txt | 3 +- Tests/Complex/Executable/complex.cxx | 4 +- Tests/Complex/Library/CMakeLists.txt | 2 +- Tests/Complex/Library/test_preprocess.cmake | 2 +- Tests/Complex/VarTests.cmake | 3 + Tests/ComplexOneConfig/CMakeLists.txt | 3 +- Tests/ComplexOneConfig/Executable/complex.cxx | 4 +- Tests/ComplexOneConfig/Library/CMakeLists.txt | 2 +- .../Library/test_preprocess.cmake | 2 +- Tests/ComplexOneConfig/VarTests.cmake | 3 + Tests/ConfigSources/CMakeLists.txt | 2 +- Tests/Contracts/PLplot/CMakeLists.txt | 2 +- Tests/Contracts/Trilinos/CMakeLists.txt | 2 +- Tests/Contracts/VTK/CMakeLists.txt | 2 +- Tests/CrossCompile/CMakeLists.txt | 2 +- .../Cuda/IncludePathNoToolkit/CMakeLists.txt | 2 +- Tests/Cuda/NotEnabled/CMakeLists.txt | 2 +- Tests/Cuda/ProperLinkFlags/CMakeLists.txt | 4 +- Tests/CudaOnly/CMakeLists.txt | 1 + Tests/CudaOnly/EnableStandard/CMakeLists.txt | 2 +- Tests/CudaOnly/OptixIR/main.cu | 1 + .../SeparateCompilationPTX/kernels.cu | 7 - .../SharedRuntimeViaCUDAFlags/CMakeLists.txt | 8 +- Tests/CudaOnly/Unity/CMakeLists.txt | 14 + Tests/CudaOnly/Unity/a.cu | 12 + Tests/CudaOnly/Unity/b.cu | 5 + Tests/CudaOnly/Unity/main.cu | 8 + Tests/CudaOnly/utils/bin2c_wrapper.cmake | 2 +- Tests/CustComDepend/CMakeLists.txt | 2 +- Tests/CustomCommand/CMakeLists.txt | 6 +- Tests/CustomCommandByproducts/CMakeLists.txt | 2 +- .../External/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/CxxDialect/CMakeLists.txt | 2 +- Tests/CxxOnly/CMakeLists.txt | 2 +- Tests/CxxSubdirC/CMakeLists.txt | 2 +- Tests/Dependency/CMakeLists.txt | 2 +- Tests/EmptyDepends/CMakeLists.txt | 2 +- Tests/EmptyLibrary/CMakeLists.txt | 2 +- Tests/EnforceConfig.cmake.in | 1 + Tests/Environment/CMakeLists.txt | 2 +- Tests/ExportImport/CMakeLists.txt | 2 +- Tests/ExportImport/Export/CMakeLists.txt | 7 +- .../Export/testSharedLibArchiveAIX.c | 10 + Tests/ExportImport/Import/A/CMakeLists.txt | 2 + Tests/ExportImport/Import/A/imp_testExe1.c | 5 +- Tests/ExportImport/Import/CMakeLists.txt | 5 +- .../Import/try_compile/CMakeLists.txt | 2 +- Tests/ExternalOBJ/CMakeLists.txt | 2 +- Tests/ExternalOBJ/Object/CMakeLists.txt | 2 +- Tests/ExternalProject/CMakeLists.txt | 2 +- Tests/ExternalProject/Example/CMakeLists.txt | 2 +- Tests/ExternalProjectLocal/CMakeLists.txt | 2 +- .../ExternalProjectLocal/Step5/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../Example/subdir/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/ExternalProjectSubdir/CMakeLists.txt | 2 +- .../Subdir1/CMakeLists.txt | 2 +- Tests/ExternalProjectUpdate/CMakeLists.txt | 2 +- Tests/FindBLAS/Test/CMakeLists.txt | 2 +- Tests/FindBZip2/Test/CMakeLists.txt | 2 +- Tests/FindBoost/Test/CMakeLists.txt | 2 +- Tests/FindBoost/TestFail/CMakeLists.txt | 2 +- Tests/FindBoost/TestHeaders/CMakeLists.txt | 2 +- Tests/FindBoost/TestPython/CMakeLists.txt | 4 +- Tests/FindDevIL/Test/CMakeLists.txt | 2 +- .../DotComponentTestTest/CMakeLists.txt | 2 +- Tests/FindDoxygen/SimpleTest/CMakeLists.txt | 2 +- Tests/FindDoxygen/StampFile/CMakeLists.txt | 2 +- Tests/FindEXPAT/Test/CMakeLists.txt | 2 +- Tests/FindFreetype/Test/CMakeLists.txt | 2 +- Tests/FindGIF/Test/CMakeLists.txt | 2 +- Tests/FindGLEW/Test/CMakeLists.txt | 2 +- Tests/FindGSL/rng/CMakeLists.txt | 2 +- Tests/FindGTK2/atk/CMakeLists.txt | 2 +- Tests/FindGTK2/atkmm/CMakeLists.txt | 2 +- Tests/FindGTK2/cairo/CMakeLists.txt | 2 +- Tests/FindGTK2/cairomm/CMakeLists.txt | 2 +- Tests/FindGTK2/gdk/CMakeLists.txt | 2 +- Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt | 2 +- Tests/FindGTK2/gdkmm/CMakeLists.txt | 2 +- Tests/FindGTK2/gio/CMakeLists.txt | 2 +- Tests/FindGTK2/giomm/CMakeLists.txt | 2 +- Tests/FindGTK2/glib/CMakeLists.txt | 2 +- Tests/FindGTK2/glibmm/CMakeLists.txt | 2 +- Tests/FindGTK2/gmodule/CMakeLists.txt | 2 +- Tests/FindGTK2/gobject/CMakeLists.txt | 2 +- Tests/FindGTK2/gthread/CMakeLists.txt | 2 +- Tests/FindGTK2/gtk/CMakeLists.txt | 2 +- Tests/FindGTK2/gtkmm/CMakeLists.txt | 2 +- Tests/FindGTK2/pango/CMakeLists.txt | 2 +- Tests/FindGTK2/pangocairo/CMakeLists.txt | 2 +- Tests/FindGTK2/pangoft2/CMakeLists.txt | 2 +- Tests/FindGTK2/pangomm/CMakeLists.txt | 2 +- Tests/FindGTK2/pangoxft/CMakeLists.txt | 2 +- Tests/FindGTK2/sigc++/CMakeLists.txt | 2 +- Tests/FindGTest/Test/CMakeLists.txt | 2 +- Tests/FindGnuTLS/Test/CMakeLists.txt | 2 +- Tests/FindICU/Test/CMakeLists.txt | 2 +- Tests/FindImageMagick/Test/CMakeLists.txt | 2 +- Tests/FindJPEG/Test/CMakeLists.txt | 2 +- Tests/FindJsonCpp/Test/CMakeLists.txt | 2 +- Tests/FindLAPACK/Test/CMakeLists.txt | 2 +- Tests/FindLTTngUST/Test/CMakeLists.txt | 2 +- Tests/FindLibLZMA/Test/CMakeLists.txt | 2 +- Tests/FindLibRHash/Test/CMakeLists.txt | 2 +- Tests/FindLibUV/Test/CMakeLists.txt | 2 +- Tests/FindLibXml2/Test/CMakeLists.txt | 2 +- Tests/FindLibXslt/Test/CMakeLists.txt | 2 +- Tests/FindMPI/Test/CMakeLists.txt | 2 +- Tests/FindMatlab/basic_checks/CMakeLists.txt | 2 +- .../components_checks/CMakeLists.txt | 2 +- .../FindMatlab/failure_reports/CMakeLists.txt | 2 +- .../no_implicit_link_checks/CMakeLists.txt | 2 +- Tests/FindMatlab/r2018a_check/CMakeLists.txt | 2 +- .../FindMatlab/targets_checks/CMakeLists.txt | 2 +- .../FindMatlab/versions_checks/CMakeLists.txt | 2 +- Tests/FindODBC/Test/CMakeLists.txt | 2 +- Tests/FindOpenCL/Test/CMakeLists.txt | 2 +- Tests/FindOpenMP/CMakeLists.txt | 9 +- Tests/FindOpenMP/Test/CMakeLists.txt | 15 +- Tests/FindOpenMP/Test/main.c | 7 +- Tests/FindOpenSP/Test/CMakeLists.txt | 2 +- Tests/FindOpenSSL/rand/CMakeLists.txt | 2 +- Tests/FindPNG/Test/CMakeLists.txt | 2 +- Tests/FindPackageTest/CMakeLists.txt | 10 +- Tests/FindPackageTest/Exporter/CMakeLists.txt | 2 +- .../lib/SortLib-4.0.0/SortLibConfig.cmake | 2 + .../SortLib-4.0.0/SortLibConfigVersion.cmake | 9 + Tests/FindPatch/Test/CMakeLists.txt | 2 +- Tests/FindProtobuf/Test/CMakeLists.txt | 27 +- .../FindProtobuf/Test/main-generate-grpc.cxx | 2 +- .../msgs/{ => grpc}/example_service.proto | 0 Tests/FindSDL/Test/CMakeLists.txt | 2 +- Tests/FindSQLite3/Test/CMakeLists.txt | 2 +- Tests/FindTIFF/Test/CMakeLists.txt | 2 +- Tests/FindThreads/C-only/CMakeLists.txt | 2 +- Tests/FindThreads/CXX-only/CMakeLists.txt | 2 +- Tests/FindVulkan/Test/CMakeLists.txt | 2 +- Tests/FindXalanC/Test/CMakeLists.txt | 2 +- Tests/FindXercesC/Test/CMakeLists.txt | 2 +- Tests/ForceInclude/CMakeLists.txt | 2 +- Tests/Fortran/CMakeLists.txt | 10 +- Tests/FortranC/CMakeLists.txt | 4 +- Tests/FortranModules/CMakeLists.txt | 2 +- .../Submodules/obfuscated_parent.f90 | 4 +- Tests/FortranOnly/CMakeLists.txt | 6 +- Tests/Framework/CMakeLists.txt | 2 +- Tests/FunctionTest/CMakeLists.txt | 2 +- Tests/GeneratorExpression/CMakeLists.txt | 15 +- Tests/GeneratorExpression/check-part2.cmake | 2 + .../CMakeLists.txt | 2 +- .../GhsMultiIntegrityDDInt/CMakeLists.txt | 2 +- Tests/GoogleTest/Test/CMakeLists.txt | 2 +- Tests/IncludeDirectories/CMakeLists.txt | 2 +- .../SystemIncludeDirectories/CMakeLists.txt | 2 +- .../TargetIncludeDirectories/CMakeLists.txt | 2 +- Tests/InterfaceLibrary/CMakeLists.txt | 2 +- Tests/InterfaceLinkLibraries/CMakeLists.txt | 2 +- Tests/JCTest/CMakeLists.txt | 2 +- Tests/Java/CMakeLists.txt | 2 +- .../BuildExport/CMakeLists.txt | 2 +- Tests/JavaExportImport/CMakeLists.txt | 2 +- Tests/JavaExportImport/Import/CMakeLists.txt | 2 +- .../InstallExport/CMakeLists.txt | 2 +- Tests/JavaJavah/CMakeLists.txt | 2 +- Tests/JavaNativeHeaders/CMakeLists.txt | 2 +- Tests/Jump/CMakeLists.txt | 2 +- Tests/LinkDirectory/CMakeLists.txt | 2 +- Tests/LinkDirectory/External/CMakeLists.txt | 2 +- Tests/LinkFlags/CMakeLists.txt | 2 +- Tests/LinkLanguage/CMakeLists.txt | 2 +- Tests/LinkStatic/CMakeLists.txt | 2 +- .../LoadCommand/CMakeCommands/CMakeLists.txt | 2 +- Tests/LoadCommand/CMakeLists.txt | 2 +- .../CMakeCommands/CMakeLists.txt | 2 +- Tests/LoadCommandOneConfig/CMakeLists.txt | 2 +- Tests/MFC/CMakeLists.txt | 2 +- Tests/MFC/CMakeLists.txt.in | 2 +- Tests/MFC/try_compile/CMakeLists.txt | 2 +- Tests/MSManifest/CMakeLists.txt | 2 +- Tests/MSManifest/Subdir2/check.cmake | 2 +- Tests/MacRuntimePath/A/CMakeLists.txt | 2 +- Tests/MacRuntimePath/B/CMakeLists.txt | 2 +- Tests/MacRuntimePath/CMakeLists.txt | 2 +- Tests/MacroTest/CMakeLists.txt | 2 +- Tests/MakeClean/CMakeLists.txt | 2 +- Tests/MathTest/CMakeLists.txt | 2 +- Tests/MissingInstall/CMakeLists.txt | 2 +- Tests/MissingSourceFile/CMakeLists.txt | 2 +- .../Module/CheckIPOSupported-C/CMakeLists.txt | 2 +- .../CheckIPOSupported-CUDA/CMakeLists.txt | 2 +- .../CheckIPOSupported-CXX/CMakeLists.txt | 2 +- .../CheckIPOSupported-Fortran/CMakeLists.txt | 6 +- Tests/Module/CheckTypeSize/CMakeLists.txt | 2 +- Tests/Module/ExternalData/CMakeLists.txt | 2 +- Tests/Module/FindDependency/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- Tests/ModuleDefinition/CMakeLists.txt | 2 +- Tests/NewlineArgs/CMakeLists.txt | 2 +- Tests/ObjectLibrary/CMakeLists.txt | 2 +- .../ExportLanguages/CMakeLists.txt | 2 +- .../ExportLanguagesTest/CMakeLists.txt | 2 +- Tests/OutDir/CMakeLists.txt | 2 +- Tests/OutOfSource/CMakeLists.txt | 2 +- Tests/PDBDirectoryAndName/CMakeLists.txt | 2 +- Tests/Plugin/CMakeLists.txt | 2 +- Tests/Plugin/PluginTest/CMakeLists.txt | 2 +- Tests/Policy0002/CMakeLists.txt | 2 +- Tests/PolicyScope/Bar.cmake | 2 +- Tests/PolicyScope/FindFoo.cmake | 2 +- .../PositionIndependentTargets/CMakeLists.txt | 2 +- Tests/PrecompiledHeader/CMakeLists.txt | 2 +- Tests/Preprocess/CMakeLists.txt | 2 +- Tests/Properties/CMakeLists.txt | 2 +- Tests/Qt4And5Automoc/CMakeLists.txt | 2 +- Tests/Qt4Deploy/CMakeLists.txt | 2 +- Tests/Qt4Targets/CMakeLists.txt | 2 +- .../Qt4Targets/IncrementalMoc/CMakeLists.txt | 2 +- .../LowMinimumVersion/CMakeLists.txt | 2 +- Tests/QtAutogen/Tests.cmake | 2 - Tests/QtAutomocNoQt/CMakeLists.txt | 2 +- Tests/README.rst | 3 +- Tests/ReturnTest/CMakeLists.txt | 2 +- Tests/RunCMake/Android/CMakeLists.txt | 2 +- Tests/RunCMake/Android/RunCMakeTest.cmake | 2 +- Tests/RunCMake/AndroidMK/CMakeLists.txt | 2 +- .../AndroidTestUtilities/CMakeLists.txt | 2 +- Tests/RunCMake/AppleTextStubs/CMakeLists.txt | 2 +- Tests/RunCMake/AutoExportDll/CMakeLists.txt | 2 +- Tests/RunCMake/Autogen/RunCMakeTest.cmake | 487 ---- .../AutogenSkipLinting-build-stderr.txt | 0 .../AutogenSkipLinting.cmake | 0 .../AutogenUseSystemIncludeCommon.cmake | 2 - .../AutogenUseSystemIncludeOff.cmake | 0 .../AutogenUseSystemIncludeOn.cmake | 0 .../CMP0111-imported-target-full.cmake | 0 .../CMP0111-imported-target-implib-only.cmake | 0 .../CMP0111-imported-target-libname.cmake | 0 .../CMP0111-imported-target-prelude.cmake | 0 .../CMP0151-common.cmake | 0 .../{Autogen => Autogen_1}/CMP0151-new.cmake | 0 .../{Autogen => Autogen_1}/CMP0151-old.cmake | 0 .../{Autogen => Autogen_1}/CMakeLists.txt | 0 .../{Autogen => Autogen_1}/Inspect.cmake | 0 .../MocGeneratedFile.cmake | 0 .../MocPredefs-build-stderr.txt | 0 .../MocPredefs-check.cxx | 0 .../MocPredefs-prefix.cmake | 0 .../{Autogen => Autogen_1}/MocPredefs.cmake | 0 .../{Autogen => Autogen_1}/MocPredefs.cxx | 0 .../{Autogen => Autogen_1}/NoQt-stderr.txt | 0 .../{Autogen => Autogen_1}/NoQt.cmake | 0 .../{Autogen => Autogen_1}/QtInFunction.cmake | 0 .../QtInFunctionNested-stderr.txt | 0 .../QtInFunctionNested.cmake | 0 .../QtInFunctionProperty.cmake | 0 Tests/RunCMake/Autogen_1/RunCMakeTest.cmake | 134 ++ .../{Autogen => Autogen_1}/SkipLinting.cxx | 0 .../{Autogen => Autogen_1}/SkipLinting.h | 0 .../RunCMake/{Autogen => Autogen_1}/empty.cpp | 0 .../CMakeLists.txt | 2 +- .../{Autogen => Autogen_2}/MyWindow.cpp | 0 .../{Autogen => Autogen_2}/MyWindow.h | 0 .../{Autogen => Autogen_2}/MyWindow.ui | 0 .../QtAutoMocDeps-stderr.txt | 0 .../QtAutoMocDeps.cmake | 0 .../QtSubDir1/CMakeLists.txt | 0 .../QtSubDir2/CMakeLists.txt | 0 .../QtSubDir3/CMakeLists.txt | 0 Tests/RunCMake/Autogen_2/RunCMakeTest.cmake | 87 + Tests/RunCMake/{Autogen => Autogen_2}/app.cpp | 0 .../{Autogen => Autogen_2}/app_qt.cpp | 0 .../{Autogen => Autogen_2}/simple_lib.cpp | 0 .../AutoMocExecutableConfig.cmake | 6 +- Tests/RunCMake/Autogen_3/CMakeLists.txt | 3 + .../{Autogen => Autogen_3}/MocExample.cmake | 2 +- Tests/RunCMake/Autogen_3/RunCMakeTest.cmake | 11 + .../AutoUicExecutableConfig.cmake | 6 +- Tests/RunCMake/Autogen_4/CMakeLists.txt | 3 + Tests/RunCMake/Autogen_4/RunCMakeTest.cmake | 11 + .../{Autogen => Autogen_4}/UicExample.cmake | 0 .../{Autogen => Autogen_4}/example_ui.cpp | 0 .../{Autogen => Autogen_4}/example_ui.h | 0 Tests/RunCMake/{Autogen => Autogen_4}/uiA.ui | 0 .../AutoRccExecutableConfig.cmake | 6 +- Tests/RunCMake/Autogen_5/CMakeLists.txt | 3 + .../{Autogen => Autogen_5}/RccExample.cmake | 2 +- Tests/RunCMake/Autogen_5/RunCMakeTest.cmake | 31 + .../RunCMake/{Autogen => Autogen_5}/data.qrc | 0 Tests/RunCMake/Autogen_6/CMakeLists.txt | 3 + Tests/RunCMake/Autogen_6/RunCMakeTest.cmake | 70 + .../Autogen_6/multi_ui_files/CMakeLists.txt | 25 + .../Autogen_6/multi_ui_files/src/main.cpp | 11 + .../multi_ui_files/src/mainwindow.cpp | 25 + .../Autogen_6/multi_ui_files/src/mainwindow.h | 22 + .../multi_ui_files/src/mainwindow.ui | 33 + .../Autogen_6/multi_ui_files/src/widget1.cpp | 22 + .../Autogen_6/multi_ui_files/src/widget1.h | 23 + .../Autogen_6/multi_ui_files/src/widget1.ui | 52 + .../Autogen_6/multi_ui_files/src/widget2.cpp | 22 + .../Autogen_6/multi_ui_files/src/widget2.h | 24 + .../Autogen_6/multi_ui_files/src/widget2.ui | 45 + .../{Autogen => Autogen_common}/example.cpp | 0 .../{Autogen => Autogen_common}/example.h | 0 .../{Autogen => Autogen_common}/exe_common.h | 0 .../{Autogen => Autogen_common}/exe_debug.cpp | 0 .../exe_release.cpp | 0 .../exe_relwithdebinfo.cpp | 0 Tests/RunCMake/Autogen_common/utils.cmake | 266 +++ Tests/RunCMake/BuildDepends/CMakeLists.txt | 2 +- Tests/RunCMake/BuildDepends/CompileDepends.c | 5 + .../BuildDepends/CompileDepends.cmake | 78 + .../BuildDepends/CompileDepends.step1.cmake | 3 + .../BuildDepends/CompileDepends.step2.cmake | 3 + .../BuildDepends/CompilerDependencies.cmake | 46 - .../CompilerDependencies.step1.cmake | 9 - .../CompilerDependencies.step2.cmake | 3 - ...> CustomCommandDepends-BadArgs-result.txt} | 0 ...> CustomCommandDepends-BadArgs-stderr.txt} | 2 +- ...ake => CustomCommandDepends-BadArgs.cmake} | 0 ...CommandDepends-compiler-deps-legacy.cmake} | 0 ...ncies.cmake => CustomCommandDepends.cmake} | 40 +- ...cmake => CustomCommandDepends.step1.cmake} | 0 ...cmake => CustomCommandDepends.step2.cmake} | 0 Tests/RunCMake/BuildDepends/LinkDepends.cmake | 24 +- .../BuildDepends/LinkDepends.step1.cmake | 23 - .../BuildDepends/LinkDependsCheck.cmake | 9 +- Tests/RunCMake/BuildDepends/LinkDependsExe.c | 5 + Tests/RunCMake/BuildDepends/LinkDependsLib.c | 5 + .../RunCMake/BuildDepends/RunCMakeTest.cmake | 29 +- .../BuildDepends/With Space/CMakeLists.txt | 1 + Tests/RunCMake/CMP0004/CMakeLists.txt | 2 +- Tests/RunCMake/CMP0068/CMakeLists.txt | 2 +- Tests/RunCMake/CMP0068/RunCMakeTest.cmake | 1 + Tests/RunCMake/CMP0069/RunCMakeTest.cmake | 1 + Tests/RunCMake/CMP0129/C-NEW.cmake | 2 + Tests/RunCMake/CMP0129/C-OLD-stderr.txt | 10 + Tests/RunCMake/CMP0129/C-OLD.cmake | 2 + Tests/RunCMake/CMP0129/C-WARN.cmake | 1 + .../CMP0129/{C.cmake => C-common.cmake} | 4 - Tests/RunCMake/CMP0129/CMakeLists.txt | 2 +- Tests/RunCMake/CMP0129/CXX-NEW.cmake | 2 + Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt | 10 + Tests/RunCMake/CMP0129/CXX-OLD.cmake | 2 + Tests/RunCMake/CMP0129/CXX-WARN.cmake | 1 + .../CMP0129/{CXX.cmake => CXX-common.cmake} | 4 - Tests/RunCMake/CMP0129/Fortran-NEW.cmake | 2 + Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt | 10 + Tests/RunCMake/CMP0129/Fortran-OLD.cmake | 2 + Tests/RunCMake/CMP0129/Fortran-WARN.cmake | 1 + Tests/RunCMake/CMP0129/Fortran-common.cmake | 4 + Tests/RunCMake/CMP0129/Fortran.cmake | 15 - Tests/RunCMake/CMP0129/RunCMakeTest.cmake | 16 +- .../CMP0156/CMP0156-Common-Imported.cmake | 21 + ...6-NEW-AppleClang-Imported-build-result.txt | 1 + ...6-NEW-AppleClang-Imported-build-stdout.txt | 3 + .../CMP0156-NEW-AppleClang-Imported.cmake | 5 + .../CMP0156/CMP0156-NEW-Imported.cmake | 4 + .../CMP0156-OLD-AppleClang-build-result.txt | 2 +- .../CMP0156-OLD-AppleClang-build-stdout.txt | 2 +- Tests/RunCMake/CMP0156/RunCMakeTest.cmake | 15 +- Tests/RunCMake/CMP0156/subdir1/CMakeLists.txt | 5 + Tests/RunCMake/CMP0156/subdir2/CMakeLists.txt | 14 + .../CMP0171-NEW-result.txt} | 0 Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt | 4 + Tests/RunCMake/CMP0171/CMP0171-NEW.cmake | 6 + .../CMP0171-OLD-result.txt} | 0 Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt | 4 + Tests/RunCMake/CMP0171/CMP0171-OLD.cmake | 9 + .../RunCMake/CMP0171/CMP0171-WARN-stderr.txt | 9 + Tests/RunCMake/CMP0171/CMP0171-WARN.cmake | 6 + .../CMP0171/CMP0171-codegen-build-result.txt | 1 + .../CMP0171-codegen.cmake} | 0 Tests/RunCMake/CMP0171/CMakeLists.txt | 6 + Tests/RunCMake/CMP0171/RunCMakeTest.cmake | 18 + Tests/RunCMake/CMP0173/CMP0173-NEW-result.txt | 1 + Tests/RunCMake/CMP0173/CMP0173-NEW-stderr.txt | 9 + Tests/RunCMake/CMP0173/CMP0173-NEW.cmake | 4 + Tests/RunCMake/CMP0173/CMP0173-OLD-stdout.txt | 2 + Tests/RunCMake/CMP0173/CMP0173-OLD.cmake | 4 + .../RunCMake/CMP0173/CMP0173-WARN-stderr.txt | 29 + Tests/RunCMake/CMP0173/CMP0173-WARN.cmake | 2 + Tests/RunCMake/CMP0173/CMP0173-common.cmake | 12 + Tests/RunCMake/CMP0173/CMakeLists.txt | 3 + Tests/RunCMake/CMP0173/RunCMakeTest.cmake | 5 + Tests/RunCMake/CMakeLists.txt | 100 +- .../CMakePresets/CommentValid-result.txt | 1 + .../CommentValid.cmake} | 0 .../CMakePresets/CommentValid.json.in | 14 + .../CMakePresets/CommentValidFull-result.txt | 1 + .../CommentValidFull.cmake} | 0 .../CMakePresets/CommentValidFull.json.in | 179 ++ .../CommentValidOldSchema-result.txt | 1 + .../CommentValidOldSchema-stderr.txt | 2 + .../CommentValidOldSchema.cmake} | 0 .../CommentValidOldSchema.json.in | 14 + .../CMakePresets/GraphvizValid-result.txt | 1 + .../CMakePresets/GraphvizValid-stdout.txt | 2 + .../RunCMake/CMakePresets/GraphvizValid.cmake | 0 .../CMakePresets/GraphvizValid.json.in | 11 + .../GraphvizValidOldSchema-result.txt | 1 + .../GraphvizValidOldSchema-stderr.txt | 2 + .../CMakePresets/GraphvizValidOldSchema.cmake | 0 .../GraphvizValidOldSchema.json.in | 11 + .../CMakePresets/HighVersion-stderr.txt | 2 +- .../CMakePresets/LowVersion-stderr.txt | 2 +- .../CMakePresets/NoCommentValid-result.txt | 1 + .../CMakePresets/NoCommentValid.cmake | 0 .../CMakePresets/NoCommentValid.json.in | 10 + .../CMakePresets/NoGraphvizValid-result.txt | 1 + .../CMakePresets/NoGraphvizValid.cmake | 0 .../CMakePresets/NoGraphvizValid.json.in | 10 + .../RunCMake/CMakePresets/RunCMakeTest.cmake | 11 + .../InvalidOption-stderr.txt | 2 +- .../MultiplePresets.json.in | 41 + .../RepeatedPresetArg.cmake | 3 + .../CMakePresetsWorkflow/RunCMakeTest.cmake | 11 +- .../SinglePresetArg.cmake | 3 + Tests/RunCMake/CPack/CMakeLists.txt | 2 +- Tests/RunCMake/CPack/DEB/Helpers.cmake | 2 +- Tests/RunCMake/CPack/README.txt | 10 +- Tests/RunCMake/CPack/RunCMakeTest.cmake | 3 +- .../CPack/tests/MULTIARCH/DEB-fail-stderr.txt | 1 + .../CPack/tests/MULTIARCH/ExpectedFiles.cmake | 5 + .../CPack/tests/MULTIARCH/VerifyResult.cmake | 4 + .../RunCMake/CPack/tests/MULTIARCH/test.cmake | 7 + .../CPackConfig/CMP0172-NEW-check.cmake | 2 + Tests/RunCMake/CPackConfig/CMP0172-NEW.cmake | 1 + .../CPackConfig/CMP0172-OLD-check.cmake | 2 + Tests/RunCMake/CPackConfig/CMP0172-OLD.cmake | 2 + .../CPackConfig/CMP0172-WARN-check.cmake | 2 + .../CPackConfig/CMP0172-WARN-stderr.txt | 12 + Tests/RunCMake/CPackConfig/CMP0172-WARN.cmake | 1 + Tests/RunCMake/CPackConfig/CMakeLists.txt | 2 +- Tests/RunCMake/CPackConfig/RunCMakeTest.cmake | 3 + .../CPackInstallProperties/CMakeLists.txt | 2 +- .../CSharpCustomCommand/CMakeLists.txt | 2 +- .../CSharpReferenceImport/CMakeLists.txt | 2 +- Tests/RunCMake/CTest/CMakeLists.txt | 2 +- .../RunCMake/CTestCommandLine/CMakeLists.txt | 2 +- .../CTestCommandLine/CMakeLists.txt.in | 2 +- .../CTestCommandLine/FailureLabels-result.txt | 1 + .../CTestCommandLine/FailureLabels-stderr.txt | 1 + .../CTestCommandLine/FailureLabels-stdout.txt | 4 + .../CTestCommandLine/PrintLabels-stdout.txt | 6 + .../CTestCommandLine/RunCMakeTest.cmake | 30 + Tests/RunCMake/CTestCommandLine/test.cmake.in | 2 +- .../CTestTimeoutAfterMatch/CMakeLists.txt.in | 2 +- .../CTestTimeoutAfterMatch/test.cmake.in | 2 +- Tests/RunCMake/CXXModules/Inspect.cmake | 2 + ...NinjaDependInfoCompileDatabase-check.cmake | 39 + .../NinjaDependInfoCompileDatabase-stderr.txt | 4 + .../NinjaDependInfoCompileDatabase.cmake | 54 + Tests/RunCMake/CXXModules/RunCMakeTest.cmake | 114 +- Tests/RunCMake/CXXModules/check-json.cmake | 66 +- .../examples/build-database-check.cmake | 81 + .../export-build-database-all-multi.json | 300 +++ .../expect/export-build-database-all.json | 153 ++ .../expect/export-build-database-config.json | 153 ++ .../export-build-database-cxx-config.json | 153 ++ .../export-build-database-cxx-multi.json | 300 +++ .../expect/export-build-database-cxx.json | 153 ++ ...ort-build-database-imported-all-multi.json | 278 +++ .../export-build-database-imported-all.json | 142 ++ ...export-build-database-imported-config.json | 142 ++ ...rt-build-database-imported-cxx-config.json | 142 ++ ...ort-build-database-imported-cxx-multi.json | 278 +++ .../export-build-database-imported-cxx.json | 142 ++ ...export-build-database-imported-target.json | 59 + .../expect/export-build-database-target.json | 153 ++ .../export-build-database-build-check.cmake | 19 + .../export-build-database-check.cmake | 19 + .../export-build-database-setup.cmake | 39 + .../examples/export-build-database-stderr.txt | 4 + ...se-target-cmake_build_database-check.cmake | 21 + .../CXX-check.cmake | 21 + .../CXX/Debug-check.cmake | 14 + .../CXX/Release-Release-check.cmake | 14 + .../Debug-check.cmake | 14 + .../Release-Release-check.cmake | 14 + .../export-build-database/CMakeLists.txt | 85 + .../dep_interface_include/anchor | 0 .../export-build-database/importable.cxx | 6 + .../examples/export-build-database/lib.cxx | 6 + .../target_interface_include/anchor | 0 .../target_public_include/anchor | 0 ...es-export-build-database-build-check.cmake | 18 + ...-modules-export-build-database-check.cmake | 18 + ...t-modules-export-build-database-stderr.txt | 4 + ...se-target-cmake_build_database-check.cmake | 22 + .../CXX-check.cmake | 22 + .../CXX/Debug-check.cmake | 19 + .../CXX/Release-Release-check.cmake | 19 + .../Debug-check.cmake | 19 + .../Release-check.cmake | 19 + .../examples/import-modules/CMakeLists.txt | 22 + .../NinjaDependInfoBMIInstall-private.json | 4 +- .../NinjaDependInfoBMIInstall-public.json | 4 +- ...injaDependInfoCompileDatabase-private.json | 51 + ...NinjaDependInfoCompileDatabase-public.json | 51 + .../expect/NinjaDependInfoExport-private.json | 4 +- .../expect/NinjaDependInfoExport-public.json | 4 +- ...ependInfoExportFilesystemSafe-private.json | 4 +- ...DependInfoExportFilesystemSafe-public.json | 4 +- .../NinjaDependInfoFileSet-private.json | 21 +- .../expect/NinjaDependInfoFileSet-public.json | 21 +- Tests/RunCMake/CacheNewline/CMakeLists.txt | 2 +- .../RunCMake/CheckIPOSupported/CMakeLists.txt | 2 +- Tests/RunCMake/CheckModules/CMakeLists.txt | 2 +- .../CheckModules/CheckLinkDirectories.cmake | 122 + .../CheckLinkDirectoriesTestLib.cmake | 1 + .../RunCMake/CheckModules/RunCMakeTest.cmake | 28 + .../CheckModules/TestLib/CMakeLists.txt | 3 + Tests/RunCMake/CheckModules/TestLib/TestLib.c | 6 + Tests/RunCMake/ClangTidy/CMakeLists.txt | 2 +- Tests/RunCMake/Codegen/CMakeLists.txt | 7 + Tests/RunCMake/Codegen/RunCMakeTest.cmake | 33 + ...d-custom-command-depends-build-check.cmake | 5 + .../Codegen/add-custom-command-depends.cmake | 16 + .../add-dependencies-build-check.cmake | 5 + Tests/RunCMake/Codegen/add-dependencies.cmake | 18 + .../append-implicit-depends-result.txt | 1 + .../append-implicit-depends-stderr.txt | 2 + .../Codegen/append-implicit-depends.cmake | 19 + .../Codegen/byproducts-build-check.cmake | 5 + Tests/RunCMake/Codegen/byproducts.cmake | 19 + Tests/RunCMake/Codegen/error.c | 1 + Tests/RunCMake/Codegen/exclude-from-all.cmake | 11 + Tests/RunCMake/Codegen/generated.h.in | 1 + ...implicit-depends-append-codegen-result.txt | 1 + ...implicit-depends-append-codegen-stderr.txt | 4 + .../implicit-depends-append-codegen.cmake | 18 + .../Codegen/implicit-depends-result.txt | 1 + .../Codegen/implicit-depends-stderr.txt | 4 + Tests/RunCMake/Codegen/implicit-depends.cmake | 11 + Tests/RunCMake/Codegen/main.c | 4 + .../Codegen/min-graph-1-build-check.cmake | 13 + Tests/RunCMake/Codegen/min-graph-1.cmake | 26 + .../Codegen/min-graph-2-build-check.cmake | 5 + Tests/RunCMake/Codegen/min-graph-2.cmake | 18 + .../Codegen/min-graph-3-build-check.cmake | 5 + Tests/RunCMake/Codegen/min-graph-3.cmake | 12 + Tests/RunCMake/Codegen/no-codegen-check.cmake | 5 + Tests/RunCMake/Codegen/no-codegen.cmake | 6 + Tests/RunCMake/Codegen/no-output-result.txt | 1 + Tests/RunCMake/Codegen/no-output-stderr.txt | 4 + Tests/RunCMake/Codegen/no-output.cmake | 11 + Tests/RunCMake/CommandLine/CMakeLists.txt | 2 +- .../DeprecateVS12-WARN-ON-stderr.txt | 5 - Tests/RunCMake/CommandLine/RunCMakeTest.cmake | 56 +- .../CommandLine/list-cache-LR-stdout.txt | 3 + .../CommandLine/list-cache-LRA-stdout.txt | 4 + .../CommandLine/list-cache-LRAH-stdout.txt | 9 + .../CommandLine/list-cache-LRH-stdout.txt | 6 + Tests/RunCMake/CommandLine/list-cache.cmake | 6 + .../print-config-dir-apple-stdout.txt | 1 + .../print-config-dir-env-stdout.txt | 1 + .../CommandLine/print-config-dir-stdout.txt | 1 + .../print-config-dir-win-stdout.txt | 1 + .../CommandLine/trace-expand-stderr.txt | 2 +- ...trace-expand-warn-uninitialized-stderr.txt | 2 +- Tests/RunCMake/CommandLine/trace-stderr.txt | 2 +- Tests/RunCMake/CommandLineTar/CMakeLists.txt | 2 +- .../CompatibleInterface/CMakeLists.txt | 2 +- .../CompileDefinitions/CMakeLists.txt | 2 +- Tests/RunCMake/CompileFeatures/CMakeLists.txt | 2 +- Tests/RunCMake/CompilerArgs/CMakeLists.txt | 2 +- Tests/RunCMake/CompilerChange/CMakeLists.txt | 2 +- Tests/RunCMake/CompilerId/C-stdout.txt | 3 + Tests/RunCMake/CompilerId/C.cmake | 4 + Tests/RunCMake/CompilerId/CMakeLists.txt | 3 + Tests/RunCMake/CompilerId/CSharp-stdout.txt | 3 + Tests/RunCMake/CompilerId/CSharp.cmake | 4 + Tests/RunCMake/CompilerId/CUDA-stdout.txt | 11 + Tests/RunCMake/CompilerId/CUDA.cmake | 6 + Tests/RunCMake/CompilerId/CXX-stdout.txt | 3 + Tests/RunCMake/CompilerId/CXX.cmake | 4 + Tests/RunCMake/CompilerId/Fortran-stdout.txt | 3 + Tests/RunCMake/CompilerId/Fortran.cmake | 4 + Tests/RunCMake/CompilerId/HIP-stdout.txt | 11 + Tests/RunCMake/CompilerId/HIP.cmake | 6 + Tests/RunCMake/CompilerId/ISPC-stdout.txt | 3 + Tests/RunCMake/CompilerId/ISPC.cmake | 4 + Tests/RunCMake/CompilerId/OBJC-stdout.txt | 3 + Tests/RunCMake/CompilerId/OBJC.cmake | 4 + Tests/RunCMake/CompilerId/OBJCXX-stdout.txt | 3 + Tests/RunCMake/CompilerId/OBJCXX.cmake | 4 + Tests/RunCMake/CompilerId/RunCMakeTest.cmake | 29 + .../RunCMake/CompilerLauncher/CMakeLists.txt | 2 +- .../RunCMake/CompilerNotFound/CMakeLists.txt | 2 +- Tests/RunCMake/ConfigDir/CMakeLists.txt | 3 + Tests/RunCMake/ConfigDir/RunCMakeTest.cmake | 6 + Tests/RunCMake/ConfigDir/check-reply.cmake | 3 + Tests/RunCMake/ConfigDir/config.cmake | 0 .../config/api/v1/query/codemodel-v2 | 0 Tests/RunCMake/Configure/CMakeLists.txt | 2 +- Tests/RunCMake/Cppcheck/CMakeLists.txt | 2 +- Tests/RunCMake/Cpplint/CMakeLists.txt | 2 +- .../CrosscompilingEmulator/CMakeLists.txt | 2 +- .../DependencyGraph/RunCMakeTest.cmake | 2 +- Tests/RunCMake/DependencyGraph/mylib.f90 | 1 + .../DisallowedCommands/CMakeLists.txt | 2 +- Tests/RunCMake/ExcludeFromAll/CMakeLists.txt | 2 +- .../ExportWithoutLanguage/CMakeLists.txt | 2 +- Tests/RunCMake/ExternalData/CMakeLists.txt | 2 +- Tests/RunCMake/ExtraGenerators/CMakeLists.txt | 2 +- Tests/RunCMake/FPHSA/CMakeLists.txt | 2 +- Tests/RunCMake/FeatureSummary/CMakeLists.txt | 2 +- Tests/RunCMake/FetchContent/CMakeLists.txt | 2 +- Tests/RunCMake/File_Archive/CMakeLists.txt | 2 +- .../RunCMake/File_Archive/RunCMakeTest.cmake | 2 + Tests/RunCMake/File_Archive/roundtrip.cmake | 7 +- .../File_Archive/working-directory.cmake | 12 + .../File_Generate/CMP0070-OLD-stderr.txt | 9 +- .../File_Generate/CMP0070-WARN-stderr.txt | 9 +- Tests/RunCMake/File_Generate/CMakeLists.txt | 5 +- .../SourceProperty-CMP0070-OLD-stderr.txt | 9 +- Tests/RunCMake/FindBoost/CMakeLists.txt | 2 +- Tests/RunCMake/FindGTK2/CMakeLists.txt | 2 +- Tests/RunCMake/FindLua/CMakeLists.txt | 2 +- Tests/RunCMake/FindMatlab/CMakeLists.txt | 2 +- Tests/RunCMake/FindPkgConfig/CMakeLists.txt | 2 +- Tests/RunCMake/Framework/CMakeLists.txt | 2 +- .../FrameworkLayout-check-common.cmake | 24 + .../RunCMake/Framework/FrameworkLayout.cmake | 4 + ...MultiConfigPostfix-build-final-check.cmake | 20 +- .../OSXFrameworkLayout-build-check.cmake | 32 +- .../iOSFrameworkLayout-build-check.cmake | 32 +- Tests/RunCMake/GNUInstallDirs/CMakeLists.txt | 2 +- .../GenEx-COMPILE_LANGUAGE/CMakeLists.txt | 2 +- .../RunCMake/GenEx-TARGET_FILE/CMakeLists.txt | 2 +- .../GenEx-TARGET_IMPORT_FILE/CMakeLists.txt | 2 +- .../GenEx-TARGET_PROPERTY/LOCATION-stderr.txt | 2 +- .../GenerateExportHeader/CMakeLists.txt | 2 +- .../nodeprecated/CMakeLists.txt.in | 2 +- .../GeneratorExpression/BadAND-stderr.txt | 12 +- .../GeneratorExpression/BadCONFIG-stderr.txt | 8 +- .../GeneratorExpression/BadIF-stderr.txt | 2 +- .../BadInstallPrefix-stderr.txt | 2 +- .../GeneratorExpression/BadNOT-stderr.txt | 12 +- .../GeneratorExpression/BadOR-stderr.txt | 12 +- .../BadSHELL_PATH-stderr.txt | 6 +- .../BadStrEqual-stderr.txt | 8 +- .../BadTargetName-stderr.txt | 2 +- .../BadTargetTypeObject-stderr.txt | 6 +- .../GeneratorExpression/BadZero-stderr.txt | 4 +- .../CMP0044-WARN-stderr.txt | 2 +- .../GeneratorExpression/CMakeLists.txt | 5 +- .../COMPILE_ONLY-not-compiling-stderr.txt | 2 +- .../FILTER-InvalidOperator-stderr.txt | 2 +- .../LINK_ONLY-not-linking-stderr.txt | 2 +- ...t-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt | 2 +- .../NonValidTarget-CXX_COMPILER_ID-stderr.txt | 2 +- ...alidTarget-CXX_COMPILER_VERSION-stderr.txt | 2 +- ...get-C_COMPILER_FRONTEND_VARIANT-stderr.txt | 2 +- .../NonValidTarget-C_COMPILER_ID-stderr.txt | 2 +- ...nValidTarget-C_COMPILER_VERSION-stderr.txt | 2 +- ...rtran_COMPILER_FRONTEND_VARIANT-stderr.txt | 2 +- ...ValidTarget-Fortran_COMPILER_ID-stderr.txt | 2 +- ...Target-Fortran_COMPILER_VERSION-stderr.txt | 2 +- .../NonValidTarget-TARGET_POLICY-stderr.txt | 2 +- .../NonValidTarget-TARGET_PROPERTY-stderr.txt | 2 +- .../RunCMake/GeneratorInstance/CMakeLists.txt | 2 +- .../RunCMake/GeneratorPlatform/CMakeLists.txt | 2 +- .../RunCMake/GeneratorToolset/CMakeLists.txt | 2 +- .../GeneratorToolset/RunCMakeTest.cmake | 8 +- Tests/RunCMake/GoogleTest/CMakeLists.txt | 2 +- ...discovery-check-test-list-extra-args.cmake | 6 + ...GoogleTestDiscoveryTestListExtraArgs.cmake | 15 + .../GoogleTestLauncher-test-stdout.txt | 17 - .../Launcher-CMP0178-NEW-test-stdout.txt | 38 + .../GoogleTest/Launcher-CMP0178-NEW.cmake | 2 + .../Launcher-CMP0178-OLD-test-stdout.txt | 35 + .../GoogleTest/Launcher-CMP0178-OLD.cmake | 2 + .../Launcher-CMP0178-WARN-stderr.txt | 38 + .../Launcher-CMP0178-WARN-test-stdout.txt | 35 + .../GoogleTest/Launcher-CMP0178-WARN.cmake | 1 + ...oogleTestLauncher.cmake => Launcher.cmake} | 10 +- Tests/RunCMake/GoogleTest/RunCMakeTest.cmake | 47 +- Tests/RunCMake/GoogleTest/launcher_test.c | 2 +- .../GoogleTest/test_list_extra_args.cpp | 22 + Tests/RunCMake/IAR/RunCMakeTest.cmake | 13 +- Tests/RunCMake/IAR/module.asm | 80 +- .../BinInInstallPrefix-CMP0052-OLD-stderr.txt | 2 +- Tests/RunCMake/IfacePaths/CMakeLists.txt | 4 +- .../SrcInInstallPrefix-CMP0052-OLD-stderr.txt | 2 +- .../RunCMake/IncludeWhatYouUse/CMakeLists.txt | 2 +- Tests/RunCMake/IncompatibleQt/CMakeLists.txt | 2 +- .../InstallParallel/RunCMakeTest.cmake | 56 +- .../InstallParallel/check-manifest.cmake | 10 + .../InstallParallel/check-num-installs.cmake | 7 + .../InstallParallel/check-parallel.cmake | 15 - .../InstallParallel/check-single.cmake | 5 - Tests/RunCMake/InstallParallel/install.cmake | 2 +- .../ninja-no-parallel-install-stdout.txt | 12 + .../ninja-parallel-install-stdout.txt | 25 + .../no-parallel-install-stderr.txt | 5 - .../no-parallel-install-stdout.txt | 12 + .../out-of-date-json-install-stdout.txt | 12 + .../parallel-install-stdout.txt | 45 +- .../InstallParallel/read-ninja-install.cmake | 4 - .../InstallParallel/subdir-1/CMakeLists.txt | 2 +- .../subdir-1/subdir-3/CMakeLists.txt | 2 +- .../subdir-1/subdir-4/CMakeLists.txt | 2 +- .../InstallParallel/subdir-2/CMakeLists.txt | 2 +- .../RunCMake/InterfaceLibrary/CMakeLists.txt | 2 +- Tests/RunCMake/Languages/CMakeLists.txt | 2 +- .../CMP0028-OLD-iface-stderr.txt | 2 +- .../LinkItemValidation/CMP0028-OLD-stderr.txt | 2 +- .../LinkItemValidation/CMakeLists.txt | 4 +- ...EORDER_FREELY-run-stdout-dedup-reverse.txt | 1 + .../Basic-REORDER_FREELY-run-stdout-dedup.txt | 1 + .../Basic-REORDER_FREELY-run-stdout.txt | 1 + ...ic-REORDER_FREELY-stderr-dedup-reverse.txt | 9 + .../Basic-REORDER_FREELY-stderr-dedup.txt | 9 + .../Basic-REORDER_FREELY-stderr.txt | 9 + .../Basic-REORDER_FREELY.cmake | 2 + ...DER_MINIMALLY-run-stdout-dedup-reverse.txt | 1 + ...sic-REORDER_MINIMALLY-run-stdout-dedup.txt | 1 + .../Basic-REORDER_MINIMALLY-run-stdout.txt | 1 + ...REORDER_MINIMALLY-stderr-dedup-reverse.txt | 10 + .../Basic-REORDER_MINIMALLY-stderr-dedup.txt | 10 + .../Basic-REORDER_MINIMALLY-stderr.txt | 11 + .../Basic-REORDER_MINIMALLY.cmake | 2 + .../LinkLibrariesStrategy/Basic-common.cmake | 15 + Tests/RunCMake/LinkLibrariesStrategy/Basic.c | 11 + Tests/RunCMake/LinkLibrariesStrategy/BasicA.c | 4 + Tests/RunCMake/LinkLibrariesStrategy/BasicB.c | 5 + Tests/RunCMake/LinkLibrariesStrategy/BasicC.c | 5 + Tests/RunCMake/LinkLibrariesStrategy/BasicX.c | 7 + .../LinkLibrariesStrategy/CMakeLists.txt | 3 + .../Duplicate-REORDER_FREELY-stderr.txt | 9 + .../Duplicate-REORDER_FREELY.cmake | 2 + .../Duplicate-REORDER_MINIMALLY-stderr.txt | 15 + .../Duplicate-REORDER_MINIMALLY.cmake | 2 + .../Duplicate-common.cmake | 12 + .../LinkLibrariesStrategy/Duplicate.c | 4 + .../LinkLibrariesStrategy/Inspect.cmake | 12 + .../LinkLibrariesStrategy/RunCMakeTest.cmake | 51 + .../LinkLibrariesStrategy/Unknown-result.txt | 1 + .../LinkLibrariesStrategy/Unknown-stderr.txt | 4 + .../LinkLibrariesStrategy/Unknown.cmake | 5 + Tests/RunCMake/LinkLibrariesStrategy/main.c | 4 + Tests/RunCMake/LinkStatic/CMakeLists.txt | 2 +- Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt | 2 +- Tests/RunCMake/Make/CMakeLists.txt | 2 +- Tests/RunCMake/MultiLint/CMakeLists copy.txt | 2 +- .../RunCMake/Ninja/CMP0058-OLD-by-stderr.txt | 2 +- .../RunCMake/Ninja/CMP0058-OLD-no-stderr.txt | 2 +- .../RunCMake/Ninja/CMP0058-WARN-by-stderr.txt | 2 +- .../RunCMake/Ninja/CMP0058-WARN-no-stderr.txt | 2 +- Tests/RunCMake/Ninja/CMakeLists.txt | 2 +- Tests/RunCMake/Ninja/RunCMakeTest.cmake | 5 +- .../CustomCommandsAndTargets.cmake | 4 +- .../NinjaMultiConfig/RunCMakeTest.cmake | 2 +- .../RunCMake/ObsoleteQtMacros/CMakeLists.txt | 2 +- .../RunCMake/PackageInfo/Appendix-check.cmake | 16 + Tests/RunCMake/PackageInfo/Appendix.cmake | 9 + Tests/RunCMake/PackageInfo/Assertions.cmake | 34 + .../RunCMake/PackageInfo/BadArgs1-result.txt | 1 + .../RunCMake/PackageInfo/BadArgs1-stderr.txt | 2 + Tests/RunCMake/PackageInfo/BadArgs1.cmake | 3 + .../RunCMake/PackageInfo/BadArgs2-result.txt | 1 + .../RunCMake/PackageInfo/BadArgs2-stderr.txt | 2 + Tests/RunCMake/PackageInfo/BadArgs2.cmake | 3 + .../RunCMake/PackageInfo/BadArgs3-result.txt | 1 + .../RunCMake/PackageInfo/BadArgs3-stderr.txt | 2 + Tests/RunCMake/PackageInfo/BadArgs3.cmake | 3 + .../RunCMake/PackageInfo/BadArgs4-result.txt | 1 + .../RunCMake/PackageInfo/BadArgs4-stderr.txt | 2 + Tests/RunCMake/PackageInfo/BadArgs4.cmake | 3 + .../RunCMake/PackageInfo/BadArgs5-result.txt | 1 + .../RunCMake/PackageInfo/BadArgs5-stderr.txt | 2 + Tests/RunCMake/PackageInfo/BadArgs5.cmake | 3 + .../PackageInfo/BadDefaultTarget-result.txt | 1 + .../PackageInfo/BadDefaultTarget-stderr.txt | 2 + .../PackageInfo/BadDefaultTarget.cmake | 5 + Tests/RunCMake/PackageInfo/CMakeLists.txt | 3 + .../PackageInfo/ExperimentalGate-result.txt | 1 + .../PackageInfo/ExperimentalGate-stderr.txt | 2 + .../PackageInfo/ExperimentalGate.cmake | 5 + .../ExperimentalWarning-stderr.txt | 7 + .../PackageInfo/ExperimentalWarning.cmake | 8 + .../InterfaceProperties-check.cmake | 24 + .../PackageInfo/InterfaceProperties.cmake | 15 + .../PackageInfo/LowerCaseFile-check.cmake | 9 + .../RunCMake/PackageInfo/LowerCaseFile.cmake | 4 + .../RunCMake/PackageInfo/Metadata-check.cmake | 16 + Tests/RunCMake/PackageInfo/Metadata.cmake | 12 + .../RunCMake/PackageInfo/Minimal-check.cmake | 18 + Tests/RunCMake/PackageInfo/Minimal.cmake | 3 + .../PackageInfo/MinimalVersion-check.cmake | 21 + .../RunCMake/PackageInfo/MinimalVersion.cmake | 19 + .../ReferencesNonExportedTarget-result.txt | 1 + .../ReferencesNonExportedTarget-stderr.txt | 1 + .../ReferencesNonExportedTarget.cmake | 6 + ...ReferencesWronglyExportedTarget-result.txt | 1 + ...ReferencesWronglyExportedTarget-stderr.txt | 7 + .../ReferencesWronglyExportedTarget.cmake | 14 + ...ReferencesWronglyImportedTarget-result.txt | 1 + ...ReferencesWronglyImportedTarget-stderr.txt | 3 + .../ReferencesWronglyImportedTarget.cmake | 7 + ...ferencesWronglyNamespacedTarget-result.txt | 1 + ...ferencesWronglyNamespacedTarget-stderr.txt | 4 + .../ReferencesWronglyNamespacedTarget.cmake | 11 + .../PackageInfo/Requirements-check.cmake | 20 + Tests/RunCMake/PackageInfo/Requirements.cmake | 20 + Tests/RunCMake/PackageInfo/RunCMakeTest.cmake | 33 + .../PackageInfo/TargetTypes-check.cmake | 11 + Tests/RunCMake/PackageInfo/TargetTypes.cmake | 20 + .../RunCMake/PackageInfo/broken-config.cmake | 1 + .../foo.cpp => PackageInfo/foo.cxx} | 0 Tests/RunCMake/PackageInfo/test-config.cmake | 1 + .../{Autogen/exe.cpp => PackageInfo/test.cxx} | 0 .../RunCMake/ParseImplicitData/CMakeLists.txt | 2 +- ...ortran-GNU-13.3.0-static-libquadmath.input | 80 + .../linux-Fortran-LFortran-0.35.0-clang.input | 25 + .../linux-Fortran-LFortran-0.35.0-gcc.input | 25 + .../linux-Fortran-LFortran-0.41.0-clang.input | 38 + .../linux-Fortran-LFortran-0.41.0-gcc.input | 43 + .../ParseImplicitIncludeInfo.cmake | 1 + ...rtran-GNU-13.3.0-static-libquadmath.output | 1 + .../ParseImplicitLinkInfo.cmake | 5 + ...rtran-GNU-13.3.0-static-libquadmath.output | 4 + ...linux-Fortran-LFortran-0.35.0-clang.output | 3 + .../linux-Fortran-LFortran-0.35.0-gcc.output | 3 + ...linux-Fortran-LFortran-0.41.0-clang.output | 4 + .../linux-Fortran-LFortran-0.41.0-gcc.output | 4 + .../PositionIndependentCode/CMakeLists.txt | 2 +- Tests/RunCMake/README.rst | 4 +- Tests/RunCMake/RunCMake.cmake | 2 +- Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt | 2 +- Tests/RunCMake/RuntimePath/CMakeLists.txt | 2 +- .../RunCMake/SourceProperties/CMakeLists.txt | 2 +- .../StandardLinkDirectories/CMakeLists.txt | 3 + .../RunCMakeTest.cmake | 22 + .../StandardLinkDirectories/TestApp.cmake | 1 + .../TestApp/CMakeLists.txt | 2 + .../StandardLinkDirectories/TestApp/TestApp.c | 6 + .../TestAppBad-result.txt | 1 + .../StandardLinkDirectories/TestLib.cmake | 1 + .../TestLib/CMakeLists.txt | 2 + .../StandardLinkDirectories/TestLib/TestLib.c | 4 + Tests/RunCMake/SymlinkTrees/CMakeLists.txt | 2 +- Tests/RunCMake/TargetObjects/CMakeLists.txt | 2 +- Tests/RunCMake/TargetPolicies/CMakeLists.txt | 2 +- .../TargetPolicies/PolicyList-stderr.txt | 1 + .../RunCMake/TargetProperties/CMakeLists.txt | 2 +- Tests/RunCMake/ToolchainFile/CMakeLists.txt | 2 +- Tests/RunCMake/UnityBuild/RunCMakeTest.cmake | 5 + Tests/RunCMake/UnityBuild/f.cu | 5 + .../UnityBuild/unitybuild_cuda-check.cmake | 5 + .../RunCMake/UnityBuild/unitybuild_cuda.cmake | 14 + .../unitybuild_cuda_group-check.cmake | 27 + .../UnityBuild/unitybuild_cuda_group.cmake | 28 + .../unitybuild_cxx_and_cuda-check.cmake | 42 + .../UnityBuild/unitybuild_cxx_and_cuda.cmake | 40 + Tests/RunCMake/VS10Project/CMakeLists.txt | 2 +- Tests/RunCMake/VS10Project/RunCMakeTest.cmake | 2 + .../VsFrameworkReference-check.cmake | 24 + .../VS10Project/VsFrameworkReference.cmake | 8 + .../VS10Project/VsToolOverride-check.cmake | 64 + .../RunCMake/VS10Project/VsToolOverride.cmake | 7 + Tests/RunCMake/VS10Project/bar.txt | 1 + Tests/RunCMake/VS10Project/foo.txt | 1 + .../VS10ProjectWinCE/RunCMakeTest.cmake | 9 - .../VsCEDebuggerDeploy-check.cmake | 118 - .../VS10ProjectWinCE/VsCEDebuggerDeploy.cmake | 14 - .../VsCSharpCFProject-check.cmake | 54 - .../VS10ProjectWinCE/VsCSharpCFProject.cmake | 8 - Tests/RunCMake/VS10ProjectWinCE/foo.cs | 3 - Tests/RunCMake/VSSolution/CMakeLists.txt | 2 +- .../VisibilityPreset/CMP0063-OLD-stderr.txt | 9 +- .../CMP0063-WARN-exe-stderr.txt | 7 + .../CMP0063-WARN-no-stderr.txt | 6 + .../CMP0063-WARN-obj-stderr.txt | 7 + .../CMP0063-WARN-sta-stderr.txt | 7 + .../RunCMake/VisibilityPreset/CMakeLists.txt | 5 +- Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake | 17 +- .../VsDotnetSdkAssemblyName-check.cmake | 22 + .../VsDotnetSdk/VsDotnetSdkAssemblyName.cmake | 9 + .../VsDotnetSdkConfigurations-check.cmake | 20 + .../VsDotnetSdkConfigurations.cmake | 11 + ...etSdkCustomCommandsSource-build-stdout.txt | 1 + ...VsDotnetSdkCustomCommandsSource-stderr.txt | 7 - .../VsDotnetSdkCustomCommandsSource.cmake | 5 +- ...etSdkCustomCommandsTarget-build-stdout.txt | 1 + ...VsDotnetSdkCustomCommandsTarget-stderr.txt | 7 - .../VsDotnetSdkCustomCommandsTarget.cmake | 2 +- .../VsDotnetSdkNugetRestore-build-stdout.txt | 1 + .../VsDotnetSdk/VsDotnetSdkNugetRestore.cmake | 8 + .../CMakeLists.txt | 2 +- Tests/RunCMake/XcFramework/RunCMakeTest.cmake | 43 +- ...te-executable-framework-ios-catalyst.cmake | 2 + ...le-framework-link-phase-ios-catalyst.cmake | 2 + ...eate-executable-library-ios-catalyst.cmake | 2 + ...able-library-link-phase-ios-catalyst.cmake | 2 + ...utable-target-framework-ios-catalyst.cmake | 2 + ...et-framework-link-phase-ios-catalyst.cmake | 2 + ...ecutable-target-library-ios-catalyst.cmake | 2 + ...rget-library-link-phase-ios-catalyst.cmake | 2 + .../create-framework-ios-catalyst.cmake | 2 + .../create-library-ios-catalyst.cmake | 2 + .../RunCMake/XcFramework/export-common.cmake | 1 + .../XcFramework/export-ios-catalyst.cmake | 4 + .../XcodeProject-Device/CMakeLists.txt | 2 +- Tests/RunCMake/XcodeProject/CMakeLists.txt | 2 +- .../add_custom_command/CMP0175-NEW-result.txt | 1 + .../add_custom_command/CMP0175-NEW-stderr.txt | 29 + .../add_custom_command/CMP0175-NEW.cmake | 2 + .../add_custom_command/CMP0175-OLD.cmake | 2 + .../CMP0175-WARN-stderr.txt | 72 + .../add_custom_command/CMP0175-WARN.cmake | 1 + .../RunCMake/add_custom_command/CMP0175.cmake | 65 + .../add_custom_command/CMakeLists.txt | 2 +- .../RemoveEmptyCommands.cmake | 3 + .../add_custom_command/RunCMakeTest.cmake | 3 + .../add_custom_command/TargetImported.cmake | 2 +- .../add_custom_command/TargetNotInDir.cmake | 2 +- .../RunCMake/add_custom_target/CMakeLists.txt | 2 +- .../RunCMake/add_dependencies/CMakeLists.txt | 2 +- Tests/RunCMake/add_executable/CMakeLists.txt | 2 +- Tests/RunCMake/add_library/CMakeLists.txt | 2 +- .../RunCMake/add_subdirectory/CMakeLists.txt | 2 +- .../add_subdirectory/RunCMakeTest.cmake | 2 +- Tests/RunCMake/alias_targets/CMakeLists.txt | 2 +- Tests/RunCMake/build_command/CMakeLists.txt | 2 +- .../WindowsKernelModeDriver-set.cmake | 2 +- ...re3_5-stderr.txt => Before3_10-stderr.txt} | 12 +- .../{Before3_5.cmake => Before3_10.cmake} | 4 +- .../cmake_minimum_required/CMakeLists.txt | 2 +- .../CompatBefore24-stderr.txt | 2 +- .../cmake_minimum_required/RunCMakeTest.cmake | 2 +- .../cmake_parse_arguments/CMakeLists.txt | 2 +- .../CornerCasesArgvN-stderr.txt | 11 + .../CornerCasesArgvN.cmake | 73 + .../cmake_parse_arguments/test_utils.cmake | 4 +- Tests/RunCMake/cmake_path/GET.cmake | 67 + .../RunCMake/cmake_pkg_config/CMakeLists.txt | 5 + .../PackageRoot/VersionPackages/a.pc | 3 + .../PackageRoot/VersionPackages/one.pc | 3 + .../PackageRoot/VersionPackages/onedot.pc | 3 + .../VersionPackages/pseudo-empty.pc | 3 + .../PackageRoot/VersionPackages/tilde.pc | 3 + .../PackageRoot/VersionPackages/zeroone.pc | 3 + .../PackageRoot/all-extract-fields.pc | 15 + .../cmake_pkg_config/PackageRoot/bar.pc | 3 + .../cmake_pkg_config/PackageRoot/baz.pc | 3 + .../PackageRoot/cflags-bothcase-f.pc | 6 + .../PackageRoot/cflags-lowercase-f.pc | 5 + .../PackageRoot/cflags-uppercase-f.pc | 5 + .../cmake_pkg_config/PackageRoot/empty-key.pc | 3 + .../cmake_pkg_config/PackageRoot/foo.pc | 3 + .../cmake_pkg_config/PackageRoot/invalid.pc | 4 + .../PackageRoot/no-description.pc | 2 + .../cmake_pkg_config/PackageRoot/no-name.pc | 2 + .../PackageRoot/no-version.pc | 2 + .../PackageRoot/qux-uninstalled.pc | 5 + .../cmake_pkg_config/PackageRoot/qux.pc | 5 + .../cmake_pkg_config/PackageRoot/relocate.pc | 6 + .../cmake_pkg_config/RunCMakeTest.cmake | 18 + .../cmake_pkg_config/TestEnv-stderr.txt | 15 + Tests/RunCMake/cmake_pkg_config/TestEnv.cmake | 75 + .../cmake_pkg_config/TestExtract-stderr.txt | 21 + .../cmake_pkg_config/TestExtract.cmake | 29 + .../cmake_pkg_config/TestMangle-stderr.txt | 8 + .../cmake_pkg_config/TestMangle.cmake | 22 + .../RunCMake/cmake_pkg_config/TestQuiet.cmake | 29 + .../cmake_pkg_config/TestRequired-result.txt | 1 + .../cmake_pkg_config/TestRequired-stderr.txt | 4 + .../cmake_pkg_config/TestRequired.cmake | 9 + .../cmake_pkg_config/TestReroot-stderr.txt | 4 + .../cmake_pkg_config/TestReroot.cmake | 10 + .../TestStrictness-BEST_EFFORT-stderr.txt | 3 + .../TestStrictness-PERMISSIVE-stderr.txt | 31 + .../TestStrictness-STRICT-stderr.txt | 38 + .../cmake_pkg_config/TestStrictness.cmake | 51 + .../TestUninstalled-stderr.txt | 2 + .../cmake_pkg_config/TestUninstalled.cmake | 10 + .../cmake_pkg_config/TestVersion-stderr.txt | 103 + .../cmake_pkg_config/TestVersion.cmake | 65 + Tests/RunCMake/configure_file/CMakeLists.txt | 2 +- Tests/RunCMake/continue/CMakeLists.txt | 2 +- .../BuildFailure-CMP0061-OLD-stderr.txt | 2 +- Tests/RunCMake/ctest_build/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_build/test.cmake.in | 2 +- .../ctest_cmake_error/CMakeLists.txt.in | 2 +- .../RunCMake/ctest_cmake_error/test.cmake.in | 2 +- .../ctest_configure/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_configure/test.cmake.in | 2 +- .../RunCMake/ctest_coverage/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_coverage/test.cmake.in | 2 +- .../ctest_disabled_test/CMakeLists.txt.in | 2 +- .../ctest_disabled_test/test.cmake.in | 2 +- .../CMakeLists.txt.in | 2 +- .../test.cmake.in | 2 +- .../RunCMake/ctest_environment/test.cmake.in | 2 +- .../RunCMake/ctest_fixtures/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_fixtures/test.cmake.in | 2 +- .../CMakeLists.txt.in | 2 +- .../CTestScriptVariableCommandLine-stderr.txt | 2 +- .../MyExperimentalFeature/CMakeLists.txt | 2 +- .../MyProductionCode/CMakeLists.txt | 2 +- .../MyThirdPartyDependency/CMakeLists.txt | 2 +- .../MyThirdPartyDependency/src/CMakeLists.txt | 10 +- .../src/{thirdparty.c => third_party.c} | 0 .../test.cmake.in | 2 +- .../RunCMake/ctest_memcheck/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_memcheck/test.cmake.in | 2 +- .../ctest_skipped_test/CMakeLists.txt.in | 2 +- .../RunCMake/ctest_skipped_test/test.cmake.in | 2 +- Tests/RunCMake/ctest_start/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_start/test.cmake.in | 2 +- Tests/RunCMake/ctest_submit/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_submit/test.cmake.in | 2 +- Tests/RunCMake/ctest_test/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_test/test.cmake.in | 2 +- Tests/RunCMake/ctest_update/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_update/test.cmake.in | 2 +- Tests/RunCMake/ctest_upload/CMakeLists.txt.in | 2 +- Tests/RunCMake/ctest_upload/test.cmake.in | 2 +- Tests/RunCMake/detect_jobserver.c | 2 + Tests/RunCMake/execute_process/CMakeLists.txt | 2 +- .../execute_process/Encoding-common.cmake | 16 + .../execute_process/Encoding-windows.cmake | 11 + Tests/RunCMake/execute_process/Encoding.cmake | 21 +- .../execute_process/EncodingAUTO-stderr.txt | 2 + .../execute_process/EncodingAUTO.cmake | 3 + .../EncodingCMP0176-NEW-stderr.txt | 2 + .../execute_process/EncodingCMP0176-NEW.cmake | 3 + .../EncodingCMP0176-OLD-stderr.txt | 2 + .../execute_process/EncodingCMP0176-OLD.cmake | 4 + .../execute_process/EncodingUTF-8-stderr.txt | 3 +- .../execute_process/EncodingUTF-8.cmake | 2 + .../execute_process/EncodingUTF8-stderr.txt | 3 +- .../execute_process/EncodingUTF8.cmake | 2 + .../EncodingUnknown-stderr.txt | 5 + .../execute_process/EncodingUnknown.cmake | 1 + .../execute_process/RunCMakeTest.cmake | 8 +- Tests/RunCMake/export/CMakeLists.txt | 2 +- Tests/RunCMake/file-CHMOD/CMakeLists.txt | 2 +- .../RunCMake/file-DOWNLOAD/RunCMakeTest.cmake | 5 +- .../file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt | 2 +- .../file-DOWNLOAD/TLS_VERIFY-bad.cmake | 4 +- .../TLS_VERSION-bad-stdout-darwin.txt | 7 + .../TLS_VERSION-bad-stdout-windows.txt | 7 + .../file-DOWNLOAD/TLS_VERSION-bad-stdout.txt | 4 + .../file-DOWNLOAD/TLS_VERSION-bad.cmake | 57 +- .../TLS_VERSION-invalid-result.txt | 1 + ...err.txt => TLS_VERSION-invalid-stderr.txt} | 6 +- .../file-DOWNLOAD/TLS_VERSION-invalid.cmake | 10 + .../RunCMakeTest.cmake | 1 + .../linux-all-check.cmake | 3 +- ...linux-indirect-dependencies-all-stdout.txt | 1 + .../linux-indirect-dependencies.cmake | 83 + .../file-MAKE_DIRECTORY/CMakeLists.txt | 3 + ...DIRECTORY-Result-many-dirs-FAIL-stdout.txt | 3 + ...MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake | 9 + ...ECTORY-Result-many-dirs-SUCCESS-stdout.txt | 1 + ...E_DIRECTORY-Result-many-dirs-SUCCESS.cmake | 7 + ...E_DIRECTORY-Result-one-dir-FAIL-stdout.txt | 3 + .../MAKE_DIRECTORY-Result-one-dir-FAIL.cmake | 3 + ...IRECTORY-Result-one-dir-SUCCESS-stdout.txt | 1 + ...AKE_DIRECTORY-Result-one-dir-SUCCESS.cmake | 2 + .../MAKE_DIRECTORY-one-dir-FAIL-result.txt | 1 + .../MAKE_DIRECTORY-one-dir-FAIL-stderr.txt | 9 + .../MAKE_DIRECTORY-one-dir-FAIL.cmake} | 0 .../file-MAKE_DIRECTORY/RunCMakeTest.cmake | 7 + Tests/RunCMake/file-RPATH/Common.cmake | 19 +- Tests/RunCMake/file-RPATH/ELF.cmake | 5 +- .../file-RPATH/ELF/elf64lsb-static.bin | Bin 0 -> 4320 bytes Tests/RunCMake/file-RPATH/XCOFF.cmake | 2 +- Tests/RunCMake/file/CMakeLists.txt | 2 +- .../file/MAKE_DIRECTORY-fail-stderr.txt | 9 - Tests/RunCMake/file/REAL_PATH-existing.cmake | 2 + .../file/REAL_PATH-non-existing-stderr.txt | 9 + .../file/REAL_PATH-non-existing.cmake | 2 + Tests/RunCMake/file/RunCMakeTest.cmake | 4 +- Tests/RunCMake/find_dependency/CMakeLists.txt | 2 +- Tests/RunCMake/find_file/CMakeLists.txt | 2 +- Tests/RunCMake/find_library/CMakeLists.txt | 2 +- Tests/RunCMake/find_package/CMakeLists.txt | 2 +- .../find_package/DebugRoot-stderr.txt | 52 + Tests/RunCMake/find_package/DebugRoot.cmake | 15 + .../Exist/DebugRoot/DebugRootConfig.cmake | 1 + .../DebugRoot/NoExist/DebugRoot/.empty | 0 .../RunCMake/find_package/RunCMakeTest.cmake | 1 + Tests/RunCMake/find_path/CMakeLists.txt | 2 +- Tests/RunCMake/find_program/CMakeLists.txt | 2 +- .../get_filename_component/CMakeLists.txt | 2 +- Tests/RunCMake/get_property/CMakeLists.txt | 2 +- Tests/RunCMake/if/CMakeLists.txt | 2 +- .../RunCMake/include/CMP0024-WARN-stderr.txt | 2 +- Tests/RunCMake/include/CMakeLists.txt | 2 +- .../include_directories/CMakeLists.txt | 2 +- .../include_external_msproject/CMakeLists.txt | 2 +- Tests/RunCMake/include_guard/CMakeLists.txt | 2 +- Tests/RunCMake/install/CMP0062-OLD-stderr.txt | 2 +- .../install/CMP0177-NEW-all-check.cmake | 36 + .../RunCMake/install/CMP0177-NEW-verify.cmake | 2 + Tests/RunCMake/install/CMP0177-NEW.cmake | 2 + .../install/CMP0177-OLD-verify-result.txt | 1 + .../install/CMP0177-OLD-verify-stderr.txt | 13 + .../RunCMake/install/CMP0177-OLD-verify.cmake | 2 + Tests/RunCMake/install/CMP0177-OLD.cmake | 2 + .../RunCMake/install/CMP0177-WARN-stderr.txt | 35 + .../install/CMP0177-WARN-verify-result.txt | 1 + .../install/CMP0177-WARN-verify-stderr.txt | 13 + .../install/CMP0177-WARN-verify.cmake | 2 + Tests/RunCMake/install/CMP0177-WARN.cmake | 1 + Tests/RunCMake/install/CMP0177.cmake | 29 + Tests/RunCMake/install/CMakeLists.txt | 2 +- .../install/DIRECTORY-TYPE-all-check.cmake | 3 + Tests/RunCMake/install/DIRECTORY-TYPE.cmake | 1 + .../install/EXPORT-TargetTwice-pkg1.txt | 5 - .../install/EXPORT-TargetTwice-pkg2.txt | 5 - .../install/FILES-TYPE-all-check.cmake | 2 + Tests/RunCMake/install/FILES-TYPE.cmake | 1 + Tests/RunCMake/install/RunCMakeTest.cmake | 8 + Tests/RunCMake/list/CMakeLists.txt | 2 +- .../RunCMake/list/GET-CMP0007-WARN-stderr.txt | 2 +- Tests/RunCMake/math/CMakeLists.txt | 2 +- Tests/RunCMake/message/CMakeLists.txt | 2 +- .../RunCMake/no_install_prefix/CMakeLists.txt | 2 +- Tests/RunCMake/project/CMP0048-NEW-stderr.txt | 2 +- Tests/RunCMake/project/CMP0048-OLD-stderr.txt | 2 +- Tests/RunCMake/project/CMP0180-NEW-stdout.txt | 16 + Tests/RunCMake/project/CMP0180-NEW.cmake | 2 + Tests/RunCMake/project/CMP0180-OLD-stdout.txt | 16 + Tests/RunCMake/project/CMP0180-OLD.cmake | 2 + ...eProjectVarsSubdir.cmake => CMP0180.cmake} | 0 Tests/RunCMake/project/CMakeLists.txt | 10 +- Tests/RunCMake/project/RunCMakeTest.cmake | 8 +- .../project/SameProjectVarsSubdir-stdout.txt | 9 - Tests/RunCMake/project/subdir1/CMakeLists.txt | 7 + Tests/RunCMake/project/subdir2/CMakeLists.txt | 1 + .../project_injected/CMP0048-WARN-stderr.txt | 2 +- .../property_init/CompileSources.cmake | 1 + Tests/RunCMake/pseudo_llvm-rc.c | 16 +- .../separate_arguments/CMakeLists.txt | 2 +- Tests/RunCMake/set/CMakeLists.txt | 2 +- Tests/RunCMake/set_property/CMakeLists.txt | 2 +- Tests/RunCMake/string/CMakeLists.txt | 2 +- .../target_compile_features/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../target_link_libraries/CMakeLists.txt | 2 +- .../LINKER_expansion-LINKER-check.cmake | 1 + ...LINKER_expansion-LINKER_NESTED-check.cmake | 4 + ..._expansion-LINKER_NESTED_SHELL-check.cmake | 4 + .../LINKER_expansion-LINKER_SHELL-check.cmake | 1 + .../LINKER_expansion-validation.cmake | 8 +- .../LINKER_expansion.cmake | 44 +- .../target_link_options/RunCMakeTest.cmake | 2 + .../RunCMake/test_include_dirs/CMakeLists.txt | 2 +- .../try_compile/BadLinkLibraries-stderr.txt | 2 +- .../try_compile/BadSources1-stderr.txt | 2 +- .../try_compile/BadSources2-stderr.txt | 2 +- Tests/RunCMake/try_compile/CMP0066-stderr.txt | 7 + Tests/RunCMake/try_compile/CMakeLists.txt | 7 +- .../CopyFileErrorNoCopyFile-stderr.txt | 2 +- Tests/RunCMake/try_compile/NoArgs-stderr.txt | 2 +- .../try_compile/NoCStandard-stderr.txt | 2 +- .../try_compile/NoCopyFile-stderr.txt | 2 +- .../try_compile/NoCopyFile2-stderr.txt | 2 +- .../try_compile/NoCopyFileError-stderr.txt | 2 +- .../try_compile/NoLogDescription-stderr.txt | 2 +- .../try_compile/NoOutputVariable-stderr.txt | 2 +- .../try_compile/NoOutputVariable2-stderr.txt | 2 +- .../RunCMake/try_compile/NoSources-stderr.txt | 2 +- .../NonSourceCompileDefinitions-stderr.txt | 2 +- .../try_compile/NonSourceCopyFile-stderr.txt | 2 +- .../OldProjectSrcDirEmpty-stderr.txt | 2 +- Tests/RunCMake/try_compile/OneArg-stderr.txt | 2 +- .../try_compile/ProjectCopyFile-stderr.txt | 2 +- .../try_compile/ProjectSrcDirEmpty-stderr.txt | 2 +- .../ProjectSrcDirMissing-stderr.txt | 2 +- .../try_compile/SourceFromBadFile-stderr.txt | 2 +- .../try_compile/SourceFromBadName-stderr.txt | 2 +- .../try_compile/SourceFromOneArg-stderr.txt | 2 +- .../SourceFromThreeArgs-stderr.txt | 2 +- .../try_compile/TargetTypeInvalid-stderr.txt | 2 +- Tests/RunCMake/try_compile/TwoArgs-stderr.txt | 2 +- .../RunCMake/try_compile/proj/CMakeLists.txt | 2 +- Tests/RunCMake/try_run/CMakeLists.txt | 2 +- Tests/RunCMake/variable_watch/CMakeLists.txt | 2 +- Tests/RunCMake/while/CMakeLists.txt | 2 +- Tests/SetLang/CMakeLists.txt | 2 +- Tests/SharedLibraryArchive/CMakeLists.txt | 38 + Tests/SharedLibraryArchive/sla-check.cmake | 14 + Tests/SharedLibraryArchive/sla.c | 7 + Tests/SharedLibraryArchive/use_sla.c | 9 + .../CMakeLists.txt | 2 +- Tests/SourceFileProperty/CMakeLists.txt | 2 +- Tests/SourceGroups/CMakeLists.txt | 2 +- Tests/SourcesProperty/CMakeLists.txt | 2 +- Tests/StagingPrefix/CMakeLists.txt | 2 +- Tests/StagingPrefix/Consumer/CMakeLists.txt | 2 +- Tests/StagingPrefix/Producer/CMakeLists.txt | 2 +- Tests/StringFileTest/CMakeLists.txt | 2 +- Tests/SubDir/CMakeLists.txt | 2 +- Tests/SubDir/Examples/CMakeLists.txt | 2 +- Tests/SubDir/Examples/example1/CMakeLists.txt | 2 +- Tests/SubDirSpaces/CMakeLists.txt | 2 +- .../SubDirSpaces/Some Examples/CMakeLists.txt | 2 +- .../Some Examples/example1/CMakeLists.txt | 2 +- Tests/SubProject/CMakeLists.txt | 2 +- Tests/SwiftMixLib/CMakeLists.txt | 12 + Tests/SwiftOnly/CMakeLists.txt | 2 +- Tests/SystemInformation/CMakeLists.txt | 2 +- Tests/TargetName/CMakeLists.txt | 2 +- Tests/TestDriver/CMakeLists.txt | 2 +- Tests/Testing/CMakeLists.txt | 5 +- Tests/TestsWorkingDirectory/CMakeLists.txt | 2 +- Tests/TryCompile/CMakeLists.txt | 2 +- Tests/TryCompile/Inner/CMakeLists.txt | 2 +- Tests/Unset/CMakeLists.txt | 2 +- Tests/UseSWIG/CMakeLists.txt | 2 +- Tests/UseSWIG/LegacyPerl/CMakeLists.txt | 2 +- Tests/UseSWIG/LegacyPython/CMakeLists.txt | 2 +- Tests/VSAndroid/CMakeLists.txt | 2 +- .../VSExcludeFromDefaultBuild/CMakeLists.txt | 2 +- Tests/VSExternalInclude/CMakeLists.txt | 16 +- Tests/VSGNUFortran/CMakeLists.txt | 2 +- .../subdir/fortran/CMakeLists.txt | 6 +- Tests/VSMARMASM/CMakeLists.txt | 1 + Tests/VSMARMASM/empty.asm | 1 + Tests/VSMASM/CMakeLists.txt | 1 + Tests/VSMASM/empty.asm | 1 + Tests/VSMidl/CMakeLists.txt | 2 +- Tests/VSMidl/src/CMakeLists.txt | 2 +- Tests/VSNASM/CMakeLists.txt | 2 +- Tests/VSProjectInSubdir/CMakeLists.txt | 2 +- Tests/VSResource/CMakeLists.txt | 2 +- Tests/VSResourceNinjaForceRSP/CMakeLists.txt | 2 +- Tests/VSWinStorePhone/CMakeLists.txt | 2 +- Tests/VSWindowsFormsResx/CMakeLists.txt | 2 +- Tests/VSXaml/CMakeLists.txt | 3 +- Tests/Visibility/CMakeLists.txt | 2 +- Tests/Wrapping/CMakeLists.txt | 6 +- Tests/X11/CMakeLists.txt | 2 +- Tests/XCTest/CMakeLists.txt | 2 +- Tests/iOSNavApp/CMakeLists.txt | 2 +- Utilities/Doxygen/CMakeLists.txt | 2 +- Utilities/Scripts/update-cppdap.bash | 2 +- Utilities/Scripts/update-curl.bash | 2 +- Utilities/Scripts/update-expat.bash | 2 +- Utilities/Scripts/update-libarchive.bash | 2 +- Utilities/Scripts/update-llpkgc.bash | 34 + Utilities/Scripts/update-third-party.bash | 2 +- Utilities/Sphinx/CMakeLists.txt | 2 +- Utilities/Sphinx/conf.py.in | 2 + Utilities/cmThirdPartyChecks.cmake | 3 + Utilities/cmcppdap/include/dap/network.h | 10 +- Utilities/cmcppdap/include/dap/protocol.h | 161 +- Utilities/cmcppdap/include/dap/typeof.h | 4 +- Utilities/cmcppdap/src/network.cpp | 9 +- Utilities/cmcppdap/src/protocol_events.cpp | 2 +- Utilities/cmcppdap/src/protocol_requests.cpp | 3 +- Utilities/cmcppdap/src/protocol_response.cpp | 5 +- Utilities/cmcppdap/src/protocol_types.cpp | 19 +- Utilities/cmcppdap/src/session.cpp | 2 +- Utilities/cmcurl/CMake/CurlSymbolHiding.cmake | 70 +- Utilities/cmcurl/CMake/CurlTests.c | 6 +- Utilities/cmcurl/CMake/FindBearSSL.cmake | 37 +- Utilities/cmcurl/CMake/FindBrotli.cmake | 65 +- Utilities/cmcurl/CMake/FindCARES.cmake | 47 - Utilities/cmcurl/CMake/FindCares.cmake | 80 + Utilities/cmcurl/CMake/FindGSS.cmake | 325 +-- Utilities/cmcurl/CMake/FindLibSSH2.cmake | 45 - Utilities/cmcurl/CMake/FindLibgsasl.cmake | 78 + Utilities/cmcurl/CMake/FindLibidn2.cmake | 78 + Utilities/cmcurl/CMake/FindLibpsl.cmake | 80 + Utilities/cmcurl/CMake/FindLibssh.cmake | 92 + Utilities/cmcurl/CMake/FindLibssh2.cmake | 80 + Utilities/cmcurl/CMake/FindLibuv.cmake | 88 + Utilities/cmcurl/CMake/FindMSH3.cmake | 49 +- Utilities/cmcurl/CMake/FindMbedTLS.cmake | 91 +- Utilities/cmcurl/CMake/FindNGHTTP2.cmake | 61 +- Utilities/cmcurl/CMake/FindNGHTTP3.cmake | 56 +- Utilities/cmcurl/CMake/FindNGTCP2.cmake | 96 +- Utilities/cmcurl/CMake/FindNettle.cmake | 83 + .../{FindQUICHE.cmake => FindQuiche.cmake} | 51 +- .../{FindLibPSL.cmake => FindRustls.cmake} | 56 +- Utilities/cmcurl/CMake/FindWolfSSH.cmake | 64 + Utilities/cmcurl/CMake/FindWolfSSL.cmake | 78 +- Utilities/cmcurl/CMake/FindZstd.cmake | 93 +- Utilities/cmcurl/CMake/Macros.cmake | 66 +- Utilities/cmcurl/CMake/OtherTests.cmake | 62 +- Utilities/cmcurl/CMake/PickyWarnings.cmake | 36 +- .../cmcurl/CMake/Platforms/WindowsCache.cmake | 20 +- Utilities/cmcurl/CMake/Utilities.cmake | 4 +- .../cmcurl/CMake/cmake_uninstall.cmake.in | 18 +- Utilities/cmcurl/CMake/curl-config.cmake.in | 12 + Utilities/cmcurl/CMakeLists.txt | 1764 ++++++++------ Utilities/cmcurl/include/curl/curl.h | 151 +- Utilities/cmcurl/include/curl/curlver.h | 10 +- Utilities/cmcurl/include/curl/easy.h | 2 +- Utilities/cmcurl/include/curl/mprintf.h | 11 +- Utilities/cmcurl/include/curl/multi.h | 16 +- Utilities/cmcurl/include/curl/system.h | 6 +- Utilities/cmcurl/include/curl/typecheck-gcc.h | 10 +- Utilities/cmcurl/include/curl/urlapi.h | 5 +- Utilities/cmcurl/lib/CMakeLists.txt | 175 +- Utilities/cmcurl/lib/altsvc.c | 68 +- Utilities/cmcurl/lib/altsvc.h | 4 +- Utilities/cmcurl/lib/amigaos.c | 2 +- Utilities/cmcurl/lib/arpa_telnet.h | 2 +- Utilities/cmcurl/lib/asyn-ares.c | 92 +- Utilities/cmcurl/lib/asyn-thread.c | 281 +-- Utilities/cmcurl/lib/asyn.h | 16 +- Utilities/cmcurl/lib/bufq.c | 69 + Utilities/cmcurl/lib/bufq.h | 6 + Utilities/cmcurl/lib/bufref.c | 2 +- Utilities/cmcurl/lib/c-hyper.c | 330 +-- Utilities/cmcurl/lib/cf-h1-proxy.c | 35 +- Utilities/cmcurl/lib/cf-h2-proxy.c | 266 ++- Utilities/cmcurl/lib/cf-haproxy.c | 37 +- Utilities/cmcurl/lib/cf-https-connect.c | 98 +- Utilities/cmcurl/lib/cf-socket.c | 698 ++++-- Utilities/cmcurl/lib/cf-socket.h | 9 +- Utilities/cmcurl/lib/cfilters.c | 187 +- Utilities/cmcurl/lib/cfilters.h | 80 +- Utilities/cmcurl/lib/conncache.c | 1435 ++++++++--- Utilities/cmcurl/lib/conncache.h | 241 +- Utilities/cmcurl/lib/connect.c | 263 +- Utilities/cmcurl/lib/connect.h | 27 +- Utilities/cmcurl/lib/content_encoding.c | 63 +- Utilities/cmcurl/lib/cookie.c | 86 +- Utilities/cmcurl/lib/cookie.h | 2 +- Utilities/cmcurl/lib/curl_addrinfo.c | 20 +- Utilities/cmcurl/lib/curl_addrinfo.h | 6 +- Utilities/cmcurl/lib/curl_config.h.cmake | 67 +- Utilities/cmcurl/lib/curl_des.c | 10 +- Utilities/cmcurl/lib/curl_des.h | 8 +- Utilities/cmcurl/lib/curl_endian.c | 6 +- Utilities/cmcurl/lib/curl_fnmatch.c | 2 +- Utilities/cmcurl/lib/curl_fnmatch.h | 2 +- Utilities/cmcurl/lib/curl_gethostname.c | 25 +- Utilities/cmcurl/lib/curl_memrchr.c | 4 + Utilities/cmcurl/lib/curl_memrchr.h | 4 + Utilities/cmcurl/lib/curl_multibyte.h | 13 +- Utilities/cmcurl/lib/curl_ntlm_core.c | 62 +- Utilities/cmcurl/lib/curl_ntlm_core.h | 7 - Utilities/cmcurl/lib/curl_printf.h | 5 +- Utilities/cmcurl/lib/curl_range.c | 15 +- Utilities/cmcurl/lib/curl_rtmp.c | 9 +- Utilities/cmcurl/lib/curl_sasl.c | 2 +- Utilities/cmcurl/lib/curl_setup.h | 107 +- Utilities/cmcurl/lib/curl_setup_once.h | 14 +- Utilities/cmcurl/lib/curl_sha256.h | 9 +- Utilities/cmcurl/lib/curl_sha512_256.c | 93 +- Utilities/cmcurl/lib/curl_sha512_256.h | 2 +- Utilities/cmcurl/lib/curl_sspi.c | 28 +- Utilities/cmcurl/lib/curl_sspi.h | 4 +- Utilities/cmcurl/lib/curl_threads.c | 15 +- Utilities/cmcurl/lib/curl_threads.h | 9 +- Utilities/cmcurl/lib/curl_trc.c | 205 +- Utilities/cmcurl/lib/curl_trc.h | 32 + Utilities/cmcurl/lib/curlx.h | 43 +- Utilities/cmcurl/lib/cw-out.c | 4 +- Utilities/cmcurl/lib/dict.c | 8 +- Utilities/cmcurl/lib/doh.c | 624 ++--- Utilities/cmcurl/lib/doh.h | 70 +- Utilities/cmcurl/lib/dynbuf.c | 4 +- Utilities/cmcurl/lib/dynhds.c | 2 +- Utilities/cmcurl/lib/dynhds.h | 36 +- Utilities/cmcurl/lib/easy.c | 188 +- Utilities/cmcurl/lib/easygetopt.c | 2 +- Utilities/cmcurl/lib/easyif.h | 2 +- Utilities/cmcurl/lib/easyoptions.c | 3 +- Utilities/cmcurl/lib/escape.c | 19 +- Utilities/cmcurl/lib/file.c | 63 +- Utilities/cmcurl/lib/fileinfo.h | 2 +- Utilities/cmcurl/lib/fopen.c | 8 +- Utilities/cmcurl/lib/formdata.c | 26 +- Utilities/cmcurl/lib/formdata.h | 4 +- Utilities/cmcurl/lib/ftp.c | 290 +-- Utilities/cmcurl/lib/ftp.h | 6 +- Utilities/cmcurl/lib/getenv.c | 6 +- Utilities/cmcurl/lib/getinfo.c | 62 +- Utilities/cmcurl/lib/gopher.c | 10 +- Utilities/cmcurl/lib/hash.c | 118 +- Utilities/cmcurl/lib/hash.h | 21 +- Utilities/cmcurl/lib/headers.c | 50 +- Utilities/cmcurl/lib/headers.h | 2 +- Utilities/cmcurl/lib/hmac.c | 2 +- Utilities/cmcurl/lib/hostasyn.c | 2 +- Utilities/cmcurl/lib/hostip.c | 137 +- Utilities/cmcurl/lib/hostip.h | 29 +- Utilities/cmcurl/lib/hostip4.c | 26 +- Utilities/cmcurl/lib/hostip6.c | 2 +- Utilities/cmcurl/lib/hsts.c | 54 +- Utilities/cmcurl/lib/hsts.h | 6 +- Utilities/cmcurl/lib/http.c | 363 +-- Utilities/cmcurl/lib/http.h | 11 +- Utilities/cmcurl/lib/http1.c | 2 +- Utilities/cmcurl/lib/http2.c | 773 +++--- Utilities/cmcurl/lib/http_aws_sigv4.c | 266 ++- Utilities/cmcurl/lib/http_chunks.c | 15 +- Utilities/cmcurl/lib/http_chunks.h | 10 +- Utilities/cmcurl/lib/http_negotiate.c | 21 +- Utilities/cmcurl/lib/http_ntlm.c | 8 +- Utilities/cmcurl/lib/http_ntlm.h | 4 +- Utilities/cmcurl/lib/http_proxy.c | 1 + Utilities/cmcurl/lib/idn.c | 125 +- Utilities/cmcurl/lib/if2ip.c | 8 + Utilities/cmcurl/lib/imap.c | 26 +- Utilities/cmcurl/lib/inet_ntop.c | 17 +- Utilities/cmcurl/lib/inet_ntop.h | 7 +- Utilities/cmcurl/lib/inet_pton.c | 16 +- Utilities/cmcurl/lib/krb5.c | 41 +- Utilities/cmcurl/lib/ldap.c | 34 +- Utilities/cmcurl/lib/libcurl.rc | 2 +- Utilities/cmcurl/lib/llist.c | 200 +- Utilities/cmcurl/lib/llist.h | 67 +- Utilities/cmcurl/lib/macos.c | 28 +- Utilities/cmcurl/lib/md4.c | 148 +- Utilities/cmcurl/lib/md5.c | 179 +- Utilities/cmcurl/lib/memdebug.c | 24 +- Utilities/cmcurl/lib/memdebug.h | 17 +- Utilities/cmcurl/lib/mime.c | 81 +- Utilities/cmcurl/lib/mime.h | 4 +- Utilities/cmcurl/lib/mprintf.c | 9 +- Utilities/cmcurl/lib/mqtt.c | 24 +- Utilities/cmcurl/lib/multi.c | 1662 +++++++------ Utilities/cmcurl/lib/multihandle.h | 48 +- Utilities/cmcurl/lib/multiif.h | 28 +- Utilities/cmcurl/lib/netrc.c | 8 +- Utilities/cmcurl/lib/netrc.h | 2 +- Utilities/cmcurl/lib/nonblock.c | 19 +- Utilities/cmcurl/lib/noproxy.c | 35 +- Utilities/cmcurl/lib/noproxy.h | 6 +- Utilities/cmcurl/lib/openldap.c | 6 +- Utilities/cmcurl/lib/parsedate.c | 14 +- Utilities/cmcurl/lib/pingpong.c | 160 +- Utilities/cmcurl/lib/pingpong.h | 6 +- Utilities/cmcurl/lib/pop3.c | 104 +- Utilities/cmcurl/lib/pop3.h | 5 - Utilities/cmcurl/lib/progress.c | 282 +-- Utilities/cmcurl/lib/progress.h | 8 +- Utilities/cmcurl/lib/rand.c | 155 +- Utilities/cmcurl/lib/rand.h | 12 +- Utilities/cmcurl/lib/rename.c | 2 +- Utilities/cmcurl/lib/request.c | 163 +- Utilities/cmcurl/lib/request.h | 35 +- Utilities/cmcurl/lib/rtsp.c | 19 +- Utilities/cmcurl/lib/rtsp.h | 10 - Utilities/cmcurl/lib/select.c | 176 +- Utilities/cmcurl/lib/select.h | 33 + Utilities/cmcurl/lib/sendf.c | 115 +- Utilities/cmcurl/lib/sendf.h | 8 + Utilities/cmcurl/lib/setopt.c | 210 +- Utilities/cmcurl/lib/setup-os400.h | 13 + Utilities/cmcurl/lib/setup-vms.h | 4 +- Utilities/cmcurl/lib/setup-win32.h | 10 +- Utilities/cmcurl/lib/sha256.c | 38 +- Utilities/cmcurl/lib/share.c | 19 +- Utilities/cmcurl/lib/share.h | 7 +- Utilities/cmcurl/lib/sigpipe.h | 19 + Utilities/cmcurl/lib/smb.c | 39 +- Utilities/cmcurl/lib/smtp.c | 111 +- Utilities/cmcurl/lib/socketpair.c | 73 +- Utilities/cmcurl/lib/socketpair.h | 54 +- Utilities/cmcurl/lib/socks.c | 37 +- Utilities/cmcurl/lib/socks_gssapi.c | 25 +- Utilities/cmcurl/lib/socks_sspi.c | 181 +- Utilities/cmcurl/lib/splay.c | 43 +- Utilities/cmcurl/lib/splay.h | 12 +- Utilities/cmcurl/lib/strcase.c | 12 +- Utilities/cmcurl/lib/strerror.c | 43 +- Utilities/cmcurl/lib/strtok.c | 2 +- Utilities/cmcurl/lib/strtoofft.c | 16 +- Utilities/cmcurl/lib/strtoofft.h | 2 +- Utilities/cmcurl/lib/system_win32.c | 45 +- Utilities/cmcurl/lib/system_win32.h | 32 +- Utilities/cmcurl/lib/telnet.c | 32 +- Utilities/cmcurl/lib/tftp.c | 79 +- Utilities/cmcurl/lib/timediff.h | 4 +- Utilities/cmcurl/lib/timeval.c | 16 +- Utilities/cmcurl/lib/transfer.c | 426 ++-- Utilities/cmcurl/lib/transfer.h | 64 +- Utilities/cmcurl/lib/url.c | 1379 +++++------ Utilities/cmcurl/lib/url.h | 20 +- Utilities/cmcurl/lib/urlapi-int.h | 6 +- Utilities/cmcurl/lib/urlapi.c | 118 +- Utilities/cmcurl/lib/urldata.h | 249 +- Utilities/cmcurl/lib/vauth/cleartext.c | 8 +- Utilities/cmcurl/lib/vauth/cram.c | 2 +- Utilities/cmcurl/lib/vauth/digest.c | 26 +- Utilities/cmcurl/lib/vauth/digest_sspi.c | 68 +- Utilities/cmcurl/lib/vauth/krb5_gssapi.c | 6 +- Utilities/cmcurl/lib/vauth/krb5_sspi.c | 34 +- Utilities/cmcurl/lib/vauth/ntlm.c | 44 +- Utilities/cmcurl/lib/vauth/ntlm_sspi.c | 30 +- Utilities/cmcurl/lib/vauth/oauth2.c | 6 +- Utilities/cmcurl/lib/vauth/spnego_gssapi.c | 20 +- Utilities/cmcurl/lib/vauth/spnego_sspi.c | 59 +- Utilities/cmcurl/lib/vauth/vauth.c | 8 +- Utilities/cmcurl/lib/version.c | 21 +- Utilities/cmcurl/lib/version_win32.c | 16 +- Utilities/cmcurl/lib/version_win32.h | 2 +- Utilities/cmcurl/lib/vquic/curl_msh3.c | 77 +- Utilities/cmcurl/lib/vquic/curl_ngtcp2.c | 390 +-- Utilities/cmcurl/lib/vquic/curl_osslq.c | 321 ++- Utilities/cmcurl/lib/vquic/curl_quiche.c | 410 ++-- Utilities/cmcurl/lib/vquic/vquic-tls.c | 79 +- Utilities/cmcurl/lib/vquic/vquic-tls.h | 5 +- Utilities/cmcurl/lib/vquic/vquic.c | 106 +- Utilities/cmcurl/lib/vssh/libssh.c | 172 +- Utilities/cmcurl/lib/vssh/libssh2.c | 139 +- Utilities/cmcurl/lib/vssh/ssh.h | 8 +- Utilities/cmcurl/lib/vssh/wolfssh.c | 37 +- Utilities/cmcurl/lib/vtls/bearssl.c | 216 +- Utilities/cmcurl/lib/vtls/cipher_suite.c | 257 +- Utilities/cmcurl/lib/vtls/cipher_suite.h | 6 +- Utilities/cmcurl/lib/vtls/gtls.c | 719 +++--- Utilities/cmcurl/lib/vtls/gtls.h | 18 +- Utilities/cmcurl/lib/vtls/hostcheck.c | 2 +- Utilities/cmcurl/lib/vtls/hostcheck.h | 2 +- Utilities/cmcurl/lib/vtls/mbedtls.c | 643 +++-- Utilities/cmcurl/lib/vtls/openssl.c | 1070 +++++---- Utilities/cmcurl/lib/vtls/openssl.h | 16 +- Utilities/cmcurl/lib/vtls/rustls.c | 476 +++- Utilities/cmcurl/lib/vtls/schannel.c | 394 +-- Utilities/cmcurl/lib/vtls/schannel_int.h | 15 +- Utilities/cmcurl/lib/vtls/schannel_verify.c | 27 +- Utilities/cmcurl/lib/vtls/sectransp.c | 1681 ++++--------- Utilities/cmcurl/lib/vtls/vtls.c | 416 ++-- Utilities/cmcurl/lib/vtls/vtls.h | 31 +- Utilities/cmcurl/lib/vtls/vtls_int.h | 65 +- Utilities/cmcurl/lib/vtls/wolfssl.c | 1050 +++++--- Utilities/cmcurl/lib/vtls/wolfssl.h | 19 + Utilities/cmcurl/lib/vtls/x509asn1.c | 97 +- Utilities/cmcurl/lib/vtls/x509asn1.h | 14 +- Utilities/cmcurl/lib/ws.c | 265 +- Utilities/cmcurl/lib/ws.h | 2 +- Utilities/cmexpat/COPYING | 2 +- Utilities/cmexpat/README.md | 27 +- Utilities/cmexpat/expat_config.h.cmake | 14 +- Utilities/cmexpat/lib/expat.h | 51 +- Utilities/cmexpat/lib/internal.h | 19 +- Utilities/cmexpat/lib/siphash.h | 10 +- Utilities/cmexpat/lib/winconfig.h | 7 +- Utilities/cmexpat/lib/xmlparse.c | 785 ++++-- Utilities/cmexpat/lib/xmlrole.c | 6 +- Utilities/cmexpat/lib/xmlrole.h | 6 +- Utilities/cmexpat/lib/xmltok.c | 36 +- Utilities/cmexpat/lib/xmltok.h | 8 +- Utilities/cmexpat/lib/xmltok_impl.c | 6 +- Utilities/cmexpat/lib/xmltok_impl.h | 2 +- Utilities/cmlibarchive/CMakeLists.txt | 125 +- .../build/cmake/FindPCRE2POSIX.cmake | 34 + .../cmlibarchive/build/cmake/config.h.in | 20 +- .../utils/gen_archive_string_composition_h.sh | 3 - Utilities/cmlibarchive/build/version | 2 +- .../cmlibarchive/libarchive/CMakeLists.txt | 7 +- Utilities/cmlibarchive/libarchive/archive.h | 12 +- .../cmlibarchive/libarchive/archive_acl.c | 52 +- .../libarchive/archive_acl_private.h | 4 +- .../libarchive/archive_check_magic.c | 20 +- .../cmlibarchive/libarchive/archive_cmdline.c | 2 - .../libarchive/archive_cmdline_private.h | 2 - .../cmlibarchive/libarchive/archive_crc32.h | 7 +- .../cmlibarchive/libarchive/archive_cryptor.c | 4 +- .../cmlibarchive/libarchive/archive_endian.h | 2 - .../cmlibarchive/libarchive/archive_entry.3 | 2 - .../cmlibarchive/libarchive/archive_entry.c | 326 ++- .../cmlibarchive/libarchive/archive_entry.h | 14 +- .../libarchive/archive_entry_acl.3 | 2 +- .../libarchive/archive_entry_copy_bhfi.c | 1 - .../libarchive/archive_entry_copy_stat.c | 1 - .../libarchive/archive_entry_link_resolver.c | 16 +- .../libarchive/archive_entry_locale.h | 2 - .../libarchive/archive_entry_perms.3 | 1 + .../libarchive/archive_entry_private.h | 10 +- .../libarchive/archive_entry_sparse.c | 1 - .../libarchive/archive_entry_stat.c | 1 - .../libarchive/archive_entry_strmode.c | 1 - .../libarchive/archive_entry_time.3 | 2 - .../libarchive/archive_entry_xattr.c | 1 - .../cmlibarchive/libarchive/archive_getdate.c | 4 - .../cmlibarchive/libarchive/archive_getdate.h | 2 - .../cmlibarchive/libarchive/archive_match.c | 29 +- .../cmlibarchive/libarchive/archive_options.c | 1 - .../libarchive/archive_options_private.h | 2 - .../libarchive/archive_pack_dev.c | 7 - .../libarchive/archive_pathmatch.c | 1 - .../libarchive/archive_pathmatch.h | 2 - .../libarchive/archive_platform.h | 15 - .../libarchive/archive_platform_acl.h | 2 - .../libarchive/archive_platform_xattr.h | 2 - .../cmlibarchive/libarchive/archive_ppmd8.c | 5 +- .../cmlibarchive/libarchive/archive_private.h | 14 +- .../cmlibarchive/libarchive/archive_random.c | 1 - .../cmlibarchive/libarchive/archive_read.3 | 2 - .../cmlibarchive/libarchive/archive_read.c | 3 +- .../libarchive/archive_read_add_passphrase.3 | 2 - .../libarchive/archive_read_add_passphrase.c | 1 - .../libarchive/archive_read_append_filter.c | 3 +- .../libarchive/archive_read_data.3 | 2 - .../libarchive/archive_read_data_into_fd.c | 1 - .../libarchive/archive_read_disk.3 | 6 +- .../archive_read_disk_entry_from_file.c | 2 +- .../libarchive/archive_read_disk_posix.c | 1 - .../libarchive/archive_read_disk_private.h | 2 - .../archive_read_disk_set_standard_lookup.c | 1 - .../libarchive/archive_read_disk_windows.c | 4 +- .../libarchive/archive_read_extract.3 | 2 - .../libarchive/archive_read_extract.c | 1 - .../libarchive/archive_read_extract2.c | 1 - .../libarchive/archive_read_filter.3 | 2 - .../libarchive/archive_read_format.3 | 2 - .../libarchive/archive_read_free.3 | 2 - .../libarchive/archive_read_header.3 | 2 - .../libarchive/archive_read_new.3 | 2 - .../libarchive/archive_read_open.3 | 2 - .../libarchive/archive_read_open_fd.c | 1 - .../libarchive/archive_read_open_file.c | 1 - .../libarchive/archive_read_open_filename.c | 112 +- .../libarchive/archive_read_open_memory.c | 1 - .../libarchive/archive_read_private.h | 2 - .../libarchive/archive_read_set_format.c | 1 - .../libarchive/archive_read_set_options.3 | 2 - .../libarchive/archive_read_set_options.c | 1 - .../archive_read_support_filter_all.c | 1 - .../archive_read_support_filter_by_code.c | 1 - .../archive_read_support_filter_bzip2.c | 4 +- .../archive_read_support_filter_compress.c | 3 +- .../archive_read_support_filter_grzip.c | 3 - .../archive_read_support_filter_gzip.c | 5 +- .../archive_read_support_filter_lrzip.c | 3 - .../archive_read_support_filter_lz4.c | 8 +- .../archive_read_support_filter_lzop.c | 9 +- .../archive_read_support_filter_none.c | 1 - .../archive_read_support_filter_program.c | 1 - .../archive_read_support_filter_rpm.c | 45 +- .../archive_read_support_filter_uu.c | 17 +- .../archive_read_support_filter_xz.c | 20 +- .../archive_read_support_filter_zstd.c | 4 +- .../archive_read_support_format_7zip.c | 43 +- .../archive_read_support_format_all.c | 3 +- .../archive_read_support_format_ar.c | 11 +- .../archive_read_support_format_by_code.c | 1 - .../archive_read_support_format_cab.c | 2 +- .../archive_read_support_format_cpio.c | 29 +- .../archive_read_support_format_empty.c | 1 - .../archive_read_support_format_iso9660.c | 121 +- .../archive_read_support_format_lha.c | 15 +- .../archive_read_support_format_mtree.c | 9 +- .../archive_read_support_format_rar.c | 145 +- .../archive_read_support_format_rar5.c | 88 +- .../archive_read_support_format_raw.c | 1 - .../archive_read_support_format_tar.c | 2124 +++++++++++------ .../archive_read_support_format_warc.c | 11 +- .../archive_read_support_format_xar.c | 42 +- .../archive_read_support_format_zip.c | 534 +++-- .../cmlibarchive/libarchive/archive_string.c | 178 +- .../cmlibarchive/libarchive/archive_string.h | 3 - .../libarchive/archive_string_composition.h | 2 - .../libarchive/archive_string_sprintf.c | 1 - .../cmlibarchive/libarchive/archive_util.3 | 2 - .../cmlibarchive/libarchive/archive_util.c | 14 +- .../libarchive/archive_version_details.c | 1 - .../cmlibarchive/libarchive/archive_virtual.c | 1 - .../cmlibarchive/libarchive/archive_windows.c | 2 - .../cmlibarchive/libarchive/archive_windows.h | 9 +- .../cmlibarchive/libarchive/archive_write.3 | 2 - .../cmlibarchive/libarchive/archive_write.c | 14 +- .../libarchive/archive_write_add_filter.c | 1 - .../archive_write_add_filter_b64encode.c | 6 +- .../archive_write_add_filter_by_name.c | 1 - .../archive_write_add_filter_bzip2.c | 4 +- .../archive_write_add_filter_compress.c | 4 +- .../archive_write_add_filter_grzip.c | 2 - .../archive_write_add_filter_gzip.c | 2 - .../archive_write_add_filter_lrzip.c | 2 - .../libarchive/archive_write_add_filter_lz4.c | 2 - .../archive_write_add_filter_lzop.c | 1 - .../archive_write_add_filter_none.c | 1 - .../archive_write_add_filter_program.c | 1 - .../archive_write_add_filter_uuencode.c | 2 - .../libarchive/archive_write_add_filter_xz.c | 4 +- .../archive_write_add_filter_zstd.c | 138 +- .../libarchive/archive_write_blocksize.3 | 2 - .../libarchive/archive_write_data.3 | 2 - .../libarchive/archive_write_disk.3 | 2 - .../libarchive/archive_write_disk_posix.c | 16 +- .../libarchive/archive_write_disk_private.h | 2 - .../archive_write_disk_set_standard_lookup.c | 1 - .../libarchive/archive_write_disk_windows.c | 3 +- .../libarchive/archive_write_filter.3 | 2 - .../libarchive/archive_write_finish_entry.3 | 2 - .../libarchive/archive_write_format.3 | 2 - .../libarchive/archive_write_free.3 | 2 - .../libarchive/archive_write_header.3 | 2 - .../libarchive/archive_write_new.3 | 2 - .../libarchive/archive_write_open.3 | 2 - .../libarchive/archive_write_open_fd.c | 1 - .../libarchive/archive_write_open_file.c | 1 - .../libarchive/archive_write_open_filename.c | 1 - .../libarchive/archive_write_open_memory.c | 1 - .../libarchive/archive_write_private.h | 4 +- .../libarchive/archive_write_set_format.c | 1 - .../archive_write_set_format_7zip.c | 1 - .../libarchive/archive_write_set_format_ar.c | 1 - .../archive_write_set_format_by_name.c | 1 - .../archive_write_set_format_cpio_binary.c | 4 +- .../archive_write_set_format_cpio_newc.c | 1 - .../archive_write_set_format_cpio_odc.c | 4 +- .../archive_write_set_format_filter_by_ext.c | 1 - .../archive_write_set_format_gnutar.c | 8 +- .../archive_write_set_format_iso9660.c | 2 +- .../archive_write_set_format_mtree.c | 1 - .../libarchive/archive_write_set_format_pax.c | 17 +- .../archive_write_set_format_private.h | 2 - .../archive_write_set_format_shar.c | 9 +- .../archive_write_set_format_ustar.c | 8 +- .../archive_write_set_format_v7tar.c | 4 +- .../archive_write_set_format_warc.c | 1 - .../libarchive/archive_write_set_format_xar.c | 27 +- .../libarchive/archive_write_set_format_zip.c | 195 +- .../libarchive/archive_write_set_options.3 | 11 +- .../libarchive/archive_write_set_options.c | 1 - .../libarchive/archive_write_set_passphrase.3 | 2 - .../libarchive/archive_write_set_passphrase.c | 36 +- .../cmlibarchive/libarchive/config_freebsd.h | 3 - Utilities/cmlibarchive/libarchive/cpio.5 | 2 - .../cmlibarchive/libarchive/filter_fork.h | 2 - .../libarchive/filter_fork_posix.c | 2 - .../libarchive/libarchive-formats.5 | 2 - .../cmlibarchive/libarchive/libarchive.3 | 2 - .../libarchive/libarchive_changes.3 | 2 - .../libarchive/libarchive_internals.3 | 4 +- Utilities/cmlibarchive/libarchive/mtree.5 | 1 - Utilities/cmlibarchive/libarchive/tar.5 | 2 - Utilities/cmllpkgc/CMakeLists.txt | 9 + Utilities/cmllpkgc/README.rst | 14 + Utilities/cmllpkgc/llpkgc.c | 205 ++ Utilities/cmllpkgc/llpkgc.h | 143 ++ Utilities/cmllpkgc/llpkgc__internal.c | 1069 +++++++++ Utilities/cmllpkgc/llpkgc__internal.h | 37 + Utilities/std/cmext/type_traits | 26 + bootstrap | 7 +- 2660 files changed, 54475 insertions(+), 23849 deletions(-) create mode 100644 Help/command/cmake_pkg_config.rst create mode 100644 Help/dev/integration-testing.rst delete mode 100644 Help/dev/try_compile-linker-language.rst create mode 100644 Help/envvar/CMAKE_CONFIG_DIR.rst create mode 100644 Help/envvar/CMAKE_EXPORT_BUILD_DATABASE.rst create mode 100644 Help/envvar/CMAKE_INSTALL_PARALLEL_LEVEL.rst create mode 100644 Help/module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt create mode 100644 Help/policy/CMP0171.rst create mode 100644 Help/policy/CMP0172.rst create mode 100644 Help/policy/CMP0173.rst create mode 100644 Help/policy/CMP0174.rst create mode 100644 Help/policy/CMP0175.rst create mode 100644 Help/policy/CMP0176.rst create mode 100644 Help/policy/CMP0177.rst create mode 100644 Help/policy/CMP0178.rst create mode 100644 Help/policy/CMP0179.rst create mode 100644 Help/policy/CMP0180.rst create mode 100644 Help/prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE.rst create mode 100644 Help/prop_tgt/EXPORT_BUILD_DATABASE.rst create mode 100644 Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst create mode 100644 Help/prop_tgt/VS_FRAMEWORK_REFERENCES.rst create mode 100644 Help/release/3.31.rst create mode 100644 Help/variable/CMAKE_AIX_SHARED_LIBRARY_ARCHIVE.rst create mode 100644 Help/variable/CMAKE_EXPORT_BUILD_DATABASE.rst create mode 100644 Help/variable/CMAKE_HOST_EXECUTABLE_SUFFIX.rst create mode 100644 Help/variable/CMAKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE.rst create mode 100644 Help/variable/CMAKE_LANG_HOST_COMPILER_ID.rst create mode 100644 Help/variable/CMAKE_LANG_HOST_COMPILER_VERSION.rst create mode 100644 Help/variable/CMAKE_LANG_STANDARD_LINK_DIRECTORIES.rst create mode 100644 Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst create mode 100644 Help/variable/CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX.rst create mode 100644 Help/variable/CMAKE_WINDOWS_KMDF_VERSION.rst create mode 100644 Help/variable/WASI.rst create mode 100644 Modules/Compiler/LFortran-Fortran.cmake create mode 100644 Modules/Internal/CMakeASM-ATTLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeASMLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeASM_MARMASMLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeASM_MASMLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeASM_NASMLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeCLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeCUDALinkerInformation.cmake create mode 100644 Modules/Internal/CMakeCXXLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeCommonLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeFortranLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeHIPLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeOBJCLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeOBJCXXLinkerInformation.cmake create mode 100644 Modules/Internal/CMakeSwiftLinkerInformation.cmake create mode 100644 Modules/Linker/AIX-ASM.cmake create mode 100644 Modules/Linker/AIX-C.cmake create mode 100644 Modules/Linker/AIX-CXX.cmake create mode 100644 Modules/Linker/AIX-Fortran.cmake create mode 100644 Modules/Linker/AIX.cmake create mode 100644 Modules/Linker/AppleClang-ASM.cmake create mode 100644 Modules/Linker/AppleClang-C.cmake create mode 100644 Modules/Linker/AppleClang-CXX.cmake create mode 100644 Modules/Linker/AppleClang-OBJC.cmake create mode 100644 Modules/Linker/AppleClang-OBJCXX.cmake create mode 100644 Modules/Linker/AppleClang.cmake create mode 100644 Modules/Linker/GNU-ASM.cmake create mode 100644 Modules/Linker/GNU-C.cmake create mode 100644 Modules/Linker/GNU-CUDA.cmake create mode 100644 Modules/Linker/GNU-CXX.cmake create mode 100644 Modules/Linker/GNU-Fortran.cmake create mode 100644 Modules/Linker/GNU-HIP.cmake create mode 100644 Modules/Linker/GNU.cmake create mode 100644 Modules/Linker/GNUgold-ASM.cmake create mode 100644 Modules/Linker/GNUgold-C.cmake create mode 100644 Modules/Linker/GNUgold-CUDA.cmake create mode 100644 Modules/Linker/GNUgold-CXX.cmake create mode 100644 Modules/Linker/GNUgold-Fortran.cmake create mode 100644 Modules/Linker/GNUgold-HIP.cmake create mode 100644 Modules/Linker/GNUgold.cmake create mode 100644 Modules/Linker/LLD-ASM.cmake create mode 100644 Modules/Linker/LLD-C.cmake create mode 100644 Modules/Linker/LLD-CUDA.cmake create mode 100644 Modules/Linker/LLD-CXX.cmake create mode 100644 Modules/Linker/LLD-Fortran.cmake create mode 100644 Modules/Linker/LLD-HIP.cmake create mode 100644 Modules/Linker/LLD-OBJC.cmake create mode 100644 Modules/Linker/LLD-OBJCXX.cmake create mode 100644 Modules/Linker/LLD.cmake create mode 100644 Modules/Linker/MOLD-ASM.cmake create mode 100644 Modules/Linker/MOLD-C.cmake create mode 100644 Modules/Linker/MOLD-CUDA.cmake create mode 100644 Modules/Linker/MOLD-CXX.cmake create mode 100644 Modules/Linker/MOLD-Fortran.cmake create mode 100644 Modules/Linker/MOLD-HIP.cmake create mode 100644 Modules/Linker/MOLD-OBJC.cmake create mode 100644 Modules/Linker/MOLD-OBJCXX.cmake create mode 100644 Modules/Linker/Solaris-ASM.cmake create mode 100644 Modules/Linker/Solaris-C.cmake create mode 100644 Modules/Linker/Solaris-CXX.cmake create mode 100644 Modules/Linker/Solaris-Fortran.cmake create mode 100644 Modules/Linker/Solaris.cmake create mode 100644 Modules/Platform/AIX-LLVMFlang-Fortran.cmake create mode 100644 Modules/Platform/Linker/AIX-AIX-ASM.cmake create mode 100644 Modules/Platform/Linker/AIX-AIX-C.cmake create mode 100644 Modules/Platform/Linker/AIX-AIX-CXX.cmake create mode 100644 Modules/Platform/Linker/AIX-AIX-Fortran.cmake create mode 100644 Modules/Platform/Linker/AIX-AIX.cmake create mode 100644 Modules/Platform/Linker/AIX-ASM.cmake create mode 100644 Modules/Platform/Linker/AIX-C.cmake create mode 100644 Modules/Platform/Linker/AIX-CXX.cmake create mode 100644 Modules/Platform/Linker/AIX-Fortran.cmake create mode 100644 Modules/Platform/Linker/Apple-ASM.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-ASM.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-C.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-CUDA.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-CXX.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-Fortran.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-OBJC.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang-OBJCXX.cmake create mode 100644 Modules/Platform/Linker/Apple-AppleClang.cmake create mode 100644 Modules/Platform/Linker/Apple-C.cmake create mode 100644 Modules/Platform/Linker/Apple-CUDA.cmake create mode 100644 Modules/Platform/Linker/Apple-CXX.cmake create mode 100644 Modules/Platform/Linker/Apple-Fortran.cmake create mode 100644 Modules/Platform/Linker/Apple-LLD-C.cmake create mode 100644 Modules/Platform/Linker/Apple-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/Apple-LLD-OBJC.cmake create mode 100644 Modules/Platform/Linker/Apple-LLD-OBJCXX.cmake create mode 100644 Modules/Platform/Linker/Apple-MOLD-C.cmake create mode 100644 Modules/Platform/Linker/Apple-MOLD-CXX.cmake create mode 100644 Modules/Platform/Linker/Apple-MOLD-OBJC.cmake create mode 100644 Modules/Platform/Linker/Apple-MOLD-OBJCXX.cmake create mode 100644 Modules/Platform/Linker/Apple-OBJC.cmake create mode 100644 Modules/Platform/Linker/Apple-OBJCXX.cmake create mode 100644 Modules/Platform/Linker/BSD-Linker-Initialize.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-ASM.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-C.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-CXX.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-Fortran.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-GNU-C.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-GNU.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-LLD-C.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/CYGWIN-LLD.cmake create mode 100644 Modules/Platform/Linker/DragonFly-ASM.cmake create mode 100644 Modules/Platform/Linker/DragonFly-C.cmake create mode 100644 Modules/Platform/Linker/DragonFly-CXX.cmake create mode 100644 Modules/Platform/Linker/DragonFly-Fortran.cmake create mode 100644 Modules/Platform/Linker/DragonFly-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/DragonFly-GNU-C.cmake create mode 100644 Modules/Platform/Linker/DragonFly-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/DragonFly-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/DragonFly-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/DragonFly-LLD-C.cmake create mode 100644 Modules/Platform/Linker/DragonFly-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/DragonFly-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-ASM.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-C.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-CXX.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-Fortran.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-GNU-C.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-GNU.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-LLD-C.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/FreeBSD-LLD.cmake create mode 100644 Modules/Platform/Linker/GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/GNU-C.cmake create mode 100644 Modules/Platform/Linker/GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/GNU-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/GNU-GNU-C.cmake create mode 100644 Modules/Platform/Linker/GNU-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/GNU-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/GNU-GNU.cmake create mode 100644 Modules/Platform/Linker/GNU.cmake create mode 100644 Modules/Platform/Linker/Linux-ASM.cmake create mode 100644 Modules/Platform/Linker/Linux-C.cmake create mode 100644 Modules/Platform/Linker/Linux-CUDA.cmake create mode 100644 Modules/Platform/Linker/Linux-CXX.cmake create mode 100644 Modules/Platform/Linker/Linux-Fortran.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-C.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-CUDA.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU-HIP.cmake create mode 100644 Modules/Platform/Linker/Linux-GNU.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-ASM.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-C.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-CUDA.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-CXX.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-Fortran.cmake create mode 100644 Modules/Platform/Linker/Linux-GNUgold-HIP.cmake create mode 100644 Modules/Platform/Linker/Linux-HIP.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-C.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-CUDA.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD-HIP.cmake create mode 100644 Modules/Platform/Linker/Linux-LLD.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-ASM.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-C.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-CUDA.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-CXX.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD-HIP.cmake create mode 100644 Modules/Platform/Linker/Linux-MOLD.cmake create mode 100644 Modules/Platform/Linker/MSYS-ASM.cmake create mode 100644 Modules/Platform/Linker/MSYS-C.cmake create mode 100644 Modules/Platform/Linker/MSYS-CXX.cmake create mode 100644 Modules/Platform/Linker/MSYS-Fortran.cmake create mode 100644 Modules/Platform/Linker/MSYS-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/MSYS-GNU-C.cmake create mode 100644 Modules/Platform/Linker/MSYS-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/MSYS-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/MSYS-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/MSYS-LLD-C.cmake create mode 100644 Modules/Platform/Linker/MSYS-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/MSYS-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/MirBSD-ASM.cmake create mode 100644 Modules/Platform/Linker/MirBSD-C.cmake create mode 100644 Modules/Platform/Linker/MirBSD-CXX.cmake create mode 100644 Modules/Platform/Linker/MirBSD-Fortran.cmake create mode 100644 Modules/Platform/Linker/MirBSD-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/MirBSD-GNU-C.cmake create mode 100644 Modules/Platform/Linker/MirBSD-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/MirBSD-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/NetBSD-ASM.cmake create mode 100644 Modules/Platform/Linker/NetBSD-C.cmake create mode 100644 Modules/Platform/Linker/NetBSD-CXX.cmake create mode 100644 Modules/Platform/Linker/NetBSD-Fortran.cmake create mode 100644 Modules/Platform/Linker/NetBSD-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/NetBSD-GNU-C.cmake create mode 100644 Modules/Platform/Linker/NetBSD-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/NetBSD-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/NetBSD-GNU.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-ASM.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-C.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-CXX.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-Fortran.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-GNU-C.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-GNU.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-LLD-C.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-LLD-Fortran.cmake create mode 100644 Modules/Platform/Linker/OpenBSD-LLD.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-ASM.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-C.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-CXX.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-GNU-C.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-GNU.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-LLD-C.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/SerenityOS-LLD.cmake create mode 100644 Modules/Platform/Linker/SunOS-ASM.cmake create mode 100644 Modules/Platform/Linker/SunOS-C.cmake create mode 100644 Modules/Platform/Linker/SunOS-CXX.cmake create mode 100644 Modules/Platform/Linker/SunOS-Fortran.cmake create mode 100644 Modules/Platform/Linker/SunOS-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/SunOS-GNU-C.cmake create mode 100644 Modules/Platform/Linker/SunOS-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/SunOS-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/SunOS-GNU.cmake create mode 100644 Modules/Platform/Linker/SunOS-Solaris-ASM.cmake create mode 100644 Modules/Platform/Linker/SunOS-Solaris-C.cmake create mode 100644 Modules/Platform/Linker/SunOS-Solaris-CXX.cmake create mode 100644 Modules/Platform/Linker/SunOS-Solaris-Fortran.cmake create mode 100644 Modules/Platform/Linker/SunOS-Solaris.cmake create mode 100644 Modules/Platform/Linker/Windows-ASM.cmake create mode 100644 Modules/Platform/Linker/Windows-C.cmake create mode 100644 Modules/Platform/Linker/Windows-CUDA.cmake create mode 100644 Modules/Platform/Linker/Windows-CXX.cmake create mode 100644 Modules/Platform/Linker/Windows-Fortran.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU-ASM.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU-C.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU-CXX.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU-Fortran.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU-HIP.cmake create mode 100644 Modules/Platform/Linker/Windows-GNU.cmake create mode 100644 Modules/Platform/Linker/Windows-HIP.cmake create mode 100644 Modules/Platform/Linker/Windows-LLD-ASM.cmake create mode 100644 Modules/Platform/Linker/Windows-LLD-C.cmake create mode 100644 Modules/Platform/Linker/Windows-LLD-CXX.cmake create mode 100644 Modules/Platform/Linker/Windows-LLD-HIP.cmake create mode 100644 Modules/Platform/Linker/Windows-LLD.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-ASM.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-C.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-CUDA.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-CXX.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-Fortran.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC-HIP.cmake create mode 100644 Modules/Platform/Linker/Windows-MSVC.cmake create mode 100644 Modules/Platform/WASI-Initialize.cmake create mode 100644 Modules/Platform/WASI.cmake create mode 100644 Source/cmBuildDatabase.cxx create mode 100644 Source/cmBuildDatabase.h create mode 100644 Source/cmCMakePkgConfigCommand.cxx create mode 100644 Source/cmCMakePkgConfigCommand.h create mode 100644 Source/cmExportAndroidMKGenerator.cxx create mode 100644 Source/cmExportAndroidMKGenerator.h create mode 100644 Source/cmExportBuildCMakeConfigGenerator.cxx create mode 100644 Source/cmExportBuildCMakeConfigGenerator.h create mode 100644 Source/cmExportCMakeConfigGenerator.cxx create mode 100644 Source/cmExportCMakeConfigGenerator.h create mode 100644 Source/cmExportInstallCMakeConfigGenerator.cxx create mode 100644 Source/cmExportInstallCMakeConfigGenerator.h create mode 100644 Source/cmExportInstallPackageInfoGenerator.cxx create mode 100644 Source/cmExportInstallPackageInfoGenerator.h create mode 100644 Source/cmExportPackageInfoGenerator.cxx create mode 100644 Source/cmExportPackageInfoGenerator.h create mode 100644 Source/cmInstallAndroidMKExportGenerator.cxx create mode 100644 Source/cmInstallAndroidMKExportGenerator.h create mode 100644 Source/cmInstallCMakeConfigExportGenerator.cxx create mode 100644 Source/cmInstallCMakeConfigExportGenerator.h create mode 100644 Source/cmInstallPackageInfoExportGenerator.cxx create mode 100644 Source/cmInstallPackageInfoExportGenerator.h create mode 100644 Source/cmInstallScriptHandler.cxx create mode 100644 Source/cmInstallScriptHandler.h create mode 100644 Source/cmPkgConfigParser.cxx create mode 100644 Source/cmPkgConfigParser.h create mode 100644 Source/cmPkgConfigResolver.cxx create mode 100644 Source/cmPkgConfigResolver.h delete mode 100644 Templates/CTestScript.cmake.in create mode 100644 Tests/CMakeLib/testAssert.cxx create mode 100644 Tests/CMakeLib/testConfig.h.in delete mode 100644 Tests/CMakeLib/testVisualStudioSlnParser.h.in delete mode 100644 Tests/CMakeLib/testXMLParser.h.in delete mode 100644 Tests/CMakeOnly/CompilerIdC/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdCSharp/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdCUDA/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdCXX/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdFortran/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt delete mode 100644 Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt create mode 100644 Tests/CPackIFWGenerator/ApplicationIcon.png create mode 100644 Tests/CPackIFWGenerator/BundleIcon.icns create mode 100644 Tests/CPackIFWGenerator/CMakeLists.txt create mode 100644 Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake create mode 100644 Tests/CPackIFWGenerator/SplashScreen.png create mode 100644 Tests/CPackIFWGenerator/install.ico create mode 100644 Tests/CPackIFWGenerator/main.cpp create mode 100644 Tests/CudaOnly/Unity/CMakeLists.txt create mode 100644 Tests/CudaOnly/Unity/a.cu create mode 100644 Tests/CudaOnly/Unity/b.cu create mode 100644 Tests/CudaOnly/Unity/main.cu create mode 100644 Tests/ExportImport/Export/testSharedLibArchiveAIX.c create mode 100644 Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfig.cmake create mode 100644 Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfigVersion.cmake rename Tests/FindProtobuf/Test/msgs/{ => grpc}/example_service.proto (100%) delete mode 100644 Tests/RunCMake/Autogen/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_1}/AutogenSkipLinting-build-stderr.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/AutogenSkipLinting.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/AutogenUseSystemIncludeCommon.cmake (92%) rename Tests/RunCMake/{Autogen => Autogen_1}/AutogenUseSystemIncludeOff.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/AutogenUseSystemIncludeOn.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0111-imported-target-full.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0111-imported-target-implib-only.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0111-imported-target-libname.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0111-imported-target-prelude.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0151-common.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0151-new.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMP0151-old.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/CMakeLists.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/Inspect.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocGeneratedFile.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocPredefs-build-stderr.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocPredefs-check.cxx (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocPredefs-prefix.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocPredefs.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/MocPredefs.cxx (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/NoQt-stderr.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/NoQt.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/QtInFunction.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/QtInFunctionNested-stderr.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/QtInFunctionNested.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/QtInFunctionProperty.cmake (100%) create mode 100644 Tests/RunCMake/Autogen_1/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_1}/SkipLinting.cxx (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/SkipLinting.h (100%) rename Tests/RunCMake/{Autogen => Autogen_1}/empty.cpp (100%) rename Tests/RunCMake/{VS10ProjectWinCE => Autogen_2}/CMakeLists.txt (62%) rename Tests/RunCMake/{Autogen => Autogen_2}/MyWindow.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/MyWindow.h (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/MyWindow.ui (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/QtAutoMocDeps-stderr.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/QtAutoMocDeps.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/QtSubDir1/CMakeLists.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/QtSubDir2/CMakeLists.txt (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/QtSubDir3/CMakeLists.txt (100%) create mode 100644 Tests/RunCMake/Autogen_2/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_2}/app.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/app_qt.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_2}/simple_lib.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_3}/AutoMocExecutableConfig.cmake (61%) create mode 100644 Tests/RunCMake/Autogen_3/CMakeLists.txt rename Tests/RunCMake/{Autogen => Autogen_3}/MocExample.cmake (85%) create mode 100644 Tests/RunCMake/Autogen_3/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_4}/AutoUicExecutableConfig.cmake (61%) create mode 100644 Tests/RunCMake/Autogen_4/CMakeLists.txt create mode 100644 Tests/RunCMake/Autogen_4/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_4}/UicExample.cmake (100%) rename Tests/RunCMake/{Autogen => Autogen_4}/example_ui.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_4}/example_ui.h (100%) rename Tests/RunCMake/{Autogen => Autogen_4}/uiA.ui (100%) rename Tests/RunCMake/{Autogen => Autogen_5}/AutoRccExecutableConfig.cmake (61%) create mode 100644 Tests/RunCMake/Autogen_5/CMakeLists.txt rename Tests/RunCMake/{Autogen => Autogen_5}/RccExample.cmake (86%) create mode 100644 Tests/RunCMake/Autogen_5/RunCMakeTest.cmake rename Tests/RunCMake/{Autogen => Autogen_5}/data.qrc (100%) create mode 100644 Tests/RunCMake/Autogen_6/CMakeLists.txt create mode 100644 Tests/RunCMake/Autogen_6/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/CMakeLists.txt create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/main.cpp create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.cpp create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.h create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.ui create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.cpp create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.h create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.ui create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.cpp create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.h create mode 100644 Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.ui rename Tests/RunCMake/{Autogen => Autogen_common}/example.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_common}/example.h (100%) rename Tests/RunCMake/{Autogen => Autogen_common}/exe_common.h (100%) rename Tests/RunCMake/{Autogen => Autogen_common}/exe_debug.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_common}/exe_release.cpp (100%) rename Tests/RunCMake/{Autogen => Autogen_common}/exe_relwithdebinfo.cpp (100%) create mode 100644 Tests/RunCMake/Autogen_common/utils.cmake create mode 100644 Tests/RunCMake/BuildDepends/CompileDepends.c create mode 100644 Tests/RunCMake/BuildDepends/CompileDepends.cmake create mode 100644 Tests/RunCMake/BuildDepends/CompileDepends.step1.cmake create mode 100644 Tests/RunCMake/BuildDepends/CompileDepends.step2.cmake delete mode 100644 Tests/RunCMake/BuildDepends/CompilerDependencies.cmake delete mode 100644 Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake delete mode 100644 Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies-BadArgs-result.txt => CustomCommandDepends-BadArgs-result.txt} (100%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies-BadArgs-stderr.txt => CustomCommandDepends-BadArgs-stderr.txt} (63%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies-BadArgs.cmake => CustomCommandDepends-BadArgs.cmake} (100%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies-compiler-deps-legacy.cmake => CustomCommandDepends-compiler-deps-legacy.cmake} (100%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies.cmake => CustomCommandDepends.cmake} (72%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies.step1.cmake => CustomCommandDepends.step1.cmake} (100%) rename Tests/RunCMake/BuildDepends/{CustomCommandDependencies.step2.cmake => CustomCommandDepends.step2.cmake} (100%) delete mode 100644 Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake create mode 100644 Tests/RunCMake/BuildDepends/LinkDependsExe.c create mode 100644 Tests/RunCMake/BuildDepends/LinkDependsLib.c create mode 100644 Tests/RunCMake/BuildDepends/With Space/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0129/C-NEW.cmake create mode 100644 Tests/RunCMake/CMP0129/C-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0129/C-OLD.cmake create mode 100644 Tests/RunCMake/CMP0129/C-WARN.cmake rename Tests/RunCMake/CMP0129/{C.cmake => C-common.cmake} (63%) create mode 100644 Tests/RunCMake/CMP0129/CXX-NEW.cmake create mode 100644 Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0129/CXX-OLD.cmake create mode 100644 Tests/RunCMake/CMP0129/CXX-WARN.cmake rename Tests/RunCMake/CMP0129/{CXX.cmake => CXX-common.cmake} (64%) create mode 100644 Tests/RunCMake/CMP0129/Fortran-NEW.cmake create mode 100644 Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0129/Fortran-OLD.cmake create mode 100644 Tests/RunCMake/CMP0129/Fortran-WARN.cmake create mode 100644 Tests/RunCMake/CMP0129/Fortran-common.cmake delete mode 100644 Tests/RunCMake/CMP0129/Fortran.cmake create mode 100644 Tests/RunCMake/CMP0156/CMP0156-Common-Imported.cmake create mode 100644 Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-result.txt create mode 100644 Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-stdout.txt create mode 100644 Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported.cmake create mode 100644 Tests/RunCMake/CMP0156/CMP0156-NEW-Imported.cmake create mode 100644 Tests/RunCMake/CMP0156/subdir1/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0156/subdir2/CMakeLists.txt rename Tests/RunCMake/{file-DOWNLOAD/TLS_VERSION-bad-result.txt => CMP0171/CMP0171-NEW-result.txt} (100%) create mode 100644 Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt create mode 100644 Tests/RunCMake/CMP0171/CMP0171-NEW.cmake rename Tests/RunCMake/{file/MAKE_DIRECTORY-fail-result.txt => CMP0171/CMP0171-OLD-result.txt} (100%) create mode 100644 Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt create mode 100644 Tests/RunCMake/CMP0171/CMP0171-OLD.cmake create mode 100644 Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt create mode 100644 Tests/RunCMake/CMP0171/CMP0171-WARN.cmake create mode 100644 Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt rename Tests/RunCMake/{CommandLine/DeprecateVS12-WARN-OFF.cmake => CMP0171/CMP0171-codegen.cmake} (100%) create mode 100644 Tests/RunCMake/CMP0171/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0171/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/CMP0173/CMP0173-NEW-result.txt create mode 100644 Tests/RunCMake/CMP0173/CMP0173-NEW-stderr.txt create mode 100644 Tests/RunCMake/CMP0173/CMP0173-NEW.cmake create mode 100644 Tests/RunCMake/CMP0173/CMP0173-OLD-stdout.txt create mode 100644 Tests/RunCMake/CMP0173/CMP0173-OLD.cmake create mode 100644 Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt create mode 100644 Tests/RunCMake/CMP0173/CMP0173-WARN.cmake create mode 100644 Tests/RunCMake/CMP0173/CMP0173-common.cmake create mode 100644 Tests/RunCMake/CMP0173/CMakeLists.txt create mode 100644 Tests/RunCMake/CMP0173/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/CMakePresets/CommentValid-result.txt rename Tests/RunCMake/{CommandLine/DeprecateVS12-WARN-ON.cmake => CMakePresets/CommentValid.cmake} (100%) create mode 100644 Tests/RunCMake/CMakePresets/CommentValid.json.in create mode 100644 Tests/RunCMake/CMakePresets/CommentValidFull-result.txt rename Tests/RunCMake/{VsDotnetSdk/VsDotnetSdkCustomCommandsSource-result.txt => CMakePresets/CommentValidFull.cmake} (100%) create mode 100644 Tests/RunCMake/CMakePresets/CommentValidFull.json.in create mode 100644 Tests/RunCMake/CMakePresets/CommentValidOldSchema-result.txt create mode 100644 Tests/RunCMake/CMakePresets/CommentValidOldSchema-stderr.txt rename Tests/RunCMake/{VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-result.txt => CMakePresets/CommentValidOldSchema.cmake} (100%) create mode 100644 Tests/RunCMake/CMakePresets/CommentValidOldSchema.json.in create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValid-result.txt create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValid.cmake create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValid.json.in create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake create mode 100644 Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in create mode 100644 Tests/RunCMake/CMakePresets/NoCommentValid-result.txt create mode 100644 Tests/RunCMake/CMakePresets/NoCommentValid.cmake create mode 100644 Tests/RunCMake/CMakePresets/NoCommentValid.json.in create mode 100644 Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt create mode 100644 Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake create mode 100644 Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in create mode 100644 Tests/RunCMake/CMakePresetsWorkflow/MultiplePresets.json.in create mode 100644 Tests/RunCMake/CMakePresetsWorkflow/RepeatedPresetArg.cmake create mode 100644 Tests/RunCMake/CMakePresetsWorkflow/SinglePresetArg.cmake create mode 100644 Tests/RunCMake/CPack/tests/MULTIARCH/DEB-fail-stderr.txt create mode 100644 Tests/RunCMake/CPack/tests/MULTIARCH/ExpectedFiles.cmake create mode 100644 Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake create mode 100644 Tests/RunCMake/CPack/tests/MULTIARCH/test.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-NEW-check.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-NEW.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-OLD-check.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-OLD.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-WARN-check.cmake create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt create mode 100644 Tests/RunCMake/CPackConfig/CMP0172-WARN.cmake create mode 100644 Tests/RunCMake/CTestCommandLine/FailureLabels-result.txt create mode 100644 Tests/RunCMake/CTestCommandLine/FailureLabels-stderr.txt create mode 100644 Tests/RunCMake/CTestCommandLine/FailureLabels-stdout.txt create mode 100644 Tests/RunCMake/CTestCommandLine/PrintLabels-stdout.txt create mode 100644 Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-check.cmake create mode 100644 Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt create mode 100644 Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/build-database-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-all-multi.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-all.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-config.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-config.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-multi.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all-multi.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-config.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-config.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-multi.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-target.json create mode 100644 Tests/RunCMake/CXXModules/examples/expect/export-build-database-target.json create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-build-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-setup.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-stderr.txt create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Debug-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Debug-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Release-Release-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/CMakeLists.txt create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/dep_interface_include/anchor create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/importable.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/lib.cxx create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/target_interface_include/anchor create mode 100644 Tests/RunCMake/CXXModules/examples/export-build-database/target_public_include/anchor create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-build-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-stderr.txt create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Debug-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Debug-check.cmake create mode 100644 Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Release-check.cmake create mode 100644 Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-private.json create mode 100644 Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-public.json create mode 100644 Tests/RunCMake/CheckModules/CheckLinkDirectories.cmake create mode 100644 Tests/RunCMake/CheckModules/CheckLinkDirectoriesTestLib.cmake create mode 100644 Tests/RunCMake/CheckModules/TestLib/CMakeLists.txt create mode 100644 Tests/RunCMake/CheckModules/TestLib/TestLib.c create mode 100644 Tests/RunCMake/Codegen/CMakeLists.txt create mode 100644 Tests/RunCMake/Codegen/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/add-custom-command-depends.cmake create mode 100644 Tests/RunCMake/Codegen/add-dependencies-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/add-dependencies.cmake create mode 100644 Tests/RunCMake/Codegen/append-implicit-depends-result.txt create mode 100644 Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt create mode 100644 Tests/RunCMake/Codegen/append-implicit-depends.cmake create mode 100644 Tests/RunCMake/Codegen/byproducts-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/byproducts.cmake create mode 100644 Tests/RunCMake/Codegen/error.c create mode 100644 Tests/RunCMake/Codegen/exclude-from-all.cmake create mode 100644 Tests/RunCMake/Codegen/generated.h.in create mode 100644 Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt create mode 100644 Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt create mode 100644 Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake create mode 100644 Tests/RunCMake/Codegen/implicit-depends-result.txt create mode 100644 Tests/RunCMake/Codegen/implicit-depends-stderr.txt create mode 100644 Tests/RunCMake/Codegen/implicit-depends.cmake create mode 100644 Tests/RunCMake/Codegen/main.c create mode 100644 Tests/RunCMake/Codegen/min-graph-1-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/min-graph-1.cmake create mode 100644 Tests/RunCMake/Codegen/min-graph-2-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/min-graph-2.cmake create mode 100644 Tests/RunCMake/Codegen/min-graph-3-build-check.cmake create mode 100644 Tests/RunCMake/Codegen/min-graph-3.cmake create mode 100644 Tests/RunCMake/Codegen/no-codegen-check.cmake create mode 100644 Tests/RunCMake/Codegen/no-codegen.cmake create mode 100644 Tests/RunCMake/Codegen/no-output-result.txt create mode 100644 Tests/RunCMake/Codegen/no-output-stderr.txt create mode 100644 Tests/RunCMake/Codegen/no-output.cmake delete mode 100644 Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt create mode 100644 Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/list-cache.cmake create mode 100644 Tests/RunCMake/CommandLine/print-config-dir-apple-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/print-config-dir-env-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/print-config-dir-stdout.txt create mode 100644 Tests/RunCMake/CommandLine/print-config-dir-win-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/C-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/C.cmake create mode 100644 Tests/RunCMake/CompilerId/CMakeLists.txt create mode 100644 Tests/RunCMake/CompilerId/CSharp-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/CSharp.cmake create mode 100644 Tests/RunCMake/CompilerId/CUDA-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/CUDA.cmake create mode 100644 Tests/RunCMake/CompilerId/CXX-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/CXX.cmake create mode 100644 Tests/RunCMake/CompilerId/Fortran-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/Fortran.cmake create mode 100644 Tests/RunCMake/CompilerId/HIP-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/HIP.cmake create mode 100644 Tests/RunCMake/CompilerId/ISPC-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/ISPC.cmake create mode 100644 Tests/RunCMake/CompilerId/OBJC-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/OBJC.cmake create mode 100644 Tests/RunCMake/CompilerId/OBJCXX-stdout.txt create mode 100644 Tests/RunCMake/CompilerId/OBJCXX.cmake create mode 100644 Tests/RunCMake/CompilerId/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/ConfigDir/CMakeLists.txt create mode 100644 Tests/RunCMake/ConfigDir/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/ConfigDir/check-reply.cmake create mode 100644 Tests/RunCMake/ConfigDir/config.cmake create mode 100644 Tests/RunCMake/ConfigDir/config/api/v1/query/codemodel-v2 create mode 100644 Tests/RunCMake/File_Archive/working-directory.cmake create mode 100644 Tests/RunCMake/Framework/FrameworkLayout-check-common.cmake create mode 100644 Tests/RunCMake/GoogleTest/GoogleTest-discovery-check-test-list-extra-args.cmake create mode 100644 Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListExtraArgs.cmake delete mode 100644 Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-test-stdout.txt create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW.cmake create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-test-stdout.txt create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD.cmake create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-test-stdout.txt create mode 100644 Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN.cmake rename Tests/RunCMake/GoogleTest/{GoogleTestLauncher.cmake => Launcher.cmake} (73%) create mode 100644 Tests/RunCMake/GoogleTest/test_list_extra_args.cpp create mode 100644 Tests/RunCMake/InstallParallel/check-manifest.cmake create mode 100644 Tests/RunCMake/InstallParallel/check-num-installs.cmake delete mode 100644 Tests/RunCMake/InstallParallel/check-parallel.cmake delete mode 100644 Tests/RunCMake/InstallParallel/check-single.cmake create mode 100644 Tests/RunCMake/InstallParallel/ninja-no-parallel-install-stdout.txt create mode 100644 Tests/RunCMake/InstallParallel/ninja-parallel-install-stdout.txt delete mode 100644 Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt create mode 100644 Tests/RunCMake/InstallParallel/no-parallel-install-stdout.txt create mode 100644 Tests/RunCMake/InstallParallel/out-of-date-json-install-stdout.txt delete mode 100644 Tests/RunCMake/InstallParallel/read-ninja-install.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup-reverse.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup-reverse.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup-reverse.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup-reverse.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Basic.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicA.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicB.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicC.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/BasicX.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake create mode 100644 Tests/RunCMake/LinkLibrariesStrategy/main.c create mode 100644 Tests/RunCMake/PackageInfo/Appendix-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/Appendix.cmake create mode 100644 Tests/RunCMake/PackageInfo/Assertions.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadArgs1-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs1.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadArgs2-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs2.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadArgs3-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs3.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadArgs4-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs4.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadArgs5-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadArgs5.cmake create mode 100644 Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt create mode 100644 Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake create mode 100644 Tests/RunCMake/PackageInfo/CMakeLists.txt create mode 100644 Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt create mode 100644 Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ExperimentalGate.cmake create mode 100644 Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake create mode 100644 Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/InterfaceProperties.cmake create mode 100644 Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/LowerCaseFile.cmake create mode 100644 Tests/RunCMake/PackageInfo/Metadata-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/Metadata.cmake create mode 100644 Tests/RunCMake/PackageInfo/Minimal-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/Minimal.cmake create mode 100644 Tests/RunCMake/PackageInfo/MinimalVersion-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/MinimalVersion.cmake create mode 100644 Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt create mode 100644 Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake create mode 100644 Tests/RunCMake/PackageInfo/Requirements-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/Requirements.cmake create mode 100644 Tests/RunCMake/PackageInfo/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/PackageInfo/TargetTypes-check.cmake create mode 100644 Tests/RunCMake/PackageInfo/TargetTypes.cmake create mode 100644 Tests/RunCMake/PackageInfo/broken-config.cmake rename Tests/RunCMake/{VS10ProjectWinCE/foo.cpp => PackageInfo/foo.cxx} (100%) create mode 100644 Tests/RunCMake/PackageInfo/test-config.cmake rename Tests/RunCMake/{Autogen/exe.cpp => PackageInfo/test.cxx} (100%) create mode 100644 Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-13.3.0-static-libquadmath.input create mode 100644 Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-clang.input create mode 100644 Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-gcc.input create mode 100644 Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-clang.input create mode 100644 Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-gcc.input create mode 100644 Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output create mode 100644 Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output create mode 100644 Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-clang.output create mode 100644 Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-gcc.output create mode 100644 Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-clang.output create mode 100644 Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-gcc.output create mode 100644 Tests/RunCMake/StandardLinkDirectories/CMakeLists.txt create mode 100644 Tests/RunCMake/StandardLinkDirectories/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestApp.cmake create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestApp/CMakeLists.txt create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestApp/TestApp.c create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestAppBad-result.txt create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestLib.cmake create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestLib/CMakeLists.txt create mode 100644 Tests/RunCMake/StandardLinkDirectories/TestLib/TestLib.c create mode 100644 Tests/RunCMake/UnityBuild/f.cu create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cuda-check.cmake create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cuda.cmake create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cuda_group-check.cmake create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cuda_group.cmake create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda-check.cmake create mode 100644 Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda.cmake create mode 100644 Tests/RunCMake/VS10Project/VsFrameworkReference-check.cmake create mode 100644 Tests/RunCMake/VS10Project/VsFrameworkReference.cmake create mode 100644 Tests/RunCMake/VS10Project/VsToolOverride-check.cmake create mode 100644 Tests/RunCMake/VS10Project/VsToolOverride.cmake create mode 100644 Tests/RunCMake/VS10Project/bar.txt create mode 100644 Tests/RunCMake/VS10Project/foo.txt delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/RunCMakeTest.cmake delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy.cmake delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject-check.cmake delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject.cmake delete mode 100644 Tests/RunCMake/VS10ProjectWinCE/foo.cs create mode 100644 Tests/RunCMake/VisibilityPreset/CMP0063-WARN-no-stderr.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName-check.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations-check.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations.cmake create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-build-stdout.txt delete mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-stderr.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-build-stdout.txt delete mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-stderr.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore-build-stdout.txt create mode 100644 Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-framework-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-library-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-target-framework-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-target-library-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-framework-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/create-library-ios-catalyst.cmake create mode 100644 Tests/RunCMake/XcFramework/export-ios-catalyst.cmake create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-NEW-result.txt create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-NEW-stderr.txt create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-NEW.cmake create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-OLD.cmake create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt create mode 100644 Tests/RunCMake/add_custom_command/CMP0175-WARN.cmake create mode 100644 Tests/RunCMake/add_custom_command/CMP0175.cmake rename Tests/RunCMake/cmake_minimum_required/{Before3_5-stderr.txt => Before3_10-stderr.txt} (61%) rename Tests/RunCMake/cmake_minimum_required/{Before3_5.cmake => Before3_10.cmake} (67%) create mode 100644 Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/CMakeLists.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/a.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/one.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/onedot.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/pseudo-empty.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/tilde.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/zeroone.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/all-extract-fields.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/bar.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/baz.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-bothcase-f.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-lowercase-f.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-uppercase-f.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/empty-key.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/foo.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/invalid.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/no-description.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/no-name.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/no-version.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/qux-uninstalled.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/qux.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/PackageRoot/relocate.pc create mode 100644 Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestEnv-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestEnv.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestExtract-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestExtract.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestMangle-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestMangle.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestQuiet.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestRequired-result.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestRequired.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestReroot-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestReroot.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestStrictness-BEST_EFFORT-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestStrictness-PERMISSIVE-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestStrictness-STRICT-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestStrictness.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestUninstalled-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestUninstalled.cmake create mode 100644 Tests/RunCMake/cmake_pkg_config/TestVersion-stderr.txt create mode 100644 Tests/RunCMake/cmake_pkg_config/TestVersion.cmake rename Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/{thirdparty.c => third_party.c} (100%) create mode 100644 Tests/RunCMake/execute_process/Encoding-common.cmake create mode 100644 Tests/RunCMake/execute_process/Encoding-windows.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingAUTO-stderr.txt create mode 100644 Tests/RunCMake/execute_process/EncodingAUTO.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingCMP0176-NEW-stderr.txt create mode 100644 Tests/RunCMake/execute_process/EncodingCMP0176-NEW.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingCMP0176-OLD-stderr.txt create mode 100644 Tests/RunCMake/execute_process/EncodingCMP0176-OLD.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingUTF-8.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingUTF8.cmake create mode 100644 Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt create mode 100644 Tests/RunCMake/execute_process/EncodingUnknown.cmake create mode 100644 Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-darwin.txt create mode 100644 Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-windows.txt create mode 100644 Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout.txt create mode 100644 Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-result.txt rename Tests/RunCMake/file-DOWNLOAD/{TLS_VERSION-bad-stderr.txt => TLS_VERSION-invalid-stderr.txt} (68%) create mode 100644 Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid.cmake create mode 100644 Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies-all-stdout.txt create mode 100644 Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt rename Tests/RunCMake/{file/MAKE_DIRECTORY-fail.cmake => file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake} (100%) create mode 100644 Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake create mode 100755 Tests/RunCMake/file-RPATH/ELF/elf64lsb-static.bin delete mode 100644 Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-existing.cmake create mode 100644 Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-non-existing.cmake create mode 100644 Tests/RunCMake/find_package/DebugRoot-stderr.txt create mode 100644 Tests/RunCMake/find_package/DebugRoot.cmake create mode 100644 Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig.cmake create mode 100644 Tests/RunCMake/find_package/DebugRoot/NoExist/DebugRoot/.empty create mode 100644 Tests/RunCMake/install/CMP0177-NEW-all-check.cmake create mode 100644 Tests/RunCMake/install/CMP0177-NEW-verify.cmake create mode 100644 Tests/RunCMake/install/CMP0177-NEW.cmake create mode 100644 Tests/RunCMake/install/CMP0177-OLD-verify-result.txt create mode 100644 Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt create mode 100644 Tests/RunCMake/install/CMP0177-OLD-verify.cmake create mode 100644 Tests/RunCMake/install/CMP0177-OLD.cmake create mode 100644 Tests/RunCMake/install/CMP0177-WARN-stderr.txt create mode 100644 Tests/RunCMake/install/CMP0177-WARN-verify-result.txt create mode 100644 Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt create mode 100644 Tests/RunCMake/install/CMP0177-WARN-verify.cmake create mode 100644 Tests/RunCMake/install/CMP0177-WARN.cmake create mode 100644 Tests/RunCMake/install/CMP0177.cmake delete mode 100644 Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt delete mode 100644 Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt create mode 100644 Tests/RunCMake/project/CMP0180-NEW-stdout.txt create mode 100644 Tests/RunCMake/project/CMP0180-NEW.cmake create mode 100644 Tests/RunCMake/project/CMP0180-OLD-stdout.txt create mode 100644 Tests/RunCMake/project/CMP0180-OLD.cmake rename Tests/RunCMake/project/{SameProjectVarsSubdir.cmake => CMP0180.cmake} (100%) delete mode 100644 Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt create mode 100644 Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED-check.cmake create mode 100644 Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED_SHELL-check.cmake create mode 100644 Tests/SharedLibraryArchive/CMakeLists.txt create mode 100644 Tests/SharedLibraryArchive/sla-check.cmake create mode 100644 Tests/SharedLibraryArchive/sla.c create mode 100644 Tests/SharedLibraryArchive/use_sla.c create mode 100644 Tests/VSMARMASM/empty.asm create mode 100644 Tests/VSMASM/empty.asm create mode 100755 Utilities/Scripts/update-llpkgc.bash delete mode 100644 Utilities/cmcurl/CMake/FindCARES.cmake create mode 100644 Utilities/cmcurl/CMake/FindCares.cmake delete mode 100644 Utilities/cmcurl/CMake/FindLibSSH2.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibgsasl.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibidn2.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibpsl.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibssh.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibssh2.cmake create mode 100644 Utilities/cmcurl/CMake/FindLibuv.cmake create mode 100644 Utilities/cmcurl/CMake/FindNettle.cmake rename Utilities/cmcurl/CMake/{FindQUICHE.cmake => FindQuiche.cmake} (66%) rename Utilities/cmcurl/CMake/{FindLibPSL.cmake => FindRustls.cmake} (50%) create mode 100644 Utilities/cmcurl/CMake/FindWolfSSH.cmake create mode 100644 Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake create mode 100644 Utilities/cmllpkgc/CMakeLists.txt create mode 100644 Utilities/cmllpkgc/README.rst create mode 100644 Utilities/cmllpkgc/llpkgc.c create mode 100644 Utilities/cmllpkgc/llpkgc.h create mode 100644 Utilities/cmllpkgc/llpkgc__internal.c create mode 100644 Utilities/cmllpkgc/llpkgc__internal.h diff --git a/.clang-tidy b/.clang-tidy index 6d2edd4f7..c85fd67db 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,6 +2,7 @@ Checks: "-*,\ bugprone-*,\ -bugprone-assignment-in-if-condition,\ +-bugprone-crtp-constructor-accessibility,\ -bugprone-easily-swappable-parameters,\ -bugprone-empty-catch,\ -bugprone-implicit-widening-of-multiplication-result,\ @@ -10,19 +11,23 @@ bugprone-*,\ -bugprone-misplaced-widening-cast,\ -bugprone-multi-level-implicit-pointer-conversion,\ -bugprone-narrowing-conversions,\ +-bugprone-return-const-ref-from-parameter,\ +-bugprone-suspicious-stringview-data-usage,\ -bugprone-switch-missing-default-case,\ -bugprone-too-small-loop-variable,\ -bugprone-unchecked-optional-access,\ -bugprone-unused-local-non-trivial-variable,\ -bugprone-unused-return-value,\ +-bugprone-use-after-move,\ misc-*,\ -misc-confusable-identifiers,\ -misc-const-correctness,\ -misc-include-cleaner,\ --misc-no-recursion,\ -misc-non-private-member-variables-in-classes,\ +-misc-no-recursion,\ -misc-static-assert,\ -misc-use-anonymous-namespace,\ +-misc-use-internal-linkage,\ modernize-*,\ -modernize-avoid-c-arrays,\ -modernize-macro-to-enum,\ @@ -36,13 +41,14 @@ modernize-*,\ performance-*,\ -performance-avoid-endl,\ -performance-enum-size,\ --performance-inefficient-vector-operation,\ --performance-noexcept-swap,\ +-performance-unnecessary-copy-initialization,\ +-performance-unnecessary-value-param,\ readability-*,\ -readability-avoid-nested-conditional-operator,\ -readability-avoid-return-with-void-value,\ -readability-avoid-unconditional-preprocessor-if,\ -readability-convert-member-functions-to-static,\ +-readability-enum-initial-value,\ -readability-function-cognitive-complexity,\ -readability-function-size,\ -readability-identifier-length,\ @@ -51,6 +57,7 @@ readability-*,\ -readability-inconsistent-declaration-parameter-name,\ -readability-magic-numbers,\ -readability-make-member-function-const,\ +-readability-math-missing-parentheses,\ -readability-named-parameter,\ -readability-redundant-casting,\ -readability-redundant-declaration,\ @@ -61,6 +68,7 @@ readability-*,\ -readability-static-accessed-through-instance,\ -readability-suspicious-call-argument,\ -readability-uppercase-literal-suffix,\ +-readability-use-std-min-max,\ cmake-*,\ -cmake-ostringstream-use-cmstrcat,\ -cmake-string-concatenation-use-cmstrcat,\ diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake index 803e09e4b..3c30d3442 100644 --- a/Auxiliary/bash-completion/cmake +++ b/Auxiliary/bash-completion/cmake @@ -173,7 +173,9 @@ _cmake() printf -v quoted %q "$cur" if [[ ! "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then - COMPREPLY=( $( compgen -W "configure${IFS}build${IFS}test${IFS}all" -- "$quoted" ) ) + COMPREPLY=( + $( compgen -W "configure${IFS}build${IFS}package${IFS}test${IFS}workflow${IFS}all" -- "$quoted" ) + ) fi return ;; @@ -182,12 +184,16 @@ _cmake() local quoted printf -v quoted %q "$cur" - local build_or_configure="configure" - if [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then - build_or_configure="build" + local preset_type + if [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--workflow${IFS}" ]]; then + preset_type="workflow" + elif [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then + preset_type="build" + else + preset_type="configure" fi - local presets=$( cmake --list-presets="$build_or_configure" 2>/dev/null | + local presets=$( cmake --list-presets="$preset_type" 2>/dev/null | grep -o "^ \".*\"" | sed \ -e "s/^ //g" \ -e "s/\"//g" \ @@ -195,6 +201,24 @@ _cmake() COMPREPLY=( $( compgen -W "$presets" -- "$quoted" ) ) return ;; + --workflow) + local quoted + printf -v quoted %q "$cur" + # Options allowed right after `--workflow` + local workflow_options='--preset --list-presets --fresh' + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W "$workflow_options" -- "$quoted" ) ) + else + local presets=$( cmake --list-presets=workflow 2>/dev/null | + grep -o "^ \".*\"" | sed \ + -e "s/^ //g" \ + -e "s/\"//g" \ + -e 's/ /\\\\ /g' ) + COMPREPLY=( $( compgen -W "$presets $workflow_options" -- "$quoted" ) ) + fi + return + ;; esac if ($is_old_completion || $is_init_completion); then @@ -204,6 +228,8 @@ _cmake() fi if [[ "$cur" == -* ]]; then + # FIXME(#26100): `cmake --help` is missing some options and does not + # have any mode-specific options like `cmake --build`'s `--config`. COMPREPLY=( $(compgen -W '$( _parse_help "$1" --help )' -- ${cur}) ) [[ $COMPREPLY == *= ]] && compopt -o nospace [[ $COMPREPLY ]] && return diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el index 893c0d4d0..35af733a9 100644 --- a/Auxiliary/cmake-mode.el +++ b/Auxiliary/cmake-mode.el @@ -183,7 +183,7 @@ set the path with these commands: ) (defun cmake-point-in-indendation () - (string-match "^[ \\t]*$" (buffer-substring (point-at-bol) (point)))) + (string-match "^[ \\t]*$" (buffer-substring (line-beginning-position) (point)))) (defun cmake-indent-line-to (column) "Indent the current line to COLUMN. diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim index b504e4edd..d09bbfc11 100644 --- a/Auxiliary/vim/syntax/cmake.vim +++ b/Auxiliary/vim/syntax/cmake.vim @@ -416,6 +416,7 @@ syn keyword cmakeProperty contained \ VS_DOTNET_STARTUP_OBJECT \ VS_DOTNET_TARGET_FRAMEWORK_VERSION \ VS_DPI_AWARE + \ VS_FRAMEWORK_REFERENCES \ VS_GLOBAL_KEYWORD \ VS_GLOBAL_PROJECT_TYPES \ VS_GLOBAL_ROOTNAMESPACE diff --git a/CMakeLists.txt b/CMakeLists.txt index 081bd7d1d..79a562cbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.29 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) @@ -267,9 +267,7 @@ macro(CMAKE_SETUP_TESTING) endif() # configure some files for testing - 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) endmacro() diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 81f26f59f..88f39f18e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -26,7 +26,8 @@ To contribute patches: #. Fork the upstream `CMake Repository`_ into a personal account. #. Run `Utilities/SetupForDevelopment.sh`_ for local git configuration. #. See `Building CMake`_ for building CMake locally. -#. See the `CMake Source Code Guide`_ for coding guidelines. +#. See the `CMake Source Code Guide`_ for coding guidelines + and the `CMake Testing Guide`_ for testing instructions. #. Create a topic branch named suitably for your work. Base all new work on the upstream ``master`` branch. Base work on the upstream ``release`` branch only if it fixes a @@ -49,6 +50,7 @@ The merge request will enter the `CMake Review Process`_ for consideration. .. _`Utilities/SetupForDevelopment.sh`: Utilities/SetupForDevelopment.sh .. _`Building CMake`: README.rst#building-cmake .. _`CMake Source Code Guide`: Help/dev/source.rst +.. _`CMake Testing Guide`: Help/dev/testing.rst .. _`commit messages`: Help/dev/review.rst#commit-messages .. _`CMake Review Process`: Help/dev/review.rst @@ -61,10 +63,10 @@ drive testing and submit results to the `CMake CDash Page`_. Anyone is welcome to provide testing machines in order to help keep support for their platforms working. -See documentation on `CMake Testing Process`_ for more information. +See documentation on `CMake Integration Testing`_ for more information. .. _`CMake CDash Page`: https://open.cdash.org/index.php?project=CMake -.. _`CMake Testing Process`: Help/dev/testing.rst +.. _`CMake Integration Testing`: Help/dev/integration-testing.rst License ======= diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in index ae5571542..d92448011 100644 --- a/CTestCustom.cmake.in +++ b/CTestCustom.cmake.in @@ -86,7 +86,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "[0-9]+ Warning\\(s\\) detected" # SunPro # Ignore false positive on `cm::optional` usage from GCC - "cmFileCommand.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& tls_verify \\+2\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]" + "cmFileCommand.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& tlsVerifyOpt \\+2\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '.*cm::optional::_mem\\)\\)' may be used uninitialized \\[-Wmaybe-uninitialized\\]" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: note: '.*cm::optional::_mem\\)\\)' was declared here" "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& modmap_fmt \\+4\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]" @@ -101,6 +101,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "libuv/src/.*:[0-9]+:[0-9]+: warning: 1st function call argument is an uninitialized value" "libuv/src/.*:[0-9]+:[0-9]+: warning: Dereference of null pointer" "libuv/src/.*:[0-9]+:[0-9]+: warning: The left operand of '[^']+' is a garbage value" + "libuv/src/.*:[0-9]+:[0-9]+: warning: Value of '[^']+' was not checked and may be overwritten by function '[^']+'" "nghttp2/lib/.*:[0-9]+:[0-9]+: warning: Access to field '[^']+' results in a dereference of a null pointer" "nghttp2/lib/.*:[0-9]+:[0-9]+: warning: Dereference of null pointer" "nghttp2/lib/.*:[0-9]+:[0-9]+: warning: Value stored to '[^']+' is never read" diff --git a/CompileFlags.cmake b/CompileFlags.cmake index f94e079ad..3080daae8 100644 --- a/CompileFlags.cmake +++ b/CompileFlags.cmake @@ -37,7 +37,13 @@ elseif(_CLANG_MSVC_WINDOWS AND "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUA string(APPEND CMAKE_EXE_LINKER_FLAGS " -Xlinker -stack:20000000") endif() -#silence duplicate symbol warnings on AIX +# Silence "Additional optimization may be attained by recompiling and +# specifying MAXMEM option" warning on XLC (AIX) +if(CMAKE_CXX_COMPILER_ID MATCHES "^(XL|XLClang)$") + string(APPEND CMAKE_CXX_FLAGS " -qmaxmem=-1") +endif() + +# Silence duplicate symbol warnings on AIX if(CMAKE_SYSTEM_NAME MATCHES "AIX") if(NOT CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -bhalt:5 ") diff --git a/Help/command/DEPRECATED_POLICY_VERSIONS.txt b/Help/command/DEPRECATED_POLICY_VERSIONS.txt index 7c6826004..f5104d336 100644 --- a/Help/command/DEPRECATED_POLICY_VERSIONS.txt +++ b/Help/command/DEPRECATED_POLICY_VERSIONS.txt @@ -1,3 +1,11 @@ +.. versionchanged:: 3.31 + + Compatibility with versions of CMake older than 3.10 is deprecated. + Calls to :command:`cmake_minimum_required(VERSION)` or + :command:`cmake_policy(VERSION)` that do not specify at least + 3.10 as their policy version (optionally via ``...``) + will produce a deprecation warning in CMake 3.31 and above. + .. versionchanged:: 3.27 Compatibility with versions of CMake older than 3.5 is deprecated. diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 77357c0ee..122bb4eda 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -5,6 +5,8 @@ Add a custom build rule to the generated build system. There are two main signatures for ``add_custom_command``. +.. _`add_custom_command(OUTPUT)`: + Generating Files ^^^^^^^^^^^^^^^^ @@ -26,6 +28,7 @@ The first signature is for adding a custom command to produce an output: [JOB_POOL job_pool] [JOB_SERVER_AWARE ] [VERBATIM] [APPEND] [USES_TERMINAL] + [CODEGEN] [COMMAND_EXPAND_LISTS] [DEPENDS_EXPLICIT_ONLY]) @@ -53,7 +56,7 @@ The options are: the appended commands and dependencies apply to all configurations. The ``COMMENT``, ``MAIN_DEPENDENCY``, and ``WORKING_DIRECTORY`` - options are currently ignored when APPEND is given, but may be + options are currently ignored when ``APPEND`` is given, but may be used in the future. ``BYPRODUCTS`` @@ -81,6 +84,10 @@ The options are: The :ref:`Makefile Generators` will remove ``BYPRODUCTS`` and other :prop_sf:`GENERATED` files during ``make clean``. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + All byproducts must be set in the first call to + ``add_custom_command(OUTPUT...)`` for the output files. + .. versionadded:: 3.20 Arguments to ``BYPRODUCTS`` may use a restricted set of :manual:`generator expressions `. @@ -94,11 +101,15 @@ The options are: ``COMMAND`` Specify the command-line(s) to execute at build time. - If more than one ``COMMAND`` is specified they will be executed in order, + At least one ``COMMAND`` would normally be given, but certain patterns + may omit it, such as adding commands in separate calls using `APPEND`. + + If more than one ``COMMAND`` is specified, they will be executed in order, but *not* necessarily composed into a stateful shell or batch script. - (To run a full script, use the :command:`configure_file` command or the + To run a full script, use the :command:`configure_file` command or the :command:`file(GENERATE)` command to create it, and then specify - a ``COMMAND`` to launch it.) + a ``COMMAND`` to launch it. + The optional ``ARGS`` argument is for backward compatibility and will be ignored. @@ -143,7 +154,8 @@ The options are: ``COMMENT`` Display the given message before the commands are executed at - build time. + build time. This will be ignored if ``APPEND`` is given, although a future + version may use it. .. versionadded:: 3.26 Arguments to ``COMMENT`` may use @@ -203,6 +215,26 @@ The options are: ``${CC} "-I$,;-I>" foo.cc`` to be properly expanded. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + If the appended commands need this option to be set, it must be set on the + first call to ``add_custom_command(OUTPUT...)`` for the output files. + +``CODEGEN`` + .. versionadded:: 3.31 + + Adds the custom command to a global ``codegen`` target that can be + used to execute the custom command while avoiding the majority of the + build graph. + + This option is supported only by :ref:`Ninja Generators` and + :ref:`Makefile Generators`, and is ignored by other generators. + Furthermore, this option is allowed only if policy :policy:`CMP0171` + is set to ``NEW``. + + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + It can only be set on the first call to ``add_custom_command(OUTPUT...)`` + for the output files. + ``IMPLICIT_DEPENDS`` Request scanning of implicit dependencies of an input file. The language given specifies the programming language whose @@ -227,6 +259,10 @@ The options are: Using a pool that is not defined by :prop_gbl:`JOB_POOLS` causes an error by ninja at build time. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + Job pools can only be specified in the first call to + ``add_custom_command(OUTPUT...)`` for the output files. + ``JOB_SERVER_AWARE`` .. versionadded:: 3.28 @@ -238,6 +274,10 @@ The options are: This option is silently ignored by other generators. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + Job server awareness can only be specified in the first call to + ``add_custom_command(OUTPUT...)`` for the output files. + .. _`GNU Make Documentation`: https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html ``MAIN_DEPENDENCY`` @@ -249,6 +289,9 @@ The options are: library or an executable) counts as an implicit main dependency which gets silently overwritten by a custom command specification. + This option is currently ignored if ``APPEND`` is given, but a future + version may use it. + ``OUTPUT`` Specify the output files the command is expected to produce. Each output file will be marked with the :prop_sf:`GENERATED` @@ -293,6 +336,10 @@ The options are: With the :generator:`Ninja` generator, this places the command in the ``console`` :prop_gbl:`pool `. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + If the appended commands need access to the terminal, it must be set on + the first call to ``add_custom_command(OUTPUT...)`` for the output files. + ``VERBATIM`` All arguments to the commands will be escaped properly for the build tool so that the invoked command receives each argument @@ -303,11 +350,18 @@ The options are: is platform specific because there is no protection of tool-specific special characters. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + If the appended commands need to be treated as ``VERBATIM``, it must be set + on the first call to ``add_custom_command(OUTPUT...)`` for the output files. + ``WORKING_DIRECTORY`` Execute the command with the given current working directory. - If it is a relative path it will be interpreted relative to the + If it is a relative path, it will be interpreted relative to the build tree directory corresponding to the current source directory. + This option is currently ignored if ``APPEND`` is given, but a future + version may use it. + .. versionadded:: 3.13 Arguments to ``WORKING_DIRECTORY`` may use :manual:`generator expressions `. @@ -393,6 +447,10 @@ The options are: :ref:`Makefile Generators`, :ref:`Visual Studio Generators`, and the :generator:`Xcode` generator. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + Depfiles can only be set on the first call to + ``add_custom_command(OUTPUT...)`` for the output files. + ``DEPENDS_EXPLICIT_ONLY`` .. versionadded:: 3.27 @@ -408,6 +466,10 @@ The options are: This option can be enabled on all custom commands by setting :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` to ``ON``. + This keyword cannot be used with ``APPEND`` (see policy :policy:`CMP0175`). + It can only be set on the first call to ``add_custom_command(OUTPUT...)`` + for the output files. + Only the :ref:`Ninja Generators` actually use this information to remove unnecessary implicit dependencies. @@ -454,6 +516,25 @@ will re-run whenever ``in.txt`` changes. where ```` is the build configuration, and then compile the generated source as part of a library. +.. versionadded:: 3.31 + Use the ``CODEGEN`` option to add a custom command's outputs to the builtin + ``codegen`` target. This is useful to make generated code available for + static analysis without building the entire project. For example: + + .. code-block:: cmake + + add_executable(someTool someTool.c) + + add_custom_command( + OUTPUT out.c + COMMAND someTool -o out.c + CODEGEN) + + add_library(myLib out.c) + + A user may build the ``codegen`` target to generate ``out.c``. + ``someTool`` is built as dependency, but ``myLib`` is not built at all. + Example: Generating Files for Multiple Targets """""""""""""""""""""""""""""""""""""""""""""" @@ -543,15 +624,17 @@ of the following is specified: Run after all other rules within the target have been executed. Projects should always specify one of the above three keywords when using -the ``TARGET`` form. For backward compatibility reasons, ``POST_BUILD`` is -assumed if no such keyword is given, but projects should explicitly provide -one of the keywords to make clear the behavior they expect. +the ``TARGET`` form. See policy :policy:`CMP0175`. + +All other keywords shown in the signature above have the same meaning as they +do for the :command:`add_custom_command(OUTPUT)` form of the command. +At least one ``COMMAND`` must be given, see policy :policy:`CMP0175`. .. note:: 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 12 2013 (and newer)** generators these command + For :ref:`Visual Studio Generators` these command lines or custom commands will be omitted for the specific configuration and no "empty-string-command" will be added. diff --git a/Help/command/cmake_parse_arguments.rst b/Help/command/cmake_parse_arguments.rst index 0bb1d91bb..441cbfa47 100644 --- a/Help/command/cmake_parse_arguments.rst +++ b/Help/command/cmake_parse_arguments.rst @@ -20,48 +20,49 @@ It processes the arguments given to that macro or function, and defines a set of variables which hold the values of the respective options. -The first signature reads processes arguments passed in the ``...``. +The first signature reads arguments passed in the ``...``. This may be used in either a :command:`macro` or a :command:`function`. .. versionadded:: 3.7 The ``PARSE_ARGV`` signature is only for use in a :command:`function` - body. In this case the arguments that are parsed come from the + body. In this case, the arguments that are parsed come from the ``ARGV#`` variables of the calling function. The parsing starts with the ````-th argument, where ```` is an unsigned integer. This allows for the values to have special characters like ``;`` in them. -The ```` argument contains all options for the respective macro, -i.e. keywords which can be used when calling the macro without any value -following, like e.g. the ``OPTIONAL`` keyword of the :command:`install` -command. +The ```` argument contains all options for the respective function +or macro. These are keywords that have no value following them, like the +``OPTIONAL`` keyword of the :command:`install` command. -The ```` argument contains all keywords for this macro -which are followed by one value, like e.g. ``DESTINATION`` keyword of the -:command:`install` command. +The ```` argument contains all keywords for this function +or macro which are followed by one value, like the ``DESTINATION`` keyword of +the :command:`install` command. The ```` argument contains all keywords for this -macro which can be followed by more than one value, like e.g. the +function or macro which can be followed by more than one value, like the ``TARGETS`` or ``FILES`` keywords of the :command:`install` command. .. versionchanged:: 3.5 - All keywords shall be unique. I.e. every keyword shall only be specified - once in either ````, ```` or + All keywords must be unique. Each keyword can only be specified + once in any of the ````, ````, or ````. A warning will be emitted if uniqueness is violated. When done, ``cmake_parse_arguments`` will consider for each of the -keywords listed in ````, ```` and -```` a variable composed of the given ```` -followed by ``"_"`` and the name of the respective keyword. These -variables will then hold the respective value from the argument list -or be undefined if the associated option could not be found. -For the ```` keywords, these will always be defined, -to ``TRUE`` or ``FALSE``, whether the option is in the argument list or not. +keywords listed in ````, ````, and +````, a variable composed of the given ```` +followed by ``"_"`` and the name of the respective keyword. For +```` and ````, these variables +will then hold the respective value(s) from the argument list, or be undefined +if the associated keyword was not given (policy :policy:`CMP0174` can also +affect the behavior for ````). For the ```` +keywords, these variables will always be defined, and they will be set to +``TRUE`` if the keyword is present, or ``FALSE`` if it is not. All remaining arguments are collected in a variable ``_UNPARSED_ARGUMENTS`` that will be undefined if all arguments were recognized. This can be checked afterwards to see -whether your macro was called with unrecognized parameters. +whether your macro or function was called with unrecognized parameters. .. versionadded:: 3.15 ```` and ```` that were given no @@ -70,8 +71,47 @@ whether your macro was called with unrecognized parameters. received values. This can be checked to see if there were keywords without any values given. -Consider the following example macro, ``my_install()``, which takes similar -arguments to the real :command:`install` command: +.. versionchanged:: 3.31 + If a ```` is followed by an empty string as its value, + policy :policy:`CMP0174` controls whether a corresponding + ``_`` variable is defined or not. + +Choose a ```` carefully to avoid clashing with existing variable names. +When used inside a function, it is usually suitable to use the prefix ``arg``. +There is a very strong convention that all keywords are fully uppercase, so +this prefix results in variables of the form ``arg_SOME_KEYWORD``. This makes +the code more readable, and it minimizes the chance of clashing with cache +variables, which also have a strong convention of being all uppercase. + +.. code-block:: cmake + + function(my_install) + set(options OPTIONAL FAST) + set(oneValueArgs DESTINATION RENAME) + set(multiValueArgs TARGETS CONFIGURATIONS) + cmake_parse_arguments(PARSE_ARGV 0 arg + "${options}" "${oneValueArgs}" "${multiValueArgs}" + ) + + # The above will set or unset variables with the following names: + # arg_OPTIONAL + # arg_FAST + # arg_DESTINATION + # arg_RENAME + # arg_TARGETS + # arg_CONFIGURATIONS + # + # The following will also be set or unset: + # arg_UNPARSED_ARGUMENTS + # arg_KEYWORDS_MISSING_VALUES + +When used inside a macro, ``arg`` might not be a suitable prefix because the +code will affect the calling scope. If another macro also called in the same +scope were to use ``arg`` in its own call to ``cmake_parse_arguments()``, +and if there are any common keywords between the two macros, the later call's +variables can overwrite or remove those of the earlier macro's call. +Therefore, it is advisable to incorporate something unique from the macro name +in the ````, such as ``arg_lowercase_macro_name``. .. code-block:: cmake @@ -79,40 +119,63 @@ arguments to the real :command:`install` command: set(options OPTIONAL FAST) set(oneValueArgs DESTINATION RENAME) set(multiValueArgs TARGETS CONFIGURATIONS) - cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" - "${multiValueArgs}" ${ARGN} ) + cmake_parse_arguments(arg_my_install + "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN} + ) + # ... + endmacro() + macro(my_special_install) + # NOTE: Has the same keywords as my_install() + set(options OPTIONAL FAST) + set(oneValueArgs DESTINATION RENAME) + set(multiValueArgs TARGETS CONFIGURATIONS) + cmake_parse_arguments(arg_my_special_install + "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN} + ) # ... + endmacro() -Assume ``my_install()`` has been called like this: +Suppose the above macros are called one after the other, like so: .. code-block:: cmake my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub CONFIGURATIONS) - -After the ``cmake_parse_arguments`` call the macro will have set or undefined -the following variables:: - - MY_INSTALL_OPTIONAL = TRUE - MY_INSTALL_FAST = FALSE # was not used in call to my_install - MY_INSTALL_DESTINATION = "bin" - MY_INSTALL_RENAME # was not used - MY_INSTALL_TARGETS = "foo;bar" - MY_INSTALL_CONFIGURATIONS # was not used - MY_INSTALL_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL" - MY_INSTALL_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS" - # No value for "CONFIGURATIONS" given - -You can then continue and process these variables. - -Keywords terminate lists of values, e.g. if directly after a -``one_value_keyword`` another recognized keyword follows, this is -interpreted as the beginning of the new option. E.g. -``my_install(TARGETS foo DESTINATION OPTIONAL)`` would result in -``MY_INSTALL_DESTINATION`` set to ``"OPTIONAL"``, but as ``OPTIONAL`` -is a keyword itself ``MY_INSTALL_DESTINATION`` will be empty (but added -to ``MY_INSTALL_KEYWORDS_MISSING_VALUES``) and ``MY_INSTALL_OPTIONAL`` will -therefore be set to ``TRUE``. + my_special_install(TARGETS barry DESTINATION sbin RENAME FAST) + +After these two calls, the following describes the variables that will be +set or unset:: + + arg_my_install_OPTIONAL = TRUE + arg_my_install_FAST = FALSE # was not present in call to my_install + arg_my_install_DESTINATION = "bin" + arg_my_install_RENAME # was not present + arg_my_install_TARGETS = "foo;bar" + arg_my_install_CONFIGURATIONS # was not present + arg_my_install_UNPARSED_ARGUMENTS = "blub" # nothing expected after "OPTIONAL" + arg_my_install_KEYWORDS_MISSING_VALUES = "CONFIGURATIONS" # value was missing + + arg_my_special_install_OPTIONAL = FALSE # was not present + arg_my_special_install_FAST = TRUE + arg_my_special_install_DESTINATION = "sbin" + arg_my_special_install_RENAME # value was missing + arg_my_special_install_TARGETS = "barry" + arg_my_special_install_CONFIGURATIONS # was not present + arg_my_special_install_UNPARSED_ARGUMENTS + arg_my_special_install_KEYWORDS_MISSING_VALUES = "RENAME" + +Keywords terminate lists of values. If a keyword is given directly after a +````, that preceding ```` receives no +value and the keyword is added to the ``_KEYWORDS_MISSING_VALUES`` +variable. In the above example, the call to ``my_special_install()`` contains +the ``RENAME`` keyword immediately followed by the ``FAST`` keyword. +In this case, ``FAST`` terminates processing of the ``RENAME`` keyword. +``arg_my_special_install_FAST`` is set to ``TRUE``, +``arg_my_special_install_RENAME`` is unset, and +``arg_my_special_install_KEYWORDS_MISSING_VALUES`` contains the value +``RENAME``. See Also ^^^^^^^^ diff --git a/Help/command/cmake_pkg_config.rst b/Help/command/cmake_pkg_config.rst new file mode 100644 index 000000000..0d7e512de --- /dev/null +++ b/Help/command/cmake_pkg_config.rst @@ -0,0 +1,263 @@ +cmake_pkg_config +---------------- + +.. versionadded:: 3.31 + +.. only:: html + + .. contents:: + +Process pkg-config format package files. + +Synopsis +^^^^^^^^ + +.. parsed-literal:: + + cmake_pkg_config(EXTRACT [] [...]) + +Introduction +^^^^^^^^^^^^ + +This command generates CMake variables and targets from pkg-config format +package files natively, without needing to invoke or even require the presence +of a pkg-config implementation. A ```` is either an absolute path to a +package file, or a package name to be searched for using the typical pkg-config +search patterns. The optional ```` string has the same format and +semantics as a pkg-config style version specifier, with the exception that if +no comparison operator is specified ``=`` is assumed. + +.. _`common options`: + +There are multiple signatures for this command, and some of the options are +common between them. They are: + +``EXACT`` / ``QUIET`` / ``REQUIRED`` + The ``EXACT`` option requests that the version string be matched exactly + (including empty string, if no version is provided), overriding the typical + pkg-config version comparison algorithm. This will ignore any comparison + operator attached to the version string. + + The ``QUIET`` option disables informational messages, including those + indicating that the package cannot be found if it is not ``REQUIRED``. The + ``REQUIRED`` option stops processing with an error message if the package + cannot be found. + +``STRICTNESS `` + Specify how strictly the contents of the package files will be verified during + parsing and resolution. An invalid file, under the provided strictness mode, + will cause the command to fail. Possible modes are: + + * ``STRICT``: Closely mirrors the behavior of the original FDO pkg-config. + Variables and keywords must be unique. Variables must be defined before + they are used. The Name, Description, and Version keywords must be present. + The overall structure of the file must be valid and parsable. + + * ``PERMISSIVE``: Closely mirrors the behavior of the pkgconf implementation. + Duplicate variables are overridden. Duplicate keywords are appended. + Undefined variables resolve to empty strings. The Name, Description, and + Version keywords must be present. The overall structure of the file must be + valid and parsable. + + * ``BEST_EFFORT``: Same behavior as ``PERMISSIVE`` with regards to duplicate + or uninitialized variables and keywords, but will not fail under any + conditions. Package files which require BEST_EFFORT will fail validation + under all other major implementations and should be fixed. + + The default strictness is ``PERMISSIVE``. + +``ENV_MODE`` + Specifies which environment variables will be queried when running a given + command. Possible modes are: + + * ``FDO``: Queries only the original set of ``PKG_CONFIG_*`` environment + variables used by the freedesktop.org ``pkg-config`` implementation. + + * ``PKGCONF``: Queries the more extensive set of environment variables used + by the ``pkgconf`` implementation. + + * ``IGNORE``: Ignores the presence, absence, and value of environment + variables entirely. In all cases an environment variable would be queried + its treated as defined, but with a value of empty string for the purpose + of the operation. This does not modify the current environment. For boolean + environment variables, such as ``PKG_CONFIG_ALLOW_*``, this means they are + evaluated as truthy. + + ``PKG_CONFIG_SYSROOT_PATH`` is a minor exception. When ``ENV_MODE IGNORE`` + is used, no root path prepending will occur by default and ``pc_sysrootdir`` + remains defaulted to ``/``. + + Target-generating subcommands always ignore flag-filtering environment + variables. The default environment mode is ``PKGCONF``. + +``PC_LIBDIR ...`` + Overrides the default search location for package files; also used to derive + the ``pc_path`` package variable. + + When this option is not provided, the default library directory is the first + available of the following values: + + #. ``CMAKE_PKG_CONFIG_PC_LIB_DIRS`` + #. The ``PKG_CONFIG_LIBDIR`` environment variable + #. The output of ``pkg-config --variable pc_path pkg-config`` + #. A platform-dependent default value + +``PC_PATH ...`` + Overrides the supplemental package file directories which will be prepended + to the search path; also used to derive the ``pc_path`` package variable. + + When this option is not provided, the default paths are the first available of + the following values: + + #. ``CMAKE_PKG_CONFIG_PC_PATH`` + #. The ``PKG_CONFIG_PATH`` environment variable + #. Empty list + +``DISABLE_UNINSTALLED `` + Overrides the search behavior for "uninstalled" package files. These are + package files with an "-uninstalled" suffix which describe packages integrated + directly from a build tree. + + Normally such package files have higher priority than "installed" packages. + When ``DISABLE_UNINSTALLED`` is true, searching for "uninstalled" packages + is disabled. + + When this option is not provided, the default search behavior is determined + by the first available of the following values: + + #. ``CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED`` + #. If the ``PKG_CONFIG_DISABLE_UNINSTALLED`` environment variable is defined + the search is disabled, otherwise it is enabled. + +``PC_SYSROOT_DIR `` + Overrides the root path which will be prepended to paths specified by ``-I`` + compile flags and ``-L`` library search locations; also used to derive the + ``pc_sysrootdir`` package variable. + + When this option is not provided, the default root path is provided by the + first available of the following values: + + #. ``CMAKE_PKG_CONFIG_SYSROOT_DIR`` + #. The ``PKG_CONFIG_SYSROOT_DIR`` environment variable + #. If no root path is available, nothing will be prepended to include or + library directory paths and ``pc_sysrootdir`` will be set to ``/`` + +``TOP_BUILD_DIR `` + Overrides the top build directory path used to derived the ``pc_top_builddir`` + package variable. + + When this option is not provided, the default top build directory path is + the first available of the following values: + + #. ``CMAKE_PKG_CONFIG_TOP_BUILD_DIR`` + #. The ``PKG_CONFIG_TOP_BUILD_DIR`` environment variable + #. If no top build directory path is available, the ``pc_top_builddir`` + package variable is not set + +Signatures +^^^^^^^^^^ + +.. signature:: + cmake_pkg_config(EXTRACT [] [...]) + + Extract the contents of the package into variables. + + .. code-block:: cmake + + cmake_pkg_config(EXTRACT [] + [REQUIRED] [EXACT] [QUIET] + [STRICTNESS ] + [ENV_MODE ] + [PC_LIBDIR ...] + [PC_PATH ...] + [DISABLE_UNINSTALLED ] + [PC_SYSROOT_DIR ] + [TOP_BUILD_DIR ] + [SYSTEM_INCLUDE_DIRS ...] + [SYSTEM_LIBRARY_DIRS ...] + [ALLOW_SYSTEM_INCLUDES ] + [ALLOW_SYSTEM_LIBS ]) + +The following variables will be populated from the contents of package file: + +==================================== ====== ======================================================================================== + Variable Type Definition +==================================== ====== ======================================================================================== +``CMAKE_PKG_CONFIG_NAME`` String Value of the ``Name`` keyword +``CMAKE_PKG_CONFIG_DESCRIPTION`` String Value of the ``Description`` keyword +``CMAKE_PKG_CONFIG_VERSION`` String Value of the ``Version`` keyword +``CMAKE_PKG_CONFIG_PROVIDES`` List Value of the ``Provides`` keyword +``CMAKE_PKG_CONFIG_REQUIRES`` List Value of the ``Requires`` keyword +``CMAKE_PKG_CONFIG_CONFLICTS`` List Value of the ``Conflicts`` keyword +``CMAKE_PKG_CONFIG_CFLAGS`` String Value of the ``CFlags`` / ``Cflags`` keyword +``CMAKE_PKG_CONFIG_INCLUDES`` List All ``-I`` prefixed flags from ``CMAKE_PKG_CONFIG_CFLAGS`` +``CMAKE_PKG_CONFIG_COMPILE_OPTIONS`` List All flags not prefixed with ``-I`` from ``CMAKE_PKG_CONFIG_CFLAGS`` +``CMAKE_PKG_CONFIG_LIBS`` String Value of the ``Libs`` keyword +``CMAKE_PKG_CONFIG_LIBDIRS`` List All ``-L`` prefixed flags from ``CMAKE_PKG_CONFIG_LIBS`` +``CMAKE_PKG_CONFIG_LIBNAMES`` List All ``-l`` prefixed flags from ``CMAKE_PKG_CONFIG_LIBS`` +``CMAKE_PKG_CONFIG_LINK_OPTIONS`` List All flags not prefixed with ``-L`` or ``-l`` from ``CMAKE_PKG_CONFIG_LIBS`` +``CMAKE_PKG_CONFIG_*_PRIVATE`` \* ``CFLAGS`` / ``LIBS`` / ``REQUIRES`` and derived, but in their ``.private`` suffix forms +==================================== ====== ======================================================================================== + +``SYSTEM_INCLUDE_DIRS`` + Overrides the "system" directories for the purpose of flag mangling include + directories in ``CMAKE_PKG_CONFIG_CFLAGS`` and derived variables. + + When this option is not provided, the default directories are provided by the + first available of the following values: + + #. ``CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS`` + #. The ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` environment variable + #. The output of ``pkgconf --variable pc_system_includedirs pkg-config`` + #. A platform-dependent default value + + Additionally, when the ``ENV_MODE`` is ``PKGCONF`` the + ``CMAKE_PKG_CONFIG_PKGCONF_INCLUDES`` variable will be concatenated to the + list if available. If it is not available, the following environment variables + will be queried and concatenated: + + * ``CPATH`` + * ``C_INCLUDE_PATH`` + * ``CPLUS_INCLUDE_PATH`` + * ``OBJC_INCLUDE_PATH`` + * ``INCLUDE`` (Windows Only) + +``SYSTEM_LIBRARY_DIRS`` + Overrides the "system" directories for the purpose of flag mangling library + directories in ``CMAKE_PKG_CONFIG_LIBS`` and derived variables. + + When this option is not provided, the default directories are provided by the + first available of the following values: + + #. ``CMAKE_PKG_CONFIG_SYS_LIB_DIRS`` + #. The ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variable + #. The output of ``pkgconf --variable pc_system_libdirs pkg-config`` + #. A platform-dependent default value + + Additionally, when the ``ENV_MODE`` is ``PKGCONF`` the + ``CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS`` variable will be concatenated to the + list if available. If it is not available, the ``LIBRARY_PATH`` environment + variable will be queried and concatenated. + +``ALLOW_SYSTEM_INCLUDES`` + Preserves "system" directories during flag mangling of include directories + in ``CMAKE_PKG_CONFIG_CFLAGS`` and derived variables. + + When this option is not provided, the default value is determined by the first + available of the following values: + + #. ``CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES`` + #. If the ``PKG_CONFIG_ALLOW_SYSTEM_CFLAGS`` environment variable is defined + the flags are preserved, otherwise they are filtered during flag mangling. + + +``ALLOW_SYSTEM_LIBS`` + Preserves "system" directories during flag mangling of library directories + in ``CMAKE_PKG_CONFIG_LIBS`` and derived variables. + + When this option is not provided, the default value is determined by the first + available of the following values: + + #. ``CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS`` + #. If the ``PKG_CONFIG_ALLOW_SYSTEM_LIBS`` environment variable is defined + the flags are preserved, otherwise they are filtered during flag mangling. diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst index 98430c5c4..dba53311b 100644 --- a/Help/command/execute_process.rst +++ b/Help/command/execute_process.rst @@ -140,20 +140,32 @@ Options: ``NONE`` Perform no decoding. This assumes that the process output is encoded in the same way as CMake's internal encoding (UTF-8). - This is the default. + + This was the default in CMake 3.14 and older. + ``AUTO`` Use the current active console's codepage or if that isn't available then use ANSI. + + This was the default in CMake 3.15 through 3.30. + ``ANSI`` Use the ANSI codepage. + ``OEM`` Use the original equipment manufacturer (OEM) code page. - ``UTF8`` or ``UTF-8`` - Use the UTF-8 codepage. + ``UTF-8`` .. versionadded:: 3.11 - Accept ``UTF-8`` spelling for consistency with the - `UTF-8 RFC `_ naming convention. + + Use the UTF-8 codepage. + + This is the default. See policy :policy:`CMP0176`. + + ``UTF8`` + Use the UTF-8 codepage. Use of this name is discouraged in favor + of ``UTF-8`` to match the `UTF-8 RFC `_ + naming convention. ``COMMAND_ERROR_IS_FATAL `` .. versionadded:: 3.19 diff --git a/Help/command/file.rst b/Help/command/file.rst index 34e97e7f5..890bdf474 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -400,10 +400,19 @@ Filesystem ============== ====================================================== .. signature:: - file(MAKE_DIRECTORY ...) + file(MAKE_DIRECTORY ... [RESULT ]) Create the given directories and their parents as needed. + The options are: + + ``RESULT `` + .. versionadded:: 3.31 + + Set ```` variable to ``0`` on success or an error message + otherwise. If ``RESULT`` is not specified and the operation fails, + an error is emitted. + .. versionchanged:: 3.30 ```` can be an empty list. CMake 3.29 and earlier required at least one directory to be given. @@ -802,10 +811,21 @@ Transfer environment variable will be used instead. See :variable:`CMAKE_TLS_VERSION` for allowed values. + .. versionchanged:: 3.31 + The default is TLS 1.2. + Previously, no minimum version was enforced by default. + ``TLS_VERIFY `` Specify whether to verify the server certificate for ``https://`` URLs. - The default is to *not* verify. If this option is not specified, the - value of the :variable:`CMAKE_TLS_VERIFY` variable will be used instead. + If this option is not specified, the value of the + :variable:`CMAKE_TLS_VERIFY` variable or :envvar:`CMAKE_TLS_VERIFY` + environment variable will be used instead. + If neither is set, the default is *on*. + + .. versionchanged:: 3.31 + The default is on. Previously, the default was off. + Users may set the :envvar:`CMAKE_TLS_VERIFY` environment + variable to ``0`` to restore the old default. .. versionadded:: 3.18 Added support to ``file(UPLOAD)``. @@ -818,9 +838,7 @@ Transfer .. versionadded:: 3.18 Added support to ``file(UPLOAD)``. - For ``https://`` URLs CMake must be built with OpenSSL support. ``TLS/SSL`` - certificates are not checked by default. Set ``TLS_VERIFY`` to ``ON`` to - check certificates. + For ``https://`` URLs CMake must be built with SSL/TLS support. Additional options to ``DOWNLOAD`` are: @@ -892,8 +910,9 @@ Archiving PATHS ... [FORMAT ] [COMPRESSION - [COMPRESSION_LEVEL ]] + [COMPRESSION_LEVEL ]] [MTIME ] + [WORKING_DIRECTORY ] [VERBOSE]) :target: ARCHIVE_CREATE :break: verbatim @@ -904,40 +923,55 @@ Archiving listed in ````. Note that ```` must list actual files or directories; wildcards are not supported. - Use the ``FORMAT`` option to specify the archive format. Supported values - for ```` are ``7zip``, ``gnutar``, ``pax``, ``paxr``, ``raw`` and - ``zip``. If ``FORMAT`` is not given, the default format is ``paxr``. + The options are: - Some archive formats allow the type of compression to be specified. - The ``7zip`` and ``zip`` archive formats already imply a specific type of - compression. The other formats use no compression by default, but can be - directed to do so with the ``COMPRESSION`` option. Valid values for - ```` are ``None``, ``BZip2``, ``GZip``, ``XZ``, and ``Zstd``. + ``FORMAT `` + Specify the archive format. Supported values for ```` are + ``7zip``, ``gnutar``, ``pax``, ``paxr``, ``raw`` and ``zip``. + If ``FORMAT`` is not given, the default format is ``paxr``. + + ``COMPRESSION `` + Some archive formats allow the type of compression to be specified. + The ``7zip`` and ``zip`` archive formats already imply a specific type of + compression. The other formats use no compression by default, but can be + directed to do so with the ``COMPRESSION`` option. Valid values for + ```` are ``None``, ``BZip2``, ``GZip``, ``XZ``, and ``Zstd``. + + .. note:: + With ``FORMAT`` set to ``raw``, only one file will be compressed + with the compression type specified by ``COMPRESSION``. + + ``COMPRESSION_LEVEL `` + .. versionadded:: 3.19 - .. versionadded:: 3.19 The compression level can be specified with the ``COMPRESSION_LEVEL`` option. The ```` should be between 0-9, with the default being 0. The ``COMPRESSION`` option must be present when ``COMPRESSION_LEVEL`` is given. - .. versionadded:: 3.26 - The ```` of the ``Zstd`` algorithm can be set - between 0-19. + .. versionadded:: 3.26 + The ```` of the ``Zstd`` algorithm can be set + between 0-19. - .. note:: - With ``FORMAT`` set to ``raw``, only one file will be compressed with the - compression type specified by ``COMPRESSION``. + ``MTIME `` + Specify the modification time recorded in tarball entries. - The ``VERBOSE`` option enables verbose output for the archive operation. + ``WORKING_DIRECTORY `` + .. versionadded:: 3.31 - To specify the modification time recorded in tarball entries, use - the ``MTIME`` option. + Specify the directory in which the archive creation operation will + be executed. Paths in the ```` argument can be relative to + this directory. If this option is not provided, the current working + directory will be used by default. + + ``VERBOSE`` + Enable verbose output from the archive operation. .. signature:: file(ARCHIVE_EXTRACT INPUT [DESTINATION ] - [PATTERNS ...] + [PATTERNS ...] [LIST_ONLY] [VERBOSE] [TOUCH]) @@ -947,17 +981,30 @@ Archiving Extracts or lists the content of the specified ````. - The directory where the content of the archive will be extracted to can - be specified using the ``DESTINATION`` option. If the directory does not - exist, it will be created. If ``DESTINATION`` is not given, the current - binary directory will be used. + The options are: + + ``DESTINATION `` + Specify the directory under which the content of the archive will be + extracted. If the directory does not exist, it will be created. + If ``DESTINATION`` is not given, the current binary directory will + be used. - If required, you may select which files and directories to list or extract - from the archive using the specified ````. Wildcards are - supported. If the ``PATTERNS`` option is not given, the entire archive will - be listed or extracted. + ``PATTERNS ...`` + Extract/list only files and directories that match one of the given + patterns. Wildcards are supported. If the ``PATTERNS`` option is + not given, the entire archive will be listed or extracted. - ``LIST_ONLY`` will list the files in the archive rather than extract them. + ``LIST_ONLY`` + List the files in the archive rather than extract them. + + ``TOUCH`` + .. versionadded:: 3.24 + + Give extracted files a current local timestamp instead of extracting + file timestamps from the archive. + + ``VERBOSE`` + Enable verbose output from the extraction operation. .. note:: The working directory for this subcommand is the ``DESTINATION`` directory @@ -966,12 +1013,6 @@ Archiving ``INPUT`` archives as they are unlikely to be extracted where a relative path works. - .. versionadded:: 3.24 - The ``TOUCH`` option gives extracted files a current local - timestamp instead of extracting file timestamps from the archive. - - With ``VERBOSE``, the command will produce verbose output. - Handling Runtime Binaries ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1162,6 +1203,14 @@ Handling Runtime Binaries 5. Otherwise, the dependency is unresolved. + .. versionchanged:: 3.31 + + Resolution of each encountered library file name occurs at most once + while processing a given root ELF file (executable or shared object). + If a library file name is encountered again in the dependency tree, + the original resolution is assumed. This behavior more closely matches + the dynamic loader's behavior on Linux. + On Windows platforms, library resolution works as follows: 1. DLL dependency names are converted to lowercase for matching filters. diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index f555fe4dd..c26076b13 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst @@ -503,6 +503,42 @@ The :variable:`CMAKE_IGNORE_PATH`, :variable:`CMAKE_IGNORE_PREFIX_PATH`, :variable:`CMAKE_SYSTEM_IGNORE_PREFIX_PATH` variables can also cause some of the above locations to be ignored. +Paths are searched in the order described above. The first viable package +configuration file found is used, even if a newer version of the package +resides later in the list of search paths. + +For search paths which contain ``*``, the order among matching paths +is unspecified unless the :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` variable +is set. This variable, along with the +:variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION` variable, determines the order +in which CMake considers paths that match a single search path containing +``*``. For example, if the file system contains the package +configuration files + +:: + + /example-1.2/example-config.cmake + /example-1.10/example-config.cmake + /share/example-2.0/example-config.cmake + +it is unspecified (when the aforementioned variables are unset) whether +``find_package(example)`` will find ``example-1.2`` or ``example-1.10`` +(assuming that both are viable), but ``find_package`` will *not* find +``example-2.0``, because one of the other two will be found first. + +To control the order in which ``find_package`` searches directories that match +a glob expression, use :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` and +:variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION`. +For instance, to cause the above example to select ``example-1.10``, +one can set + +.. code-block:: cmake + + SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) + SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) + +before calling ``find_package``. + .. versionadded:: 3.16 Added the ``CMAKE_FIND_USE_`` variables to globally disable various search locations. @@ -648,22 +684,6 @@ is acceptable the following variables are set: Number of version components, 0 to 4 and the corresponding package configuration file is loaded. -When multiple package configuration files are available whose version files -claim compatibility with the version requested it is unspecified which -one is chosen: unless the variable :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` -is set no attempt is made to choose a highest or closest version number. - -To control the order in which ``find_package`` checks for compatibility use -the two variables :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` and -:variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION`. -For instance in order to select the highest version one can set - -.. code-block:: cmake - - SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) - SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) - -before calling ``find_package``. Package File Interface Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/command/find_program.rst b/Help/command/find_program.rst index b6b2508b8..d20ca9366 100644 --- a/Help/command/find_program.rst +++ b/Help/command/find_program.rst @@ -41,3 +41,23 @@ When more than one value is given to the ``NAMES`` option this command by default will consider one name at a time and search every directory for it. The ``NAMES_PER_DIR`` option tells this command to consider one directory at a time and search for all names in it. + +The set of files considered to be programs is platform-specific: + +* On Windows, filename suffixes are considered in order ``.com``, ``.exe``, + and no suffix. + +* On non-Windows systems, no filename suffix is considered, but files + must have execute permission (see policy :policy:`CMP0109`). + +To search for scripts, specify an extension explicitly: + +.. code-block:: cmake + + if(WIN32) + set(_script_suffix .bat) + else() + set(_script_suffix .sh) + endif() + + find_program(MY_SCRIPT NAMES my_script${_script_suffix}) diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst index a0a12bb61..202bfd96e 100644 --- a/Help/command/get_property.rst +++ b/Help/command/get_property.rst @@ -29,7 +29,7 @@ It must be one of the following: Scope is unique and does not accept a name. ``DIRECTORY`` - Scope defaults to the current directory but another + Scope defaults to the current directory, but another directory (already processed by CMake) may be named by the full or relative path ````. Relative paths are treated as relative to the current source directory. @@ -79,10 +79,10 @@ It must be one of the following: ``DIRECTORY `` The test property will be read from the ```` directory's - scope. CMake must already know about the directory, either by having added - it through a call to :command:`add_subdirectory` or ```` being the top - level directory. Relative paths are treated as relative to the current - source directory. ```` may reference a binary directory. + scope. CMake must already know about the directory, either by having + added it through a call to :command:`add_subdirectory` or ```` being + the top level directory. Relative paths are treated as relative to the + current source directory. ```` may reference a binary directory. ``CACHE`` Scope must name one cache entry. @@ -91,19 +91,20 @@ It must be one of the following: Scope is unique and does not accept a name. The required ``PROPERTY`` option is immediately followed by the name of -the property to get. If the property is not set an empty value is -returned, although some properties support inheriting from a parent scope -if defined to behave that way (see :command:`define_property`). +the property to get. If the property is not set, the named ```` +will be unset in the calling scope upon return, although some properties +support inheriting from a parent scope if defined to behave that way +(see :command:`define_property`). -If the ``SET`` option is given the variable is set to a boolean +If the ``SET`` option is given, the variable is set to a boolean value indicating whether the property has been set. If the ``DEFINED`` -option is given the variable is set to a boolean value indicating -whether the property has been defined such as with the +option is given, the variable is set to a boolean value indicating +whether the property has been defined, such as with the :command:`define_property` command. -If ``BRIEF_DOCS`` or ``FULL_DOCS`` is given then the variable is set to a +If ``BRIEF_DOCS`` or ``FULL_DOCS`` is given, then the variable is set to a string containing documentation for the requested property. If -documentation is requested for a property that has not been defined +documentation is requested for a property that has not been defined, ``NOTFOUND`` is returned. .. note:: diff --git a/Help/command/if.rst b/Help/command/if.rst index de25ad35b..9eaf6da1b 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst @@ -41,13 +41,15 @@ Compound conditions are evaluated in the following order of precedence: 1. `Parentheses`_. -2. Unary tests such as `EXISTS`_, `COMMAND`_, and `DEFINED`_. +2. Unary tests such as `COMMAND`_, `POLICY`_, `TARGET`_, `TEST`_, + `EXISTS`_, `IS_READABLE`_, `IS_WRITABLE`_, `IS_EXECUTABLE`_, + `IS_DIRECTORY`_, `IS_SYMLINK`_, `IS_ABSOLUTE`_, and `DEFINED`_. 3. Binary tests such as `EQUAL`_, `LESS`_, `LESS_EQUAL`_, `GREATER`_, `GREATER_EQUAL`_, `STREQUAL`_, `STRLESS`_, `STRLESS_EQUAL`_, `STRGREATER`_, `STRGREATER_EQUAL`_, `VERSION_EQUAL`_, `VERSION_LESS`_, `VERSION_LESS_EQUAL`_, `VERSION_GREATER`_, `VERSION_GREATER_EQUAL`_, - `PATH_EQUAL`_, and `MATCHES`_. + `PATH_EQUAL`_, `IN_LIST`_, `IS_NEWER_THAN`_, and `MATCHES`_. 4. Unary logical operator `NOT`_. @@ -471,6 +473,10 @@ above-documented condition syntax accepts ````: variables. If so, their defined values are used otherwise the original value is used. +* The left hand argument to `IN_LIST`_ is tested to see if it is a defined + variable. If so, the variable's value is used, otherwise the original + value is used. + * The right hand argument to `NOT`_ is tested to see if it is a boolean constant. If so, the value is used, otherwise it is assumed to be a variable and it is dereferenced. diff --git a/Help/command/include_external_msproject.rst b/Help/command/include_external_msproject.rst index 435465462..6490856b6 100644 --- a/Help/command/include_external_msproject.rst +++ b/Help/command/include_external_msproject.rst @@ -1,7 +1,8 @@ include_external_msproject -------------------------- -Include an external Microsoft project file in a workspace. +Include an external Microsoft project file in the solution file produced +by :ref:`Visual Studio Generators`. Ignored on other generators. .. code-block:: cmake @@ -11,9 +12,9 @@ Include an external Microsoft project file in a workspace. [PLATFORM platformName] dep1 dep2 ...) -Includes an external Microsoft project in the generated workspace -file. Currently does nothing on UNIX. This will create a target -named ``[projectname]``. This can be used in the :command:`add_dependencies` +Includes an external Microsoft project in the generated solution file. +This will create a target named ``[projectname]``. +This can be used in the :command:`add_dependencies` command to make things depend on the external project. ``TYPE``, ``GUID`` and ``PLATFORM`` are optional parameters that allow one to diff --git a/Help/command/install.rst b/Help/command/install.rst index b2742d6a7..a0e6a081f 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -19,6 +19,7 @@ Synopsis install(`SCRIPT`_ [...]) install(`CODE`_ [...]) install(`EXPORT`_ [...]) + install(`PACKAGE_INFO`_ [...]) install(`RUNTIME_DEPENDENCY_SET`_ [...]) Introduction @@ -38,6 +39,13 @@ are executed in order during installation. The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the default copying behavior of ``install()``. +.. versionchanged:: 3.31 + Projects can enable :prop_gbl:`INSTALL_PARALLEL` to enable a parallel + installation. When using the parallel install, subdirectories added by calls + to the :command:`add_subdirectory` command are installed independently + and the order that install rules added in different subdirectories will run is + not guaranteed. + .. _`common options`: There are multiple signatures for this command. Some of them define @@ -50,7 +58,7 @@ signatures that specify them. The common options are: ```` should be a relative path. An absolute path is allowed, but not recommended. - When a relative path is given it is interpreted relative to the value + When a relative path is given, it is interpreted relative to the value of the :variable:`CMAKE_INSTALL_PREFIX` variable. The prefix can be relocated at install time using the ``DESTDIR`` mechanism explained in the :variable:`CMAKE_INSTALL_PREFIX` variable @@ -67,6 +75,11 @@ signatures that specify them. The common options are: If an absolute path (with a leading slash or drive letter) is given it is used verbatim. + .. versionchanged:: 3.31 + ```` will be normalized according to the same + :ref:`normalization rules ` as the + :command:`cmake_path` command. + ``PERMISSIONS ...`` Specify permissions for installed files. Valid permissions are ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``, ``GROUP_READ``, @@ -104,11 +117,6 @@ signatures that specify them. The common options are: Specify that the file is excluded from a full installation and only installed as part of a component-specific installation -``RENAME `` - Specify a name for an installed file that may be different from the - original file. Renaming is allowed only when a single file is - installed by the command. - ``OPTIONAL`` Specify that it is not an error if the file to be installed does not exist. @@ -393,6 +401,12 @@ Signatures If a relative path is specified, it is treated as relative to the :genex:`$`. + Unlike other ``DESTINATION`` arguments for the various ``install()`` + subcommands, paths given after ``INCLUDES DESTINATION`` are used as + given. They are not normalized, nor assumed to be normalized, although + it is recommended that they are given in normalized form (see + :ref:`Normalization`). + ``RUNTIME_DEPENDENCY_SET `` .. versionadded:: 3.21 @@ -536,6 +550,10 @@ Signatures However, if any item begins in a generator expression it must evaluate to a full path. + The optional ``RENAME `` argument is used to specify a name for the + installed file that is different from the original file name. Renaming + is allowed only when a single file is installed by the command. + Either a ``TYPE`` or a ``DESTINATION`` must be provided, but not both. A ``TYPE`` argument specifies the generic file type of the files being installed. A destination will then be set automatically by taking the @@ -561,6 +579,7 @@ Signatures ``LOCALE`` ``${CMAKE_INSTALL_LOCALEDIR}`` ``/locale`` ``MAN`` ``${CMAKE_INSTALL_MANDIR}`` ``/man`` ``DOC`` ``${CMAKE_INSTALL_DOCDIR}`` ``/doc`` + ``LIBEXEC`` ``${CMAKE_INSTALL_LIBEXECDIR}`` ``libexec`` ======================= ================================== ========================= Projects wishing to follow the common practice of installing headers into a @@ -599,6 +618,9 @@ Signatures use "generator expressions" with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual for available expressions. + .. versionadded:: 3.31 + The ``TYPE`` argument now supports type ``LIBEXEC``. + .. signature:: install(DIRECTORY ... [...]) @@ -713,6 +735,7 @@ Signatures ``LOCALE`` ``${CMAKE_INSTALL_LOCALEDIR}`` ``/locale`` ``MAN`` ``${CMAKE_INSTALL_MANDIR}`` ``/man`` ``DOC`` ``${CMAKE_INSTALL_DOCDIR}`` ``/doc`` + ``LIBEXEC`` ``${CMAKE_INSTALL_LIBEXECDIR}`` ``libexec`` ======================= ================================== ========================= Note that some of the types' built-in defaults use the ``DATAROOT`` directory as @@ -736,6 +759,9 @@ Signatures The list of ``dirs...`` given to ``DIRECTORY`` may use "generator expressions" too. + .. versionadded:: 3.31 + The ``TYPE`` argument now supports type ``LIBEXEC``. + .. signature:: install(SCRIPT [...]) install(CODE [...]) @@ -800,6 +826,7 @@ Signatures the generated file will be called ``.cmake`` but the ``FILE`` option may be used to specify a different name. The value given to the ``FILE`` option must be a file name with the ``.cmake`` extension. + If a ``CONFIGURATIONS`` option is given then the file will only be installed when one of the named configurations is installed. Additionally, the generated import file will reference only the matching target @@ -898,6 +925,61 @@ Signatures executable from the installation tree using the imported target name ``mp_myexe`` as if the target were built in its own tree. +.. signature:: + install(PACKAGE_INFO [...]) + + .. versionadded:: 3.31 + .. note:: + + Experimental. Gated by ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO``. + + Installs a |CPS|_ file exporting targets for dependent projects: + + .. code-block:: cmake + + install(PACKAGE_INFO EXPORT + [APPENDIX ] + [DESTINATION ] + [LOWER_CASE_FILE] + [VERSION + [COMPAT_VERSION ] + [VERSION_SCHEMA ]] + [DEFAULT_TARGETS ...] + [DEFAULT_CONFIGURATIONS ...] + [PERMISSIONS ...] + [CONFIGURATIONS ...] + [COMPONENT ] + [EXCLUDE_FROM_ALL]) + + The ``PACKAGE_INFO`` form generates and installs a |CPS| file which describes + installed targets such that they can be consumed by another project. + Target installations are associated with the export ```` + using the ``EXPORT`` option of the :command:`install(TARGETS)` signature + documented above. Unlike :command:`install(EXPORT)`, this information is not + expressed in CMake code, and can be consumed by tools other than CMake. When + imported into another CMake project, the imported targets will be prefixed + with ``::``. By default, the generated file will be called + ``[-].cps``. If ``LOWER_CASE_FILE`` is given, + the package name as it appears on disk (in both the file name and install + destination) will be first converted to lower case. + + If ``DESTINATION`` is not specified, a platform-specific default is used. + + If ``APPENDIX`` is specified, rather than generating a top level package + specification, the specified targets will be exported as an appendix to the + named package. Appendices may be used to separate less commonly used targets + (along with their external dependencies) from the rest of a package. This + enables consumers to ignore transitive dependencies for targets that they + don't use, and also allows a single logical "package" to be composed of + artifacts produced by multiple build trees. + + Appendices are not permitted to change basic package metadata; therefore, + none of ``VERSION``, ``COMPAT_VERSION``, ``VERSION_SCHEMA``, + ``DEFAULT_TARGETS`` or ``DEFAULT_CONFIGURATIONS`` may be specified in + combination with ``APPENDIX``. Additionally, it is strongly recommended that + use of ``LOWER_CASE_FILE`` should be consistent between the main package and + any appendices. + .. signature:: install(RUNTIME_DEPENDENCY_SET [...]) @@ -1090,3 +1172,6 @@ and by CPack. You can also invoke this script manually with This is an environment variable rather than a CMake variable. It allows you to change the installation prefix on UNIX systems. See :envvar:`DESTDIR` for details. + +.. _CPS: https://cps-org.github.io/cps/ +.. |CPS| replace:: Common Package Specification diff --git a/Help/command/project.rst b/Help/command/project.rst index d220b83f0..b42a03490 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst @@ -44,27 +44,18 @@ Projects should not rely on ``_SOURCE_DIR`` or ``_BINARY_DIR`` holding a particular value outside of the scope of the call to ``project()`` or one of its child scopes. -.. versionchanged:: 3.30.3 +.. versionchanged:: 3.30 ``_SOURCE_DIR``, ``_BINARY_DIR``, and - ``_IS_TOP_LEVEL`` are always set as non-cache variables by - ``project( ...)``. - -.. versionchanged:: 3.30.4 - The variables ``_SOURCE_DIR``, ``_BINARY_DIR``, - and ``_IS_TOP_LEVEL`` are only set as non-cache variables if - they are already set as cache or non-cache variables when - ``project( ...)`` is called. - Note that this logic is flawed, as it can result in different behavior - between the first and subsequent runs because cache variables won't exist - on the first run, but they will on subsequent runs. - -.. versionchanged:: 3.30.5 - The variables ``_SOURCE_DIR``, ``_BINARY_DIR``, - and ``_IS_TOP_LEVEL`` are only set as non-cache variables if - they are already set as non-cache variables when - ``project( ...)`` is called. - Unlike the flawed behavior of 3.30.4, non-cache variables will not be set - if only cache variables of the same name are set. + ``_IS_TOP_LEVEL``, if already set as normal variables when + ``project( ...)`` is called, are updated by the call. + Cache entries by the same names are always set as before. + See release notes for 3.30.3, 3.30.4, and 3.30.5 for details. + +.. versionchanged:: 3.31 + ``_SOURCE_DIR``, ``_BINARY_DIR``, and + ``_IS_TOP_LEVEL`` are always set as normal variables by + ``project( ...)``. See policy :policy:`CMP0180`. + Cache entries by the same names are always set as before. Options ^^^^^^^ diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst index a82adda05..94a24297d 100644 --- a/Help/command/target_link_libraries.rst +++ b/Help/command/target_link_libraries.rst @@ -140,6 +140,11 @@ Items containing ``::``, such as ``Foo::Bar``, are assumed to be target names and will cause an error if no such target exists. See policy :policy:`CMP0028`. +See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and +corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property +for details on how CMake orders direct link dependencies on linker +command lines. + See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. diff --git a/Help/cpack_gen/archive.rst b/Help/cpack_gen/archive.rst index 7f7921d97..5836f91b8 100644 --- a/Help/cpack_gen/archive.rst +++ b/Help/cpack_gen/archive.rst @@ -91,14 +91,10 @@ CPack generators which are essentially archives at their core. These include: .. versionadded:: 3.18 - :Default: ``1`` + :Default: value of :variable:`CPACK_THREADS` If set to ``0``, the number of available cores on the machine will be used instead. - The default is ``1`` which limits compression to a single thread. Note that - not all compression modes support threading in all environments. Currently, - only the XZ compression may support it. - - See also the :variable:`CPACK_THREADS` variable. + Note that not all compression modes support threading in all environments. .. versionadded:: 3.21 diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index 23436de88..15f94dcd2 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst @@ -182,11 +182,22 @@ List of CPack DEB generator specific variables: only the automatically discovered dependencies will be set for this component. + .. versionchanged:: 3.31 + + The variable is always expanded as a list. Before it was expanded only + if used in cooperation with :variable:`CPACK_DEB_COMPONENT_INSTALL`, + :variable:`CPACK_DEBIAN_PACKAGE_SHLIBDEPS` or + :variable:`CPACK_DEBIAN__PACKAGE_SHLIBDEPS`. + This meant that if a component had no shared libraries discovered + (e.g. a package composed only of scripts) you had to join the list + by yourself to obtain a valid Depends field. + Example: .. code-block:: cmake set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.1-6), libc6 (< 2.4)") + list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS cmake) .. variable:: CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS @@ -654,6 +665,31 @@ List of CPack DEB generator specific variables: This value is not interpreted. It is possible to pass an optional revision number of the referenced source package as well. +.. variable:: CPACK_DEBIAN_PACKAGE_MULTIARCH + CPACK_DEBIAN__PACKAGE_MULTIARCH + + Sets the `Multi-Arch` field of the Debian package. + Packages can declare in their control file how they should handle + situations, where packages for different architectures are being installed + on the same machine. + + :Mandatory: No + :Default: + + - An empty string for non-component based installations + - :variable:`CPACK_DEBIAN_PACKAGE_MULTIARCH` for component-based + installations. + + .. versionadded:: 3.31 + Per-component :variable:`!CPACK_DEBIAN__PACKAGE_MULTIARCH` variables. + + See https://wiki.debian.org/MultiArch/Hints + + .. note:: + + This value is validated. It must be one of the following values: + ``same``, ``foreign``, ``allowed``. + Packaging of debug information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/cpack_gen/ifw.rst b/Help/cpack_gen/ifw.rst index c252095be..68f8792d9 100644 --- a/Help/cpack_gen/ifw.rst +++ b/Help/cpack_gen/ifw.rst @@ -275,6 +275,16 @@ Package This feature is available for QtIFW 4.0.0 and later. +.. variable:: CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS + + .. versionadded:: 3.31 + + A list of URLs associated with the ProductImages. + Only used if ``CPACK_IFW_PACKAGE_PRODUCT_IMAGES`` is defined + and it has the same size. + + This feature is available for QtIFW 4.0.0 and later. + .. variable:: CPACK_IFW_PACKAGE_RUN_PROGRAM .. versionadded:: 3.23 diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst index 4a2ce5f9b..ebd376666 100644 --- a/Help/cpack_gen/rpm.rst +++ b/Help/cpack_gen/rpm.rst @@ -246,9 +246,8 @@ List of CPack RPM generator specific variables: :Default: (system default) May be used to override RPM compression type to be used to build the - RPM. For example some Linux distribution now default to ``lzma`` or ``xz`` - compression whereas older cannot use such RPM. Using this one can enforce - compression type to be used. + RPM. For example some Linux distributions default to ``xz`` or ``zstd``. + Using this, one can specify a specific compression type to be used. Possible values are: @@ -264,6 +263,11 @@ List of CPack RPM generator specific variables: ``gzip`` GNU Gzip compression + ``zstd`` + .. versionadded:: 3.31 + + Zstandard compression + .. variable:: CPACK_RPM_PACKAGE_AUTOREQ CPACK_RPM__PACKAGE_AUTOREQ diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst index f19698868..ae4f1e7c6 100644 --- a/Help/cpack_gen/wix.rst +++ b/Help/cpack_gen/wix.rst @@ -458,8 +458,7 @@ Windows using WiX. administrative privileges. Start menu entries created by the installer are visible to all users. - This is the default if :variable:`CPACK_WIX_VERSION` is set to any - value other than ``3``. + This is the default. See policy :policy:`CMP0172`. ``perUser`` Not yet supported. This is reserved for future use. @@ -467,9 +466,8 @@ Windows using WiX. ``NONE`` Create an installer without any ``InstallScope`` attribute. - If :variable:`CPACK_WIX_VERSION` is not set, or is set to ``3``, this - value is the default to preserve compatibility with 3.28 and lower. - Otherwise, this value is not supported. + This is supported only if :variable:`CPACK_WIX_VERSION` is not set, + or is set to ``3``. .. deprecated:: 3.29 diff --git a/Help/dev/README.rst b/Help/dev/README.rst index 9c3878b0c..2a2d32d9d 100644 --- a/Help/dev/README.rst +++ b/Help/dev/README.rst @@ -23,12 +23,13 @@ branches and tags. Upstream development processes are covered by the following documents: * The `CMake Review Process`_ manages integration of changes. -* The `CMake Testing Process`_ drives integration testing. +* The `CMake Integration Testing`_ infrastructure tests changes + before and after merging. .. _`Kitware's GitLab Instance`: https://gitlab.kitware.com .. _`CMake Repository`: https://gitlab.kitware.com/cmake/cmake .. _`CMake Review Process`: review.rst -.. _`CMake Testing Process`: testing.rst +.. _`CMake Integration Testing`: integration-testing.rst Developer Documentation ======================= @@ -37,10 +38,12 @@ CMake developer documentation is provided by the following documents: * The `CMake Source Code Guide`_. * The `CMake Documentation Guide`_. +* The `CMake Testing Guide`_. * The `CMake Experimental Features Guide`_. .. _`CMake Source Code Guide`: source.rst .. _`CMake Documentation Guide`: documentation.rst +.. _`CMake Testing Guide`: testing.rst .. _`CMake Experimental Features Guide`: experimental.rst Maintainer Documentation diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst index 00413e17d..8270bb147 100644 --- a/Help/dev/documentation.rst +++ b/Help/dev/documentation.rst @@ -310,17 +310,17 @@ are suppressed inside of square- or angle-brackets. This behavior can be controlled using the ``:break:`` option; note, however, that there is no way to *force* a line break. The default value is 'smart'. Allowable values are: - ``all`` - Allow line breaks at any whitespace. +``all`` + Allow line breaks at any whitespace. - ``smart`` (default) - Allow line breaks at whitespace, except between matched square- or - angle-brackets. For example, if a signature contains the text - ``... [OUTPUT_VARIABLE ]``, a line break would be allowed - after ``...`` but not between ``OUTPUT_VARIABLE`` and ````. +``smart`` (default) + Allow line breaks at whitespace, except between matched square- or + angle-brackets. For example, if a signature contains the text + ``... [OUTPUT_VARIABLE ]``, a line break would be allowed + after ``...`` but not between ``OUTPUT_VARIABLE`` and ````. - ``verbatim`` - Allow line breaks only where the source document contains a newline. +``verbatim`` + Allow line breaks only where the source document contains a newline. The directive treats its content as the documentation of the signature(s). Indent the signature documentation accordingly. diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst index 35ea34f89..f5b9114b7 100644 --- a/Help/dev/experimental.rst +++ b/Help/dev/experimental.rst @@ -39,6 +39,23 @@ When activated, this experimental feature provides the following: using the ``CMAKE_EXPORT_FIND_PACKAGE_NAME`` variable and/or ``EXPORT_FIND_PACKAGE_NAME`` target property. +Export |CPS| Package Information +================================ + +In order to activate support for this experimental feature, set + +* variable ``CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO`` to +* value ``b80be207-778e-46ba-8080-b23bba22639e``. + +This UUID may change in future versions of CMake. Be sure to use the value +documented here by the source tree of the version of CMake with which you are +experimenting. + +When activated, this experimental feature provides the following: + +* The experimental ``install(PACKAGE_INFO)`` command is available to export + package information in the |CPS|_ format. + C++ ``import std`` support ========================== @@ -60,3 +77,27 @@ When activated, this experimental feature provides the following: * Targets with the property set to a true value and at least ``cxx_std_23`` may use ``import std;`` in any scanned C++ source file. + +.. _CPS: https://cps-org.github.io/cps/ +.. |CPS| replace:: Common Package Specification + +Build database support +====================== + +In order to activate support for exporting build databases, set + +* variable ``CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE`` to +* value ``4bd552e2-b7fb-429a-ab23-c83ef53f3f13``. + +This UUID may change in future versions of CMake. Be sure to use the value +documented here by the source tree of the version of CMake with which you are +experimenting. + +When activated, this experimental feature provides the following: + +* The :prop_tgt:`EXPORT_BUILD_DATABASE` target property and its initializing + variable :variable:`CMAKE_EXPORT_BUILD_DATABASE` and environment variable + :envvar:`CMAKE_EXPORT_BUILD_DATABASE`. + +* Targets with the property set to a true value will have their C++ build + information exported to the build database. diff --git a/Help/dev/integration-testing.rst b/Help/dev/integration-testing.rst new file mode 100644 index 000000000..434cf2301 --- /dev/null +++ b/Help/dev/integration-testing.rst @@ -0,0 +1,46 @@ +CMake Integration Testing +************************* + +The following documents how to run integration testing builds. +See documentation on `CMake Development`_ for more information. + +See the `CMake Testing Guide`_ for running the test suite locally. + +.. _`CMake Development`: README.rst +.. _`CMake Testing Guide`: testing.rst + +CMake Dashboard Scripts +======================= + +The *integration testing* step of the `CMake Review Process`_ uses a set of +testing machines that follow an integration branch on their own schedule to +drive testing and submit results to the `CMake CDash Page`_. Anyone is +welcome to provide testing machines in order to help keep support for their +platforms working. + +The `CMake Dashboard Scripts Repository`_ provides CTest scripts to drive +nightly, continuous, and experimental testing of CMake. Use the following +commands to set up a new integration testing client: + +.. code-block:: console + + $ mkdir -p ~/Dashboards + $ cd ~/Dashboards + $ git clone https://gitlab.kitware.com/cmake/dashboard-scripts.git CMakeScripts + $ cd CMakeScripts + +The `cmake_common.cmake`_ script contains comments at the top with +instructions to set up a testing client. As it instructs, create a +CTest script with local settings and include ``cmake_common.cmake``. + +.. _`CMake Review Process`: review.rst +.. _`CMake CDash Page`: https://open.cdash.org/index.php?project=CMake +.. _`CMake Dashboard Scripts Repository`: https://gitlab.kitware.com/cmake/dashboard-scripts +.. _`cmake_common.cmake`: https://gitlab.kitware.com/cmake/dashboard-scripts/-/blob/master/cmake_common.cmake + +Nightly Start Time +------------------ + +The ``cmake_common.cmake`` script expects its includer to be run from a +nightly scheduled task (cron job). Schedule such tasks for sometime after +``1:00am UTC``, the time at which our nightly testing branches fast-forward. diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst index c904673fb..10b3f7d7e 100644 --- a/Help/dev/maint.rst +++ b/Help/dev/maint.rst @@ -306,11 +306,22 @@ Commit with a message such as:: Begin post-$ver development -Push the update to the ``master`` and ``release`` branches: +Create a topic branch for the update to ``master``: .. code-block:: shell - git push --atomic origin master release-$ver:release + git branch branch-$ver master + +Open a merge request with the ``branch-$ver`` branch for review and CI. +Add the following trailing lines in the merge request description:: + + Fast-forward: true + Backport-ff: release:HEAD~1^2 + +This configures the ``Do: merge`` action to fast-foward the ``master`` +and ``release`` branches to the respective commits created above. + +Further steps may proceed after this has been merged. Announce 'release' Branch ------------------------- diff --git a/Help/dev/review.rst b/Help/dev/review.rst index 34796a1a3..40bf3e56f 100644 --- a/Help/dev/review.rst +++ b/Help/dev/review.rst @@ -348,7 +348,7 @@ Integration Testing The above `topic testing`_ tests the MR topic independent of other merge requests and on only a few key platforms and configurations. -The `CMake Testing Process`_ also has a large number of machines +`CMake Integration Testing`_ also uses a large number of machines provided by Kitware and generous volunteers that cover nearly all supported platforms, generators, and configurations. In order to avoid overwhelming these resources, they do not test every MR @@ -403,7 +403,7 @@ until one of the following occurs: Once a MR has been removed from the topic stage a new ``Do: stage`` command is needed to stage it again. -.. _`CMake Testing Process`: testing.rst +.. _`CMake Integration Testing`: integration-testing.rst Resolve ======= diff --git a/Help/dev/source.rst b/Help/dev/source.rst index fa7d620d1..363089fbd 100644 --- a/Help/dev/source.rst +++ b/Help/dev/source.rst @@ -249,6 +249,14 @@ These are: * ``cm::is_unique_ptr``: Checks if a type is a ``std::unique_ptr`` type. + * ``cm::remove_member_pointer`` + Produces the underlying type of a member-pointer type, ie, given ``T C::*``, + returns ``T``. + + * ``cm::member_pointer_class`` + Produces the class associated with a member-pointer type, ie, given + ``T C::*``, returns ``C``. + CMake assumes the compiler supports ``#pragma once``. Use this for all hand-written header files. @@ -310,6 +318,7 @@ The CMake source tree is organized as follows. * ``Tests/``: The test suite. See `Tests/README.rst`_. + To run the tests, see the `CMake Testing Guide`_. * ``Utilities/``: Scripts, third-party source code. @@ -331,5 +340,6 @@ The CMake source tree is organized as follows. See `Utilities/Release/README.rst`_. .. _`CMake Documentation Guide`: documentation.rst +.. _`CMake Testing Guide`: testing.rst .. _`Tests/README.rst`: ../../Tests/README.rst .. _`Utilities/Release/README.rst`: ../../Utilities/Release/README.rst diff --git a/Help/dev/testing.rst b/Help/dev/testing.rst index 279e4b29f..f90cef770 100644 --- a/Help/dev/testing.rst +++ b/Help/dev/testing.rst @@ -1,43 +1,111 @@ -CMake Testing Process -********************* +CMake Testing Guide +******************* -The following documents the process for running integration testing builds. +The following is a guide to the CMake test suite for developers. See documentation on `CMake Development`_ for more information. +See `CMake Integration Testing`_ for running integration testing builds. + +See `Tests/README.rst`_ for the test suite layout in the source tree. + .. _`CMake Development`: README.rst +.. _`CMake Integration Testing`: integration-testing.rst +.. _`Tests/README.rst`: ../../Tests/README.rst + +Running Tests in the Build Tree +=============================== + +After `Building CMake`_, one may run the test suite in the build tree +using `ctest(1)`_: + +* With a single-configuration CMake generator, such as ``Ninja`` + or ``Unix Makefiles``, one may simply run ``ctest``: + + .. code-block:: console + + $ ctest + +* With a multi-configuration CMake generator, such as + ``Ninja Multi-Config``, ``Visual Studio``, or ``Xcode``, + one must tell ``ctest`` which configuration to test + by passing the ``-C `` option: + + .. code-block:: console + + $ ctest -C Debug + +Some useful `ctest(1)`_ options include: + +``-N`` + List test names without running them. + +``-V`` + Show verbose output from each test. + +``-j `` + Run to run up to ``N`` tests concurrently. -CMake Dashboard Scripts -======================= +``-R `` + Select tests for which the regular expression matches a substring + of their name. -The *integration testing* step of the `CMake Review Process`_ uses a set of -testing machines that follow an integration branch on their own schedule to -drive testing and submit results to the `CMake CDash Page`_. Anyone is -welcome to provide testing machines in order to help keep support for their -platforms working. +Cleaning Test Build Trees +------------------------- -The `CMake Dashboard Scripts Repository`_ provides CTest scripts to drive -nightly, continuous, and experimental testing of CMake. Use the following -commands to set up a new integration testing client: +Many CMake tests create their own test project build trees underneath +the ``Tests/`` directory at the top of the CMake build tree. These +build trees are left behind after testing completes in order to +facilitate manual investigation of results. Many of the tests do *not* +clean their build trees if they are run again, with the exception of +tests using the `RunCMake`_ infrastructure. + +In order to clear test build trees, drive the ``test_clean`` custom target +in the CMake build tree: + +.. code-block:: console + + $ cmake --build . --target test_clean + +This removes the ``Tests/`` subdirectories created by individual tests +so they will use a fresh directory next time they run. + +.. _`Building CMake`: ../../README.rst#building-cmake +.. _`ctest(1)`: https://cmake.org/cmake/help/latest/manual/ctest.1.html +.. _`RunCMake`: ../../Tests/RunCMake/README.rst + +Running Tests with a Different Generator +======================================== + +After `Building CMake`_ with one CMake generator, one may configure the +test suite using a different generator in a separate build tree, without +building CMake itself again, by defining ``CMake_TEST_EXTERNAL_CMAKE`` +to be the absolute path to the ``bin`` directory containing the ``cmake``, +``ctest``, and ``cpack`` executables. + +For example, after building CMake with the ``Ninja`` generator: .. code-block:: console - $ mkdir -p ~/Dashboards - $ cd ~/Dashboards - $ git clone https://gitlab.kitware.com/cmake/dashboard-scripts.git CMakeScripts - $ cd CMakeScripts + $ cmake -B build-ninja -G Ninja -DCMAKE_BUILD_TYPE=Debug + $ cmake --build build-ninja -The `cmake_common.cmake`_ script contains comments at the top with -instructions to set up a testing client. As it instructs, create a -CTest script with local settings and include ``cmake_common.cmake``. +one may configure a second build tree to drive tests with the +``Ninja Multi-Config`` generator: + +.. code-block:: console -.. _`CMake Review Process`: review.rst -.. _`CMake CDash Page`: https://open.cdash.org/index.php?project=CMake -.. _`CMake Dashboard Scripts Repository`: https://gitlab.kitware.com/cmake/dashboard-scripts -.. _`cmake_common.cmake`: https://gitlab.kitware.com/cmake/dashboard-scripts/-/blob/master/cmake_common.cmake + $ cmake -B build-nmc-tests -G "Ninja Multi-Config" \ + -DCMake_TEST_EXTERNAL_CMAKE="$PWD/build-ninja/bin" + $ cmake --build build-nmc-tests --config Release + +The second build tree does not build CMake itself, but does configure +the test suite and build test binaries. One may then run tests normally: + +.. code-block:: console -Nightly Start Time ------------------- + $ cd build-nmc-tests + $ ctest -C Release -The ``cmake_common.cmake`` script expects its includer to be run from a -nightly scheduled task (cron job). Schedule such tasks for sometime after -``1:00am UTC``, the time at which our nightly testing branches fast-forward. +Note that the configuration with which one drives tests in the second +build tree is independent of the configuration with which CMake was +built in the first. diff --git a/Help/dev/try_compile-linker-language.rst b/Help/dev/try_compile-linker-language.rst deleted file mode 100644 index 8482deecf..000000000 --- a/Help/dev/try_compile-linker-language.rst +++ /dev/null @@ -1,6 +0,0 @@ -try_compile-linker-language ---------------------------- - -* The :command:`try_compile` and :command:`try_run` commands gained a - ``LINKER_LANGUAGE`` option to specify the :prop_tgt:`LINKER_LANGUAGE` - target property in the generated test project. diff --git a/Help/envvar/CMAKE_CONFIG_DIR.rst b/Help/envvar/CMAKE_CONFIG_DIR.rst new file mode 100644 index 000000000..1b5f14f12 --- /dev/null +++ b/Help/envvar/CMAKE_CONFIG_DIR.rst @@ -0,0 +1,18 @@ +CMAKE_CONFIG_DIR +---------------- + +.. versionadded:: 3.31 + +.. include:: ENV_VAR.txt + +Specify a CMake user-wide configuration directory for +:manual:`cmake-file-api(7)` queries. + +If this environment variable is not set, the default user-wide +configuration directory is platform-specific: + +- Windows: ``%LOCALAPPDATA%\CMake`` +- macOS: ``$XDG_CONFIG_HOME/CMake`` if set, otherwise + ``$HOME/Library/Application Support/CMake`` +- Linux/Other: ``$XDG_CONFIG_HOME/cmake`` if set, otherwise + ``$HOME/.config/cmake`` diff --git a/Help/envvar/CMAKE_EXPORT_BUILD_DATABASE.rst b/Help/envvar/CMAKE_EXPORT_BUILD_DATABASE.rst new file mode 100644 index 000000000..b6d004d8f --- /dev/null +++ b/Help/envvar/CMAKE_EXPORT_BUILD_DATABASE.rst @@ -0,0 +1,17 @@ +CMAKE_EXPORT_BUILD_DATABASE +--------------------------- + +.. versionadded:: 3.31 + +.. include:: ENV_VAR.txt + +The default value for :variable:`CMAKE_EXPORT_BUILD_DATABASE` when there is no +explicit configuration given on the first run while creating a new build tree. +On later runs in an existing build tree the value persists in the cache as +:variable:`CMAKE_EXPORT_BUILD_DATABASE`. + +.. note :: + + This variable is meaningful only when experimental support for build + databases has been enabled by the + ``CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE`` gate. diff --git a/Help/envvar/CMAKE_INSTALL_PARALLEL_LEVEL.rst b/Help/envvar/CMAKE_INSTALL_PARALLEL_LEVEL.rst new file mode 100644 index 000000000..84b5930c1 --- /dev/null +++ b/Help/envvar/CMAKE_INSTALL_PARALLEL_LEVEL.rst @@ -0,0 +1,11 @@ +CMAKE_INSTALL_PARALLEL_LEVEL +---------------------------- + +.. versionadded:: 3.31 + +.. include:: ENV_VAR.txt + +Specifies the default maximum number of concurrent processes to use when +installing using ``cmake --install``. + +This has no impact unless :prop_gbl:`INSTALL_PARALLEL` is enabled. diff --git a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst index 82bd007e3..bb04b3085 100644 --- a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst +++ b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst @@ -4,7 +4,7 @@ CMAKE_MSVCIDE_RUN_PATH .. include:: ENV_VAR.txt Extra PATH locations for custom commands when using -:generator:`Visual Studio 12 2013` (or above) generators. +:ref:`Visual Studio Generators`. The ``CMAKE_MSVCIDE_RUN_PATH`` environment variable sets the default value for the :variable:`CMAKE_MSVCIDE_RUN_PATH` variable if not already explicitly set. diff --git a/Help/generator/Visual Studio 12 2013.rst b/Help/generator/Visual Studio 12 2013.rst index 522522cc3..8d3b2c8d5 100644 --- a/Help/generator/Visual Studio 12 2013.rst +++ b/Help/generator/Visual Studio 12 2013.rst @@ -1,57 +1,8 @@ Visual Studio 12 2013 --------------------- -Deprecated. Generates Visual Studio 12 (VS 2013) 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 12 2013 tools - using the :generator:`Visual Studio 14 2015` (or above) generator - with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v120``, 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 12" 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 (JavaScript, -Powershell, Python, 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 :option:`cmake -A` option, to specify a target platform - name (architecture). For example: - - * ``cmake -G "Visual Studio 12 2013" -A Win32`` - * ``cmake -G "Visual Studio 12 2013" -A x64`` - * ``cmake -G "Visual Studio 12 2013" -A ARM`` - -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 12 2013 Win64`` - Specify target platform ``x64``. - -``Visual Studio 12 2013 ARM`` - Specify target platform ``ARM``. - -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 :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. - -.. include:: VS_TOOLSET_HOST_ARCH_LEGACY.txt +Removed. This once generated Visual Studio 12 2013 project files, but +the generator has been removed since CMake 3.31. It is still possible +to build with VS 12 2013 tools using the :generator:`Visual Studio 14 2015` +(or above) generator with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v120``, +or by using the :generator:`NMake Makefiles` generator. diff --git a/Help/guide/user-interaction/index.rst b/Help/guide/user-interaction/index.rst index 335599223..0c3ef6ab3 100644 --- a/Help/guide/user-interaction/index.rst +++ b/Help/guide/user-interaction/index.rst @@ -300,6 +300,8 @@ the table below: commands used without a type :variable:`CMAKE_EXPORT_COMPILE_COMMANDS` Generate a ``compile_commands.json`` file for use with clang-based tools + :variable:`CMAKE_EXPORT_BUILD_DATABASE` Generate a ``build_database.json`` + file for use with clang-based tools ========================================== ============================================================ Other project-specific variables may be available diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index bd678b711..00f46aaed 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -22,6 +22,7 @@ These commands are always available. /command/cmake_minimum_required /command/cmake_parse_arguments /command/cmake_path + /command/cmake_pkg_config /command/cmake_policy /command/configure_file /command/continue diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index a69ace66b..fd5935c2b 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst @@ -43,15 +43,18 @@ Environment Variables that Control the Build /envvar/CMAKE_BUILD_PARALLEL_LEVEL /envvar/CMAKE_BUILD_TYPE /envvar/CMAKE_COLOR_DIAGNOSTICS - /envvar/CMAKE_CONFIGURATION_TYPES + /envvar/CMAKE_CONFIG_DIR /envvar/CMAKE_CONFIG_TYPE + /envvar/CMAKE_CONFIGURATION_TYPES /envvar/CMAKE_CROSSCOMPILING_EMULATOR + /envvar/CMAKE_EXPORT_BUILD_DATABASE /envvar/CMAKE_EXPORT_COMPILE_COMMANDS /envvar/CMAKE_GENERATOR /envvar/CMAKE_GENERATOR_INSTANCE /envvar/CMAKE_GENERATOR_PLATFORM /envvar/CMAKE_GENERATOR_TOOLSET /envvar/CMAKE_INSTALL_MODE + /envvar/CMAKE_INSTALL_PARALLEL_LEVEL /envvar/CMAKE_INSTALL_PREFIX /envvar/CMAKE_LANG_COMPILER_LAUNCHER /envvar/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES_EXCLUDE diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index 260030ea9..a4ce5de83 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -50,6 +50,10 @@ It has the following subdirectories: Clients may optionally create the ``reply/`` directory at any time and monitor it for the appearance of a new reply index file. +.. versionadded:: 3.31 + Users can add query files to ``api/v1/query`` inside the + :envvar:`CMAKE_CONFIG_DIR` to create user-wide queries for all CMake projects. + v1 Shared Stateless Query Files ------------------------------- diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 26a4a60e2..a6da67d8c 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -1877,6 +1877,14 @@ These expressions look up the values of rather than the directory of the consuming target for which the expression is being evaluated. + .. versionchanged:: 3.31 + Generator expressions for transitive interface properties, such as + ``$``, now correctly handle + repeated evaluations within nested generator expressions. + Previously, these repeated evaluations returned empty values due + to an optimization for transitive closures. + This change ensures consistent evaluation for non-union operations. + .. genex:: $ :target: TARGET_PROPERTY:prop diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst index 82e11ef76..9f014e10a 100644 --- a/Help/manual/cmake-modules.7.rst +++ b/Help/manual/cmake-modules.7.rst @@ -55,7 +55,6 @@ These modules are loaded using the :command:`include` command. /module/CMakeBackwardCompatibilityCXX /module/CMakeDependentOption /module/CMakeFindDependencyMacro - /module/CMakeFindFrameworks /module/CMakeFindPackageMode /module/CMakeGraphVizOptions /module/CMakePackageConfigHelpers @@ -269,6 +268,7 @@ Deprecated Utility Modules /module/AddFileDependencies /module/CMakeDetermineVSServicePack /module/CMakeExpandImportedTargets + /module/CMakeFindFrameworks /module/CMakeForceCompiler /module/CMakeParseArguments /module/Dart diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 7155404ea..c62fb48d3 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -51,6 +51,23 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used to determine whether to report an error on use of deprecated macros or functions. +Policies Introduced by CMake 3.31 +================================= + +.. toctree:: + :maxdepth: 1 + + CMP0180: project() always sets _* as normal variables. + CMP0179: De-duplication of static libraries on link lines keeps first occurrence. + CMP0178: Test command lines preserve empty arguments. + CMP0177: install() DESTINATION paths are normalized. + CMP0176: execute_process() ENCODING is UTF-8 by default. + CMP0175: add_custom_command() rejects invalid arguments. + CMP0174: cmake_parse_arguments(PARSE_ARGV) defines a variable for an empty string after a single-value keyword. + CMP0173: The CMakeFindFrameworks module is removed. + CMP0172: The CPack module enables per-machine installation by default in the CPack WIX Generator. + CMP0171: 'codegen' is a reserved target name. + Policies Introduced by CMake 3.30 ================================= diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index 59b34c683..e52376f85 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst @@ -39,6 +39,9 @@ The files are a JSON document with an object as the root: .. literalinclude:: presets/example.json :language: json +Preset files specifying version ``10`` or above may include comments using the +key ``$comment`` at any level within the JSON object to provide documentation. + The root object recognizes the following fields: ``$schema`` @@ -81,6 +84,9 @@ The root object recognizes the following fields: ``9`` .. versionadded:: 3.30 + ``10`` + .. versionadded:: 3.31 + ``cmakeMinimumRequired`` An optional object representing the minimum version of CMake needed to build this project. This object consists of the following fields: @@ -261,6 +267,16 @@ that may contain the following fields: :variable:`CMAKE_TOOLCHAIN_FILE` value. It is allowed in preset files specifying version ``3`` or above. +``graphviz`` + An optional string representing the path to the graphviz input file, + that will contain all the library and executable dependencies + in the project. See the documentation for :module:`CMakeGraphVizOptions` + for more details. + + This field supports `macro expansion`_. If a relative path is specified, + it is calculated relative to the current working directory. It is allowed + in preset files specifying version ``10`` 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, @@ -307,10 +323,14 @@ that may contain the following fields: (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. + + 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}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables by + accessing only values from the parent environment. Environment variables are inherited through the ``inherits`` field, and the preset's environment will be the union of its own ``environment`` and @@ -492,10 +512,14 @@ that may contain the following fields: (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. + + 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}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables by + accessing only values from the parent environment. Environment variables are inherited through the ``inherits`` field, and the preset's environment will be the union of its own ``environment`` @@ -653,10 +677,14 @@ that may contain the following fields: (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. + + 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}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables by + accessing only values from the parent environment. Environment variables are inherited through the ``inherits`` field, and the preset's environment will be the union of its own ``environment`` @@ -994,10 +1022,14 @@ fields: (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. + + 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}``). ``$penv{NAME}`` + allows one to prepend or append values to existing environment variables by + accessing only values from the parent environment. Environment variables are inherited through the ``inherits`` field, and the preset's environment will be the union of its own ``environment`` @@ -1264,7 +1296,7 @@ Recognized macros include: ``$penv{}`` Similar to ``$env{}``, 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. + allows one to prepend or append values to existing environment variables. For example, setting ``PATH`` to ``/path/to/ninja/bin:$penv{PATH}`` will prepend ``/path/to/ninja/bin`` to the ``PATH`` environment variable. This is needed because ``$env{}`` does not allow circular diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 673bc7c02..10dbe10d8 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -109,6 +109,7 @@ Properties on Targets /prop_tgt/ADDITIONAL_CLEAN_FILES /prop_tgt/AIX_EXPORT_ALL_SYMBOLS + /prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE /prop_tgt/ALIAS_GLOBAL /prop_tgt/ALIASED_TARGET /prop_tgt/ANDROID_ANT_ADDITIONAL_OPTIONS @@ -216,6 +217,7 @@ Properties on Targets /prop_tgt/EXCLUDE_FROM_ALL /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG + /prop_tgt/EXPORT_BUILD_DATABASE /prop_tgt/EXPORT_COMPILE_COMMANDS /prop_tgt/EXPORT_FIND_PACKAGE_NAME /prop_tgt/EXPORT_NAME @@ -334,6 +336,7 @@ Properties on Targets /prop_tgt/LINK_INTERFACE_MULTIPLICITY_CONFIG /prop_tgt/LINK_LIBRARIES /prop_tgt/LINK_LIBRARIES_ONLY_TARGETS + /prop_tgt/LINK_LIBRARIES_STRATEGY /prop_tgt/LINK_LIBRARY_OVERRIDE /prop_tgt/LINK_LIBRARY_OVERRIDE_LIBRARY /prop_tgt/LINK_OPTIONS @@ -430,6 +433,7 @@ Properties on Targets /prop_tgt/VS_DOTNET_STARTUP_OBJECT /prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION /prop_tgt/VS_DPI_AWARE + /prop_tgt/VS_FRAMEWORK_REFERENCES /prop_tgt/VS_GLOBAL_KEYWORD /prop_tgt/VS_GLOBAL_PROJECT_TYPES /prop_tgt/VS_GLOBAL_ROOTNAMESPACE diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst index 1ebdf85ea..c8721437a 100644 --- a/Help/manual/cmake-toolchains.7.rst +++ b/Help/manual/cmake-toolchains.7.rst @@ -591,14 +591,14 @@ a different SDK (e.g. a simulator) can be selected by setting the necessary (see :ref:`Switching Between Device and Simulator` below). A list of available SDKs can be obtained by running ``xcodebuild -showsdks``. -======== ================= ==================== ================ -OS CMAKE_SYSTEM_NAME Device SDK (default) Simulator SDK -======== ================= ==================== ================ -iOS iOS iphoneos iphonesimulator -tvOS tvOS appletvos appletvsimulator -visionOS visionOS xros xrsimulator -watchOS watchOS watchos watchsimulator -======== ================= ==================== ================ +======== ================= ==================== ================ ============ +OS CMAKE_SYSTEM_NAME Device SDK (default) Simulator SDK Catalyst SDK +======== ================= ==================== ================ ============ +iOS iOS iphoneos iphonesimulator macosx +tvOS tvOS appletvos appletvsimulator N/A +visionOS visionOS xros xrsimulator N/A +watchOS watchOS watchos watchsimulator N/A +======== ================= ==================== ================ ============ For example, to create a CMake configuration for iOS, the following command is sufficient: diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 315c73d65..4c27bd535 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -106,6 +106,7 @@ Variables that Provide Information /variable/CMAKE_SCRIPT_MODE_FILE /variable/CMAKE_SHARED_LIBRARY_PREFIX /variable/CMAKE_SHARED_LIBRARY_SUFFIX + /variable/CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX /variable/CMAKE_SHARED_MODULE_PREFIX /variable/CMAKE_SHARED_MODULE_SUFFIX /variable/CMAKE_SIZEOF_VOID_P @@ -142,6 +143,7 @@ Variables that Provide Information /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM + /variable/CMAKE_WINDOWS_KMDF_VERSION /variable/CMAKE_XCODE_BUILD_SYSTEM /variable/CMAKE_XCODE_PLATFORM_TOOLSET /variable/PROJECT-NAME_BINARY_DIR @@ -194,6 +196,7 @@ Variables that Change Behavior /variable/CMAKE_ERROR_DEPRECATED /variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION /variable/CMAKE_EXECUTE_PROCESS_COMMAND_ECHO + /variable/CMAKE_EXPORT_BUILD_DATABASE /variable/CMAKE_EXPORT_COMPILE_COMMANDS /variable/CMAKE_EXPORT_PACKAGE_REGISTRY /variable/CMAKE_EXPORT_NO_PACKAGE_REGISTRY @@ -320,6 +323,7 @@ Variables that Describe the System /variable/CMAKE_COMPILER_2005 /variable/CMAKE_HOST_APPLE /variable/CMAKE_HOST_BSD + /variable/CMAKE_HOST_EXECUTABLE_SUFFIX /variable/CMAKE_HOST_LINUX /variable/CMAKE_HOST_SOLARIS /variable/CMAKE_HOST_SYSTEM @@ -346,6 +350,7 @@ Variables that Describe the System /variable/MSVC_VERSION /variable/MSYS /variable/UNIX + /variable/WASI /variable/WIN32 /variable/WINCE /variable/WINDOWS_PHONE @@ -360,6 +365,7 @@ Variables that Control the Build :maxdepth: 1 /variable/CMAKE_ADSP_ROOT + /variable/CMAKE_AIX_SHARED_LIBRARY_ARCHIVE /variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS /variable/CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS /variable/CMAKE_ANDROID_API @@ -486,6 +492,7 @@ Variables that Control the Build /variable/CMAKE_LINK_GROUP_USING_FEATURE /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED /variable/CMAKE_LINK_INTERFACE_LIBRARIES + /variable/CMAKE_LINK_LIBRARIES_STRATEGY /variable/CMAKE_LINK_LIBRARY_FEATURE_ATTRIBUTES /variable/CMAKE_LINK_LIBRARY_FILE_FLAG /variable/CMAKE_LINK_LIBRARY_FLAG @@ -614,6 +621,7 @@ Variables for Languages /variable/CMAKE_LANG_COMPILER_TARGET /variable/CMAKE_LANG_COMPILER_VERSION /variable/CMAKE_LANG_CREATE_SHARED_LIBRARY + /variable/CMAKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE /variable/CMAKE_LANG_CREATE_SHARED_MODULE /variable/CMAKE_LANG_CREATE_STATIC_LIBRARY /variable/CMAKE_LANG_EXTENSIONS @@ -631,6 +639,8 @@ Variables for Languages /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT /variable/CMAKE_LANG_HOST_COMPILER + /variable/CMAKE_LANG_HOST_COMPILER_ID + /variable/CMAKE_LANG_HOST_COMPILER_VERSION /variable/CMAKE_LANG_IGNORE_EXTENSIONS /variable/CMAKE_LANG_IMPLICIT_INCLUDE_DIRECTORIES /variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES @@ -650,6 +660,7 @@ Variables for Languages /variable/CMAKE_LANG_STANDARD_INCLUDE_DIRECTORIES /variable/CMAKE_LANG_STANDARD_LATEST /variable/CMAKE_LANG_STANDARD_LIBRARIES + /variable/CMAKE_LANG_STANDARD_LINK_DIRECTORIES /variable/CMAKE_LANG_STANDARD_REQUIRED /variable/CMAKE_OBJC_EXTENSIONS /variable/CMAKE_OBJC_STANDARD diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index 48a921936..4610e1135 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -31,7 +31,7 @@ Synopsis cmake --find-package [] `Run a Workflow Preset`_ - cmake --workflow [] + cmake --workflow `View Help`_ cmake --help[-] @@ -232,6 +232,17 @@ Options will display also advanced variables. If ``H`` is specified, it will also display help for each variable. +.. option:: -LR[A][H] + + .. versionadded:: 3.31 + + Show specific non-advanced cached variables + + Show non-``INTERNAL`` nor :prop_cache:`ADVANCED` variables from the CMake + ``CACHE`` that match the given regex. If ``A`` is specified, then it + will also show advanced variables. If ``H`` is specified, it will also + display help for each variable. + .. option:: -N View mode only. @@ -255,8 +266,18 @@ Options from the top of a binary tree for a CMake project it will dump additional information such as the cache, log files etc. +.. option:: --print-config-dir + + .. versionadded:: 3.31 + + Print CMake config directory for user-wide FileAPI queries. + + See :envvar:`CMAKE_CONFIG_DIR` for more details. + .. option:: --log-level= + .. versionadded:: 3.16 + Set the log ````. The :command:`message` command will only output messages of the specified @@ -745,6 +766,15 @@ The options are: This option can be omitted if :envvar:`VERBOSE` environment variable is set. +.. option:: -j , --parallel + + .. versionadded:: 3.31 + + Install in parallel using the given number of jobs. Only available if + :prop_gbl:`INSTALL_PARALLEL` is enabled. The + :envvar:`CMAKE_INSTALL_PARALLEL_LEVEL` environment variable specifies a + default parallel level when this option is not provided. + Run :option:`cmake --install` with no options for quick help. Open a Project @@ -1364,7 +1394,7 @@ build steps in order: .. code-block:: shell - cmake --workflow [] + cmake --workflow The options are: @@ -1381,6 +1411,15 @@ The options are: must contain CMake preset files. See :manual:`preset ` for more details. + .. versionchanged:: 3.31 + When following immediately after the ``--workflow`` option, + the ``--preset`` argument can be omitted and just the ```` + name can be given. This means the following syntax is valid: + + .. code-block:: console + + $ cmake --workflow my-preset + .. option:: --list-presets Lists the available workflow presets. The current working directory must diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index c9ab31ead..928133923 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -1560,6 +1560,10 @@ Configuration settings include: * `CTest Script`_ variable: :variable:`CTEST_TLS_VERSION` * :module:`CTest` module variable: ``CTEST_TLS_VERSION`` + .. versionchanged:: 3.31 + The default is TLS 1.2. + Previously, no minimum version was enforced by default. + ``TLSVerify`` .. versionadded:: 3.30 @@ -1569,6 +1573,11 @@ Configuration settings include: * `CTest Script`_ variable: :variable:`CTEST_TLS_VERIFY` * :module:`CTest` module variable: ``CTEST_TLS_VERIFY`` + .. versionchanged:: 3.31 + The default is on. Previously, the default was off. + Users may set the :envvar:`CMAKE_TLS_VERIFY` environment + variable to ``0`` to restore the old default. + ``TriggerSite`` Legacy option. Not used. diff --git a/Help/manual/presets/example.json b/Help/manual/presets/example.json index 696ab4721..96fccb2e1 100644 --- a/Help/manual/presets/example.json +++ b/Help/manual/presets/example.json @@ -1,16 +1,22 @@ { - "version": 6, + "version": 10, "cmakeMinimumRequired": { "major": 3, "minor": 23, "patch": 0 }, + "$comment": "An example CMakePresets.json file", "include": [ "otherThings.json", "moreThings.json" ], "configurePresets": [ { + "$comment": [ + "This is a comment row.", + "This is another comment,", + "just because we can do it" + ], "name": "default", "displayName": "Default Config", "description": "Default build using Ninja generator", diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index f80685cb6..63fbdb7f5 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json @@ -9,7 +9,7 @@ "const": 1, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV1" } }, @@ -21,7 +21,7 @@ "const": 2, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV1" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV2" }, @@ -35,7 +35,7 @@ "const": 3, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV3" }, @@ -49,7 +49,7 @@ "const": 4, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -64,7 +64,7 @@ "const": 5, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -79,7 +79,7 @@ "const": 6, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -96,7 +96,7 @@ "const": 7, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -114,7 +114,7 @@ "const": 8, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -132,7 +132,7 @@ "const": 9, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV1" }, "vendor": { "$ref": "#/definitions/vendor" }, "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, @@ -142,6 +142,25 @@ "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false + }, + { + "properties": { + "$schema": { "$ref": "#/definitions/$schema" }, + "$comment": { "$ref": "#/definitions/$comment" }, + "version": { + "const": 10, + "description": "A required integer representing the version of the JSON schema." + }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV10" }, + "vendor": { "$ref": "#/definitions/vendor" }, + "configurePresets": { "$ref": "#/definitions/configurePresetsV10" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV10" }, + "testPresets": { "$ref": "#/definitions/testPresetsV10" }, + "packagePresets": { "$ref": "#/definitions/packagePresetsV10" }, + "workflowPresets": { "$ref": "#/definitions/workflowPresetsV10" }, + "include": { "$ref": "#/definitions/include" } + }, + "additionalProperties": false } ], "required": [ @@ -153,9 +172,33 @@ "description": "The schema against which to verify this document.", "format": "uri-reference" }, - "cmakeMinimumRequired": { + "$comment": { + "anyOf": [ + { + "type": "string", + "description": "The single-line comment" + }, + { + "type": "array", + "description": "The multi-line comment", + "minItems": 1, + "items": { + "type": "string", + "description": "One line of the multi-line comment" + } + } + ] + }, + "cmakeMinimumRequiredPropertiesV10": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "cmakeMinimumRequiredPropertiesV1": { "type": "object", - "description": "An optional object representing the minimum version of CMake needed to build this project.", + "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 1 and higher.", "properties": { "major": { "type": "integer", @@ -169,6 +212,33 @@ "type": "integer", "description": "An optional integer representing the patch version." } + } + }, + "cmakeMinimumRequiredV10": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/cmakeMinimumRequiredPropertiesV10" }, + { "$ref": "#/definitions/cmakeMinimumRequiredPropertiesV1" } + ], + "properties": { + "$comment": {}, + "major": {}, + "minor": {}, + "patch": {} + }, + "additionalProperties": false + }, + "cmakeMinimumRequiredV1": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project. Available in version 1 and higher.", + "allOf": [ + { "$ref": "#/definitions/cmakeMinimumRequiredPropertiesV1" } + ], + "properties": { + "major": {}, + "minor": {}, + "patch": {} }, "additionalProperties": false }, @@ -177,12 +247,264 @@ "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, the keys should be a vendor-specific domain name followed by a /-separated path. For example, the Example IDE 1.0 could use example.com/ExampleIDE/1.0. The value of each field can be anything desired by the vendor, though will typically be a map.", "properties": {} }, + "configurePresetsArchitectureAsStringV1": { + "type": "string", + "description": "An optional string representing the platform for generators that support it." + }, + "configurePresetsArchitectureAsObjectV10": { + "type": "object", + "description": "An optional object representing the platform for generators that support it. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "configurePresetsArchitectureAsObjectV1": { + "type": "object", + "description": "An optional object representing the platform for generators that support it. Available in version 1 and higher.", + "properties": { + "value": { + "type": "string", + "description": "An optional string representing the value." + }, + "strategy": { + "type": "string", + "description": "An optional string telling CMake how to handle the 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 architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", + "enum": [ + "set", + "external" + ] + } + } + }, + "configurePresetsArchitectureV10": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsArchitectureAsStringV1" }, + { + "type": "object", + "description": "An optional object representing the platform for generators that support it. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsArchitectureAsObjectV10" }, + { "$ref": "#/definitions/configurePresetsArchitectureAsObjectV1" } + ], + "properties": { + "$comment": {}, + "value": {}, + "strategy": {} + }, + "additionalProperties": false + } + ] + }, + "configurePresetsArchitectureV1": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsArchitectureAsStringV1" }, + { + "type": "object", + "description": "An optional object representing the platform for generators that support it. Available in version 1 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsArchitectureAsObjectV1" } + ], + "properties": { + "value": {}, + "strategy": {} + }, + "additionalProperties": false + } + ] + }, + "configurePresetsToolsetAsStringV1": { + "type": "string", + "description": "An optional string representing the toolset for generators that support it." + }, + "configurePresetsToolsetAsObjectV10": { + "type": "object", + "description": "An optional object representing the toolset for generators that support it. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "configurePresetsToolsetAsObjectV1": { + "type": "object", + "description": "An optional object representing the toolset for generators that support it. Available in version 1 and higher.", + "properties": { + "value": { + "type": "string", + "description": "An optional string representing the value." + }, + "strategy": { + "type": "string", + "description": "An optional string telling CMake how to handle the 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 architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", + "enum": [ + "set", + "external" + ] + } + } + }, + "configurePresetsToolsetV10": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsToolsetAsStringV1" }, + { + "type": "object", + "description": "An optional object representing the toolset for generators that support it. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsToolsetAsObjectV10" }, + { "$ref": "#/definitions/configurePresetsToolsetAsObjectV1" } + ], + "properties": { + "$comment": {}, + "value": {}, + "strategy": {} + }, + "additionalProperties": false + } + ] + }, + "configurePresetsToolsetV1": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsToolsetAsStringV1" }, + { + "type": "object", + "description": "An optional object representing the toolset for generators that support it. Available in version 1 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsToolsetAsObjectV1" } + ], + "properties": { + "value": {}, + "strategy": {} + }, + "additionalProperties": false + } + ] + }, + "configurePresetsCacheVariablesAdditionalPropertiesAsNullV1": { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + "configurePresetsCacheVariablesAdditionalPropertiesAsBooleanV1": { + "type": "boolean", + "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." + }, + "configurePresetsCacheVariablesAdditionalPropertiesAsStringV1": { + "type": "string", + "description": "A string representing the value of the variable (which supports macro expansion)." + }, + "configurePresetsCacheVariablesAdditionalPropertiesAsObjectV10": { + "type": "object", + "description": "An optional object representing the cache variables for generators that support it. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "configurePresetsCacheVariablesAdditionalPropertiesAsObjectV1": { + "type": "object", + "description": "An optional object representing the cache variables for generators that support it. Available in version 1 and higher.", + "properties": { + "type": { + "type": "string", + "description": "An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL." + }, + "value": { + "anyOf": [ + { + "type": "boolean", + "description": "A required boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." + }, + { + "type": "string", + "description": "A required string representing the value of the variable. This field supports macro expansion." + } + ] + } + } + }, + "configurePresetsCacheVariablesAdditionalPropertiesV10": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsNullV1" }, + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsBooleanV1" }, + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsStringV1" }, + { + "type": "object", + "description": "An object representing the type and value of the variable. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsObjectV10" }, + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsObjectV1" } + ], + "properties": { + "$comment": {}, + "type": {}, + "value": {} + }, + "required": [ + "value" + ], + "additionalProperties": false + } + ] + }, + "configurePresetsCacheVariablesAdditionalPropertiesV1": { + "anyOf": [ + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsNullV1" }, + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsBooleanV1" }, + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsStringV1" }, + { + "type": "object", + "description": "An object representing the type and value of the variable. Available in version 1 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesAsObjectV1" } + ], + "properties": { + "type": {}, + "value": {} + }, + "required": [ + "value" + ], + "additionalProperties": false + } + ] + }, + "configurePresetsItemsV10": { + "type": "array", + "description": "An optional array of configure preset objects. Available in version 10 and higher.", + "items": { + "type": "object", + "description": "A configure preset object. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "warnings": { + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "errors": { + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "debug": { + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "trace": { + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "graphviz": { + "type": "string", + "description": "An optional string specifying the path to graphviz dot file. Available in version 10 and higher." + } + } + } + }, "configurePresetsItemsV7": { "type": "array", - "description": "A configure preset object.", + "description": "An optional array of configure preset objects. Available in version 7 and higher.", "items": { "type": "object", - "description": "A configure preset object.", + "description": "A configure preset object. Available in version 7 and higher.", "properties": { "trace": { "type": "object", @@ -224,18 +546,17 @@ "type": "string", "description": "An optional string specifying a path to a trace output file." } - }, - "additionalProperties": false + } } } } }, "configurePresetsItemsV3": { "type": "array", - "description": "A configure preset object.", + "description": "An optional array of configure preset objects. Available in version 3 and higher.", "items": { "type": "object", - "description": "A configure preset object.", + "description": "A configure preset object. Available in version 3 and higher.", "properties": { "binaryDir": { "type": "string", @@ -252,17 +573,16 @@ "installDir": { "type": "string", "description": "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." - }, - "condition": { "$ref": "#/definitions/topCondition" } + } } } }, "configurePresetsItemsV1": { "type": "array", - "description": "An optional array of configure preset objects.", + "description": "An optional array of configure preset objects. Available in version 1 and higher.", "items": { "type": "object", - "description": "A configure preset object.", + "description": "A configure preset object. Available in version 1 and higher.", "properties": { "name": { "type": "string", @@ -308,60 +628,6 @@ "type": "string", "description": "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). 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." }, - "architecture": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the platform for generators that support it." - }, - { - "type": "object", - "description": "An optional object representing the platform for generators that support it.", - "properties": { - "value": { - "type": "string", - "description": "An optional string representing the value." - }, - "strategy": { - "type": "string", - "description": "An optional string telling CMake how to handle the 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 architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", - "enum": [ - "set", - "external" - ] - } - }, - "additionalProperties": false - } - ] - }, - "toolset": { - "anyOf": [ - { - "type": "string", - "description": "An optional string representing the toolset for generators that support it." - }, - { - "type": "object", - "description": "An optional object representing the toolset for generators that support it.", - "properties": { - "value": { - "type": "string", - "description": "An optional string representing the value." - }, - "strategy": { - "type": "string", - "description": "An optional string telling CMake how to handle the 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 architecture and toolset fields. In that case, CMake will ignore the field, but the IDE can use them to set up the environment before invoking CMake.", - "enum": [ - "set", - "external" - ] - } - }, - "additionalProperties": false - } - ] - }, "binaryDir": { "type": "string", "description": "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 specified, it must be inherited from the inherits preset (unless this preset is hidden)." @@ -374,48 +640,6 @@ "type": "object", "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", "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": "boolean", - "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." - }, - { - "type": "string", - "description": "A string representing the value of the variable (which supports macro expansion)." - }, - { - "type": "object", - "description": "An object representing the type and value of the variable.", - "properties": { - "type": { - "type": "string", - "description": "An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL." - }, - "value": { - "anyOf": [ - { - "type": "boolean", - "description": "A required boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." - }, - { - "type": "string", - "description": "A required string representing the value of the variable. This field supports macro expansion." - } - ] - } - }, - "required": [ - "value" - ], - "additionalProperties": false - } - ] - }, "propertyNames": { "pattern": "^.+$" } @@ -464,8 +688,7 @@ "type": "boolean", "description": "An optional boolean. Setting this to true is equivalent to passing --check-system-vars on the command line." } - }, - "additionalProperties": false + } }, "errors": { "type": "object", @@ -479,8 +702,7 @@ "type": "boolean", "description": "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." } - }, - "additionalProperties": false + } }, "debug": { "type": "object", @@ -498,22 +720,23 @@ "type": "boolean", "description": "An optional boolean. Setting this to true is equivalent to passing --debug-find on the command line." } - }, - "additionalProperties": false + } } } } }, - "configurePresetsV7": { + "configurePresetsV10": { "type": "array", - "description": "An optional array of configure preset objects.", + "description": "An optional array of configure preset objects. Available in version 10 and higher.", "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV1" }, + { "$ref": "#/definitions/configurePresetsItemsV10" }, + { "$ref": "#/definitions/configurePresetsItemsV7" }, { "$ref": "#/definitions/configurePresetsItemsV3" }, - { "$ref": "#/definitions/configurePresetsItemsV7" } + { "$ref": "#/definitions/configurePresetsItemsV1" } ], "items": { "properties": { + "$comment": {}, "name": {}, "hidden": {}, "inherits": {}, @@ -521,35 +744,141 @@ "displayName": {}, "description": {}, "generator": {}, - "architecture": {}, - "toolset": {}, + "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV10" }, + "toolset": { "$ref": "#/definitions/configurePresetsToolsetV10" }, "toolchainFile": {}, + "graphviz": {}, "binaryDir": {}, "installDir": {}, "cmakeExecutable": {}, - "cacheVariables": {}, + "cacheVariables": { + "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV10" } + }, "environment": {}, - "warnings": {}, - "errors": {}, - "debug": {}, - "condition": {}, - "trace": {} - }, - "required": [ - "name" - ], - "additionalProperties": false - } - }, - "configurePresetsV3": { - "type": "array", - "description": "An optional array of configure preset objects.", - "allOf": [ - { "$ref": "#/definitions/configurePresetsItemsV1" }, - { "$ref": "#/definitions/configurePresetsItemsV3" } - ], - "items": { - "properties": { + "warnings": { + "properties": { + "$comment": {}, + "dev": {}, + "deprecated": {}, + "uninitialized": {}, + "unusedCli": {}, + "systemVars": {} + }, + "additionalProperties": false + }, + "errors": { + "properties": { + "$comment": {}, + "dev": {}, + "deprecated": {} + }, + "additionalProperties": false + }, + "debug": { + "properties": { + "$comment": {}, + "output": {}, + "tryCompile": {}, + "find": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV10" }, + "trace": { + "properties": { + "$comment": {}, + "mode": {}, + "format": {}, + "source": {}, + "redirect": {} + }, + "additionalProperties": false + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "configurePresetsV7": { + "type": "array", + "description": "An optional array of configure preset objects. Available in version 7 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsItemsV7" }, + { "$ref": "#/definitions/configurePresetsItemsV3" }, + { "$ref": "#/definitions/configurePresetsItemsV1" } + ], + "items": { + "properties": { + "name": {}, + "hidden": {}, + "inherits": {}, + "vendor": {}, + "displayName": {}, + "description": {}, + "generator": {}, + "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV1" }, + "toolset": { "$ref": "#/definitions/configurePresetsToolsetV1" }, + "toolchainFile": {}, + "binaryDir": {}, + "installDir": {}, + "cmakeExecutable": {}, + "cacheVariables": { + "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV1" } + }, + "environment": {}, + "warnings": { + "properties": { + "dev": {}, + "deprecated": {}, + "uninitialized": {}, + "unusedCli": {}, + "systemVars": {} + }, + "additionalProperties": false + }, + "errors": { + "properties": { + "dev": {}, + "deprecated": {} + }, + "additionalProperties": false + }, + "debug": { + "properties": { + "output": {}, + "tryCompile": {}, + "find": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV3" }, + "trace": { + "properties": { + "mode": {}, + "format": {}, + "source": {}, + "redirect": {} + }, + "additionalProperties": false + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "configurePresetsV3": { + "type": "array", + "description": "An optional array of configure preset objects. Available in version 3 and higher.", + "allOf": [ + { "$ref": "#/definitions/configurePresetsItemsV3" }, + { "$ref": "#/definitions/configurePresetsItemsV1" } + ], + "items": { + "properties": { "name": {}, "hidden": {}, "inherits": {}, @@ -557,18 +886,42 @@ "displayName": {}, "description": {}, "generator": {}, - "architecture": {}, - "toolset": {}, + "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV1" }, + "toolset": { "$ref": "#/definitions/configurePresetsToolsetV1" }, "toolchainFile": {}, "binaryDir": {}, "installDir": {}, "cmakeExecutable": {}, - "cacheVariables": {}, + "cacheVariables": { + "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV1" } + }, "environment": {}, - "warnings": {}, - "errors": {}, - "debug": {}, - "condition": {} + "warnings": { + "properties": { + "dev": {}, + "deprecated": {}, + "uninitialized": {}, + "unusedCli": {}, + "systemVars": {} + }, + "additionalProperties": false + }, + "errors": { + "properties": { + "dev": {}, + "deprecated": {} + }, + "additionalProperties": false + }, + "debug": { + "properties": { + "output": {}, + "tryCompile": {}, + "find": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -578,7 +931,7 @@ }, "configurePresetsV1": { "type": "array", - "description": "An optional array of configure preset objects.", + "description": "An optional array of configure preset objects. Available in version 1 and higher.", "allOf": [ { "$ref": "#/definitions/configurePresetsItemsV1" } ], @@ -591,15 +944,39 @@ "displayName": {}, "description": {}, "generator": {}, - "architecture": {}, - "toolset": {}, + "architecture": { "$ref": "#/definitions/configurePresetsArchitectureV1" }, + "toolset": { "$ref": "#/definitions/configurePresetsToolsetV1" }, "binaryDir": {}, "cmakeExecutable": {}, - "cacheVariables": {}, + "cacheVariables": { + "additionalProperties": { "$ref": "#/definitions/configurePresetsCacheVariablesAdditionalPropertiesV1" } + }, "environment": {}, - "warnings": {}, - "errors": {}, - "debug": {} + "warnings": { + "properties": { + "dev": {}, + "deprecated": {}, + "uninitialized": {}, + "unusedCli": {}, + "systemVars": {} + }, + "additionalProperties": false + }, + "errors": { + "properties": { + "dev": {}, + "deprecated": {} + }, + "additionalProperties": false + }, + "debug": { + "properties": { + "output": {}, + "tryCompile": {}, + "find": {} + }, + "additionalProperties": false + } }, "required": [ "name" @@ -607,6 +984,16 @@ "additionalProperties": false } }, + "buildPresetsItemsV10": { + "type": "array", + "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 10 and higher.", + "items": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + } + }, "buildPresetsItemsV4": { "type": "array", "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 4 and higher.", @@ -625,13 +1012,7 @@ }, "buildPresetsItemsV3": { "type": "array", - "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 3 and higher.", - "items": { - "type": "object", - "properties": { - "condition": { "$ref": "#/definitions/topCondition" } - } - } + "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 3 and higher." }, "buildPresetsItemsV2": { "type": "array", @@ -748,10 +1129,44 @@ "description": "An optional string representing an option to pass after -- on the command line." } } + } + } + }, + "buildPresetsV10": { + "type": "array", + "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/buildPresetsItemsV10" }, + { "$ref": "#/definitions/buildPresetsItemsV4" }, + { "$ref": "#/definitions/buildPresetsItemsV3" }, + { "$ref": "#/definitions/buildPresetsItemsV2" } + ], + "items": { + "type": "object", + "properties": { + "$comment": {}, + "name": {}, + "hidden": {}, + "inherits": {}, + "configurePreset": {}, + "vendor": {}, + "displayName": {}, + "description": {}, + "inheritConfigureEnvironment": {}, + "environment": {}, + "jobs": {}, + "targets": {}, + "configuration": {}, + "cleanFirst": {}, + "resolvePackageReferences": {}, + "verbose": {}, + "nativeToolOptions": {}, + "condition": { "$ref": "#/definitions/topConditionV10" } }, "required": [ "name" - ] + ], + "additionalProperties": false } }, "buildPresetsV4": { @@ -781,7 +1196,7 @@ "resolvePackageReferences": {}, "verbose": {}, "nativeToolOptions": {}, - "condition": {} + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -814,7 +1229,7 @@ "cleanFirst": {}, "verbose": {}, "nativeToolOptions": {}, - "condition": {} + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -853,6 +1268,129 @@ "additionalProperties": false } }, + "testPresetsFilterIncludeIndexAsStringV2": { + "type": "string", + "description": "An optional string specifying a file with the command line syntax for --tests-information. Available in version 2 and higher." + }, + "testPresetsFilterIncludeIndexAsObjectV10": { + "type": "object", + "description": "An optional object specifying tests to include by test index. Available in version 10 and higher.", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "testPresetsFilterIncludeIndexAsObjectV2": { + "type": "object", + "description": "An optional object specifying tests to include by test index. Available in version 2 and higher.", + "properties": { + "start": { + "type": "integer", + "description": "An optional integer specifying a test index to start testing at." + }, + "end": { + "type": "integer", + "description": "An optional integer specifying a test index to stop testing at." + }, + "stride": { + "type": "integer", + "description": "An optional integer specifying the increment." + }, + "specificTests": { + "type": "array", + "description": "An optional array of integers specifying specific test indices to run.", + "items": { + "type": "integer", + "description": "An integer specifying the test to run by index." + } + } + } + }, + "testPresetsFilterIncludeIndexV10": { + "anyOf": [ + { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsStringV2" }, + { + "type": "object", + "description": "An optional object specifying test preset filters. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsObjectV10" }, + { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsObjectV2" } + ], + "properties": { + "$comment": {}, + "start": {}, + "end": {}, + "stride": {}, + "specificTests": {} + }, + "additionalProperties": false + } + ] + }, + "testPresetsFilterIncludeIndexV2": { + "anyOf": [ + { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsStringV2" }, + { + "type": "object", + "description": "An optional object specifying test preset filters. Available in version 2 and higher.", + "allOf": [ + { "$ref": "#/definitions/testPresetsFilterIncludeIndexAsObjectV2" } + ], + "properties": { + "start": {}, + "end": {}, + "stride": {}, + "specificTests": {} + }, + "additionalProperties": false + } + ] + }, + "testPresetsItemsV10": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 10 and higher.", + "items": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "filter": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "include": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "exclude": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "fixtures": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + } + } + } + } + }, + "execution": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "repeat": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + } + } + } + } + } + }, "testPresetsItemsV6": { "type": "array", "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 6 and higher.", @@ -896,13 +1434,7 @@ }, "testPresetsItemsV3": { "type": "array", - "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 3 and higher.", - "items": { - "type": "object", - "properties": { - "condition": { "$ref": "#/definitions/topCondition" } - } - } + "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 3 and higher." }, "testPresetsItemsV2": { "type": "array", @@ -1060,47 +1592,11 @@ "type": "string", "description": "An optional string specifying a regex for test labels. Equivalent to passing --label-regex on the command line." }, - "index": { - "anyOf": [ - { - "type": "object", - "description": "An optional object specifying tests to include by test index.", - "properties": { - "start": { - "type": "integer", - "description": "An optional integer specifying a test index to start testing at." - }, - "end": { - "type": "integer", - "description": "An optional integer specifying a test index to stop testing at." - }, - "stride": { - "type": "integer", - "description": "An optional integer specifying the increment." - }, - "specificTests": { - "type": "array", - "description": "An optional array of integers specifying specific test indices to run.", - "items": { - "type": "integer", - "description": "An integer specifying the test to run by index." - } - } - }, - "additionalProperties": false - }, - { - "type": "string", - "description": "An optional string specifying a file with the command line syntax for --tests-information." - } - ] - }, "useUnion": { "type": "boolean", "description": "An optional boolean. Equivalent to passing --union on the command line." } - }, - "additionalProperties": false + } }, "exclude": { "type": "object", @@ -1130,13 +1626,11 @@ "type": "string", "description": "An optional string specifying a regex for text fixtures to exclude from adding cleanup tests. Equivalent to --fixture-exclude-cleanup on the command line." } - }, - "additionalProperties": false + } } } } - }, - "additionalProperties": false + } }, "execution": { "type": "object", @@ -1184,11 +1678,7 @@ "type": "integer", "description": "A required integer." } - }, - "required": [ - "mode", "count" - ], - "additionalProperties": false + } }, "interactiveDebugging": { "type": "boolean", @@ -1209,23 +1699,137 @@ "default", "error", "ignore" ] } + } + } + } + } + }, + "testPresetsV10": { + "type": "array", + "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/testPresetsItemsV10" }, + { "$ref": "#/definitions/testPresetsItemsV6" }, + { "$ref": "#/definitions/testPresetsItemsV5" }, + { "$ref": "#/definitions/testPresetsItemsV3" }, + { "$ref": "#/definitions/testPresetsItemsV2" } + ], + "items": { + "type": "object", + "properties": { + "$comment": {}, + "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": { + "type": "object", + "properties": { + "$comment": {}, + "include": { + "type": "object", + "properties": { + "$comment": {}, + "name": {}, + "label": {}, + "useUnion": {}, + "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV10" } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "properties": { + "$comment": {}, + "name": {}, + "label": {}, + "fixtures": { + "type": "object", + "properties": { + "$comment": {}, + "any": {}, + "setup": {}, + "cleanup": {} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "$comment": {}, + "stopOnFailure": {}, + "enableFailover": {}, + "jobs": {}, + "resourceSpecFile": {}, + "testLoad": {}, + "showOnly": {}, + "repeat": { + "type": "object", + "properties": { + "$comment": {}, + "mode": {}, + "count": {} + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": {}, + "scheduleRandom": {}, + "timeout": {}, + "noTestsAction": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV10" } }, "required": [ "name" - ] + ], + "additionalProperties": false } }, "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/testPresetsItemsV6" }, { "$ref": "#/definitions/testPresetsItemsV5" }, - { "$ref": "#/definitions/testPresetsItemsV6" } + { "$ref": "#/definitions/testPresetsItemsV3" }, + { "$ref": "#/definitions/testPresetsItemsV2" } ], "items": { "type": "object", @@ -1260,9 +1864,67 @@ }, "additionalProperties": false }, - "filter": {}, - "execution": {}, - "condition": {} + "filter": { + "type": "object", + "properties": { + "include": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "useUnion": {}, + "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV2" } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "fixtures": { + "type": "object", + "properties": { + "any": {}, + "setup": {}, + "cleanup": {} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "stopOnFailure": {}, + "enableFailover": {}, + "jobs": {}, + "resourceSpecFile": {}, + "testLoad": {}, + "showOnly": {}, + "repeat": { + "type": "object", + "properties": { + "mode": {}, + "count": {} + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": {}, + "scheduleRandom": {}, + "timeout": {}, + "noTestsAction": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -1274,9 +1936,9 @@ "type": "array", "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 5 and higher.", "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, + { "$ref": "#/definitions/testPresetsItemsV5" }, { "$ref": "#/definitions/testPresetsItemsV3" }, - { "$ref": "#/definitions/testPresetsItemsV5" } + { "$ref": "#/definitions/testPresetsItemsV2" } ], "items": { "type": "object", @@ -1310,9 +1972,67 @@ }, "additionalProperties": false }, - "filter": {}, - "execution": {}, - "condition": {} + "filter": { + "type": "object", + "properties": { + "include": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "useUnion": {}, + "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV2" } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "fixtures": { + "type": "object", + "properties": { + "any": {}, + "setup": {}, + "cleanup": {} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "stopOnFailure": {}, + "enableFailover": {}, + "jobs": {}, + "resourceSpecFile": {}, + "testLoad": {}, + "showOnly": {}, + "repeat": { + "type": "object", + "properties": { + "mode": {}, + "count": {} + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": {}, + "scheduleRandom": {}, + "timeout": {}, + "noTestsAction": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -1324,8 +2044,8 @@ "type": "array", "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 3 and higher.", "allOf": [ - { "$ref": "#/definitions/testPresetsItemsV2" }, - { "$ref": "#/definitions/testPresetsItemsV3" } + { "$ref": "#/definitions/testPresetsItemsV3" }, + { "$ref": "#/definitions/testPresetsItemsV2" } ], "items": { "type": "object", @@ -1358,9 +2078,67 @@ }, "additionalProperties": false }, - "filter": {}, - "execution": {}, - "condition": {} + "filter": { + "type": "object", + "properties": { + "include": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "useUnion": {}, + "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV2" } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "fixtures": { + "type": "object", + "properties": { + "any": {}, + "setup": {}, + "cleanup": {} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "stopOnFailure": {}, + "enableFailover": {}, + "jobs": {}, + "resourceSpecFile": {}, + "testLoad": {}, + "showOnly": {}, + "repeat": { + "type": "object", + "properties": { + "mode": {}, + "count": {} + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": {}, + "scheduleRandom": {}, + "timeout": {}, + "noTestsAction": {} + }, + "additionalProperties": false + }, + "condition": { "$ref": "#/definitions/topConditionV3" } }, "required": [ "name" @@ -1405,8 +2183,66 @@ }, "additionalProperties": false }, - "filter": {}, - "execution": {} + "filter": { + "type": "object", + "properties": { + "include": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "useUnion": {}, + "index": { "$ref": "#/definitions/testPresetsFilterIncludeIndexV2" } + }, + "additionalProperties": false + }, + "exclude": { + "type": "object", + "properties": { + "name": {}, + "label": {}, + "fixtures": { + "type": "object", + "properties": { + "any": {}, + "setup": {}, + "cleanup": {} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "stopOnFailure": {}, + "enableFailover": {}, + "jobs": {}, + "resourceSpecFile": {}, + "testLoad": {}, + "showOnly": {}, + "repeat": { + "type": "object", + "properties": { + "mode": {}, + "count": {} + }, + "required": [ + "mode", "count" + ], + "additionalProperties": false + }, + "interactiveDebugging": {}, + "scheduleRandom": {}, + "timeout": {}, + "noTestsAction": {} + }, + "additionalProperties": false + } }, "required": [ "name" @@ -1414,9 +2250,25 @@ "additionalProperties": false } }, - "packagePresetsItemsV6": { + "packagePresetsItemsV10": { "type": "array", - "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 6 and higher.", + "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 10 and higher.", + "items": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "output": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + } + } + } + }, + "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": { @@ -1489,7 +2341,6 @@ "pattern": "^.+$" } }, - "condition": { "$ref": "#/definitions/topCondition" }, "generators": { "type": "array", "description": "An optional list of strings representing generators for CPack to use.", @@ -1530,8 +2381,7 @@ "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", @@ -1549,10 +2399,52 @@ "type": "string", "description": "An optional string representing the vendor name." } + } + } + }, + "packagePresetsV10": { + "type": "array", + "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/packagePresetsItemsV10" }, + { "$ref": "#/definitions/packagePresetsItemsV6" } + ], + "items": { + "type": "object", + "properties": { + "$comment": {}, + "name": {}, + "hidden": {}, + "inherits": {}, + "configurePreset": {}, + "vendor": {}, + "displayName": {}, + "description": {}, + "inheritConfigureEnvironment": {}, + "environment": {}, + "condition": { "$ref": "#/definitions/topConditionV10" }, + "generators": {}, + "configurations": {}, + "variables": {}, + "configFile": {}, + "output": { + "type": "object", + "properties": { + "$comment": {}, + "debug": {}, + "verbose": {} + }, + "additionalProperties": false + }, + "packageName": {}, + "packageVersion": {}, + "packageDirectory": {}, + "vendorName": {} }, "required": [ "name" - ] + ], + "additionalProperties": false } }, "packagePresetsV6": { @@ -1573,12 +2465,19 @@ "description": {}, "inheritConfigureEnvironment": {}, "environment": {}, - "condition": {}, + "condition": { "$ref": "#/definitions/topConditionV3" }, "generators": {}, "configurations": {}, "variables": {}, "configFile": {}, - "output": {}, + "output": { + "type": "object", + "properties": { + "debug": {}, + "verbose": {} + }, + "additionalProperties": false + }, "packageName": {}, "packageVersion": {}, "packageDirectory": {}, @@ -1590,6 +2489,25 @@ "additionalProperties": false } }, + "workflowPresetsItemsV10": { + "type": "array", + "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 10 and higher.", + "items": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" }, + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + } + } + } + } + }, "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.", @@ -1630,6 +2548,35 @@ "description": "A required string representing the name of the configure, build, test, or package preset to run as this workflow step.", "minLength": 1 } + } + } + } + } + } + }, + "workflowPresetsV10": { + "type": "array", + "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 10 and higher.", + "allOf": [ + { "$ref": "#/definitions/workflowPresetsItemsV10" }, + { "$ref": "#/definitions/workflowPresetsItemsV6" } + ], + "items": { + "type": "object", + "properties": { + "$comment": {}, + "name": {}, + "vendor": {}, + "displayName": {}, + "description": {}, + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "$comment": {}, + "type": {}, + "name": {} }, "required": [ "type", @@ -1659,7 +2606,21 @@ "vendor": {}, "displayName": {}, "description": {}, - "steps": {} + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": {}, + "name": {} + }, + "required": [ + "type", + "name" + ], + "additionalProperties": false + } + } }, "required": [ "name", @@ -1668,24 +2629,154 @@ "additionalProperties": false } }, - "condition": { - "anyOf": [ - { + "conditionAsBooleanV3": { + "type": "boolean", + "description": "A boolean which provides a constant value for the condition's evaluation." + }, + "conditionAsObjectConstV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectConstV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "const": "const" + }, + "value": { "type": "boolean", - "description": "A boolean which provides a constant value for the condition's evaluation." + "description": "A required boolean which provides a constant value for the condition's evaluation." + } + } + }, + "conditionAsObjectEqualsV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectEqualsV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "enum": [ "equals", "notEquals" ] }, + "lhs": { + "type": "string", + "description": "First string to compare. This field supports macro expansion." + }, + "rhs": { + "type": "string", + "description": "Second string to compare. This field supports macro expansion." + } + } + }, + "conditionAsObjectInListV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectInListV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "enum": [ "inList", "notInList" ] + }, + "string": { + "type": "string", + "description": "A required string to search for. This field supports macro expansion." + }, + "list": { + "type": "array", + "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.", + "items": { + "type": "string" + } + } + } + }, + "conditionAsObjectMatchesV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectMatchesV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "enum": [ "matches", "notMatches" ] + }, + "string": { + "type": "string", + "description": "A required string to search. This field supports macro expansion." + }, + "regex": { + "type": "string", + "description": "A required regular expression to search for. This field supports macro expansion." + } + } + }, + "conditionAsObjectAggregationV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectAggregationV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "enum": [ "anyOf", "allOf" ] + }, + "conditions": { + "type": "array", + "description": "A required array of condition objects. These conditions use short-circuit evaluation." + } + } + }, + "conditionAsObjectNotV10": { + "type": "object", + "properties": { + "$comment": { "$ref": "#/definitions/$comment" } + } + }, + "conditionAsObjectNotV3": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A required string specifying the type of the condition.", + "const": "not" + } + } + }, + "conditionV10": { + "anyOf": [ + { "$ref": "#/definitions/conditionAsBooleanV3" }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectConstV10" }, + { "$ref": "#/definitions/conditionAsObjectConstV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "const" - }, - "value": { - "type": "boolean", - "description": "A required boolean which provides a constant value for the condition's evaluation." - } + "$comment": {}, + "type": {}, + "value": {} }, "required": [ "type", @@ -1695,20 +2786,15 @@ }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectEqualsV10" }, + { "$ref": "#/definitions/conditionAsObjectEqualsV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "equals" - }, - "lhs": { - "type": "string", - "description": "First string to compare. This field supports macro expansion." - }, - "rhs": { - "type": "string", - "description": "Second string to compare. This field supports macro expansion." - } + "$comment": {}, + "type": {}, + "lhs": {}, + "rhs": {} }, "required": [ "type", @@ -1719,162 +2805,160 @@ }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectInListV10" }, + { "$ref": "#/definitions/conditionAsObjectInListV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "notEquals" - }, - "lhs": { - "type": "string", - "description": "First string to compare. This field supports macro expansion." - }, - "rhs": { - "type": "string", - "description": "Second string to compare. This field supports macro expansion." - } + "$comment": {}, + "type": {}, + "string": {}, + "list": {} }, "required": [ "type", - "lhs", - "rhs" + "string", + "list" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectMatchesV10" }, + { "$ref": "#/definitions/conditionAsObjectMatchesV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "inList" - }, - "string": { - "type": "string", - "description": "A required string to search for. This field supports macro expansion." - }, - "list": { - "type": "array", - "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.", - "items": { - "type": "string" - } - } + "$comment": {}, + "type": {}, + "string": {}, + "regex": {} }, "required": [ "type", "string", - "list" + "regex" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectAggregationV10" }, + { "$ref": "#/definitions/conditionAsObjectAggregationV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "notInList" - }, - "string": { - "type": "string", - "description": "A required string to search for. This field supports macro expansion." - }, - "list": { + "$comment": {}, + "type": {}, + "conditions": { "type": "array", - "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.", - "items": { - "type": "string" - } + "items": { "$ref": "#/definitions/conditionV10" } } }, "required": [ "type", - "string", - "list" + "conditions" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectNotV10" }, + { "$ref": "#/definitions/conditionAsObjectNotV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "matches" - }, - "string": { - "type": "string", - "description": "A required string to search. This field supports macro expansion." - }, - "regex": { - "type": "string", - "description": "A required regular expression to search for. This field supports macro expansion." - } + "$comment": {}, + "type": {}, + "condition": { "$ref": "#/definitions/conditionV10" } }, "required": [ "type", - "string", - "regex" + "condition" + ], + "additionalProperties": false + } + ] + }, + "conditionV3": { + "anyOf": [ + { "$ref": "#/definitions/conditionAsBooleanV3" }, + { + "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectConstV3" } + ], + "properties": { + "type": {}, + "value": {} + }, + "required": [ + "type", + "value" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectEqualsV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "notMatches" - }, - "string": { - "type": "string", - "description": "A required string to search. This field supports macro expansion." - }, - "regex": { - "type": "string", - "description": "A required regular expression to search for. This field supports macro expansion." - } + "type": {}, + "lhs": {}, + "rhs": {} + }, + "required": [ + "type", + "lhs", + "rhs" + ], + "additionalProperties": false + }, + { + "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectInListV3" } + ], + "properties": { + "type": {}, + "string": {}, + "list": {} }, "required": [ "type", "string", - "regex" + "list" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectMatchesV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "anyOf" - }, - "conditions": { - "type": "array", - "description": "A required array of condition objects. These conditions use short-circuit evaluation.", - "items": { "$ref": "#/definitions/condition" } - } + "type": {}, + "string": {}, + "regex": {} }, "required": [ "type", - "conditions" + "string", + "regex" ], "additionalProperties": false }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectAggregationV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "allOf" - }, + "type": {}, "conditions": { "type": "array", - "description": "A required array of condition objects. These conditions use short-circuit evaluation.", - "items": { "$ref": "#/definitions/condition" } + "items": { "$ref": "#/definitions/conditionV3" } } }, "required": [ @@ -1885,13 +2969,12 @@ }, { "type": "object", + "allOf": [ + { "$ref": "#/definitions/conditionAsObjectNotV3" } + ], "properties": { - "type": { - "type": "string", - "description": "A required string specifying the type of the condition.", - "const": "not" - }, - "condition": { "$ref": "#/definitions/condition" } + "type": {}, + "condition": { "$ref": "#/definitions/conditionV3" } }, "required": [ "type", @@ -1901,13 +2984,20 @@ } ] }, - "topCondition": { + "topConditionAsNullV3": { + "type": "null", + "description": "Null indicates that the condition always evaluates to true and is not inherited." + }, + "topConditionV10": { "anyOf": [ - { "$ref": "#/definitions/condition" }, - { - "type": "null", - "description": "Null indicates that the condition always evaluates to true and is not inherited." - } + { "$ref": "#/definitions/conditionV10" }, + { "$ref": "#/definitions/topConditionAsNullV3" } + ] + }, + "topConditionV3": { + "anyOf": [ + { "$ref": "#/definitions/conditionV3" }, + { "$ref": "#/definitions/topConditionAsNullV3" } ] }, "include": { diff --git a/Help/module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt b/Help/module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt new file mode 100644 index 000000000..35f433b7b --- /dev/null +++ b/Help/module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt @@ -0,0 +1,5 @@ + ``CMAKE_REQUIRED_LINK_DIRECTORIES`` + .. versionadded:: 3.31 + + A :ref:`;-list ` of libraries search paths to pass to + the linker (see :command:`try_compile` for further details). diff --git a/Help/policy/CMP0156.rst b/Help/policy/CMP0156.rst index d2ce291e2..7956742ae 100644 --- a/Help/policy/CMP0156.rst +++ b/Help/policy/CMP0156.rst @@ -34,6 +34,11 @@ are de-duplicated by keeping their first occurrence, thus respecting the project-specified order. This policy provides compatibility with projects that have not been updated to expect the latter behavior. +.. note:: + + When this policy is set to ``NEW``, the policy :policy:`CMP0179` controls + which occurrence of the static libraries is kept when they are de-duplicated. + The ``OLD`` behavior for this policy is to always repeat static libraries as if using a traditional linker, and always de-duplicate shared libraries by keeping the last occurrence of each. The ``NEW`` behavior for this policy diff --git a/Help/policy/CMP0171.rst b/Help/policy/CMP0171.rst new file mode 100644 index 000000000..c364bf4a7 --- /dev/null +++ b/Help/policy/CMP0171.rst @@ -0,0 +1,26 @@ +CMP0171 +------- + +.. versionadded:: 3.31 + +``codegen`` is a reserved target name. + +CMake 3.30 and earlier did not reserve ``codegen`` as a builtin target name, +leaving projects free to create their own target with that name. +CMake 3.31 and later prefer to reserve ``codegen`` as a builtin target name +to drive custom commands created with the ``CODEGEN`` option to +:command:`add_custom_command`. In order to support building the ``codegen`` +target in scripted environments, e.g., ``cmake --build . --target codegen``, +the ``codegen`` target needs to be generated even if no custom commands +use the ``CODEGEN`` option. This policy provides compatibility for projects +that have not been updated to avoid creating a target named ``codegen``. + +The ``OLD`` behavior of this policy allows projects to create a target +with the name ``codegen``. The ``NEW`` behavior halts with a fatal error +if a target with the name ``codegen`` is created. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0172.rst b/Help/policy/CMP0172.rst new file mode 100644 index 000000000..c31697579 --- /dev/null +++ b/Help/policy/CMP0172.rst @@ -0,0 +1,39 @@ +CMP0172 +------- + +.. versionadded:: 3.31 + +The :module:`CPack` module enables per-machine installation by default +in the :cpack_gen:`CPack WIX Generator`. + +The :cpack_gen:`CPack WIX Generator`'s :variable:`CPACK_WIX_INSTALL_SCOPE` +option controls the scope of the generated Windows Installer package. +When :variable:`CPACK_WIX_VERSION` is set to 4 or higher, the default scope +is ``perMachine``. However, when using WIX 3 the default scope is ``NONE``, +and CPack does not set any ``InstallScope`` in the package specification. +The resulting installer requires administrative privileges and installs +into the system-wide ``ProgramFiles`` directory, but the start menu entry +and uninstaller registration are created only for the current user. + +The :module:`CPack` module in CMake 3.30 and older does not specify any +:variable:`CPACK_WIX_INSTALL_SCOPE` value by default, so CPack uses no +installation scope by default with WIX 3. CMake 3.31 and newer instead +prefer to set :variable:`CPACK_WIX_INSTALL_SCOPE` to ``perMachine`` by +default to make the behavior consistent across all WIX versions. This +policy provides compatibility for projects that have not been updated +to expect ``perMachine`` behavior. + +The ``OLD`` behavior for this policy is to not set +:variable:`CPACK_WIX_INSTALL_SCOPE` by default. The ``NEW`` behavior for +this policy is to set :variable:`CPACK_WIX_INSTALL_SCOPE` to ``perMachine`` +by default. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default +.. include:: STANDARD_ADVICE.txt + +See documentation of the +:variable:`CMAKE_POLICY_WARNING_CMP0172 >` +variable to control the warning. + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0173.rst b/Help/policy/CMP0173.rst new file mode 100644 index 000000000..30de19f58 --- /dev/null +++ b/Help/policy/CMP0173.rst @@ -0,0 +1,22 @@ +CMP0173 +------- + +.. versionadded:: 3.31 + +The :module:`CMakeFindFrameworks` module is removed. + +CMake's framework handling has evolved well beyond what the +``CMakeFindFrameworks`` module supports. The module lacks any handling of +XCFrameworks, it never documented the one command it provides, and +:command:`find_library` provides superior capabilities in all respects. + +The ``OLD`` behavior of this policy is for :module:`CMakeFindFrameworks` to +continue to provide the undocumented ``cmake_find_frameworks()`` command. +The ``NEW`` behavior halts with a fatal error if anything tries to include +the module. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0174.rst b/Help/policy/CMP0174.rst new file mode 100644 index 000000000..748fdba9d --- /dev/null +++ b/Help/policy/CMP0174.rst @@ -0,0 +1,37 @@ +CMP0174 +------- + +.. versionadded:: 3.31 + +:command:`cmake_parse_arguments(PARSE_ARGV)` defines a variable for an empty +string after a single-value keyword. + +One of the main reasons for using the ``PARSE_ARGV`` form of the +:command:`cmake_parse_arguments` command is to more robustly handle corner +cases related to empty values. The non-``PARSE_ARGV`` form doesn't preserve +empty arguments, but the ``PARSE_ARGV`` form does. For each single-value +keyword given, a variable should be defined if the keyword is present, even +if it is followed by an empty string. + +Prior to CMake 3.31, no variable would be defined if the value given after a +single-value keyword was an empty string. This meant the code could not detect +the difference between the keyword not being given, and it being given but with +an empty value, except by iterating over all the arguments and checking if the +keyword is present. + +For the ``OLD`` behavior of this policy, +:command:`cmake_parse_arguments(PARSE_ARGV)` does not define a variable for a +single-value keyword followed by an empty string, or followed by no value at +all. + +For the ``NEW`` behavior, :command:`cmake_parse_arguments(PARSE_ARGV)` always +defines a variable for each keyword given in the arguments, even a single-value +keyword with an empty string as its value or no value at all. With the +``NEW`` behavior, the code can robustly check if a single-value keyword was +given using just ``if(DEFINED _)``. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0175.rst b/Help/policy/CMP0175.rst new file mode 100644 index 000000000..f2c372d6e --- /dev/null +++ b/Help/policy/CMP0175.rst @@ -0,0 +1,40 @@ +CMP0175 +------- + +.. versionadded:: 3.31 + +:command:`add_custom_command` rejects invalid arguments. + +CMake 3.30 and earlier silently ignored unsupported keywords and missing or +invalid arguments for the different forms of the :command:`add_custom_command` +command. CMake 3.31 implements more rigorous argument checking and will flag +invalid or missing arguments as errors. + +The ``OLD`` behavior of this policy will accept the same invalid keywords or +arguments as CMake 3.30 and earlier. The ``NEW`` behavior will flag the +following as errors that previously went unreported: + +* The ``OUTPUT`` form does not accept ``PRE_BUILD``, ``PRE_LINK``, or + ``POST_BUILD`` keywords. +* When the ``APPEND`` keyword is given, the ``OUTPUT`` form also does not + accept ``BYPRODUCTS``, ``COMMAND_EXPAND_LISTS``, ``DEPENDS_EXPLICIT_ONLY``, + ``DEPFILE``, ``JOB_POOL``, ``JOB_SERVER_AWARE``, ``USES_TERMINAL``, or + ``VERBATIM`` keywords. +* The ``TARGET`` form requires exactly one of ``PRE_BUILD``, ``PRE_LINK``, or + ``POST_BUILD`` to be given. Previously, if none were given, ``POST_BUILD`` + was assumed, or if multiple keywords were given, the last one was used. +* The ``TARGET`` form does not accept ``DEPENDS``, ``DEPENDS_EXPLICIT_ONLY``, + ``DEPFILE``, ``IMPLICIT_DEPENDS``, ``MAIN_DEPENDENCY``, ``JOB_POOL``, + ``JOB_SERVER_AWARE``, or ``USES_TERMINAL`` keywords. +* The ``TARGET`` form now requires at least one ``COMMAND`` to be given. +* If a keyword expects a value to be given after it, but no value is provided, + that was previously treated as though the keyword was not given at all. +* The ``COMMENT`` keyword expects exactly one value after it. If multiple + values are given, or if the ``COMMENT`` keyword is given more than once, + this is an error. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0176.rst b/Help/policy/CMP0176.rst new file mode 100644 index 000000000..46831748a --- /dev/null +++ b/Help/policy/CMP0176.rst @@ -0,0 +1,27 @@ +CMP0176 +------- + +.. versionadded:: 3.31 + +:command:`execute_process` ``ENCODING`` is ``UTF-8`` by default. + +The ``ENCODING`` option is meaningful only on Windows. It specifies the +character encoding expected in the process's output on stdout and stderr. +In CMake 3.14 and below the default encoding was ``NONE``, which corresponds +to CMake's internal UTF-8 encoding. In CMake 3.15 through CMake 3.30 the +default encoding was accidentally changed to ``AUTO``, but the change went +unnoticed and was not documented. + +CMake 3.31 and above prefer the ``ENCODING`` default to be ``UTF-8``. +This policy provides compatibility with projects that may have been +relying on the default being ``AUTO``. + +The ``OLD`` behavior of this policy is for :command:`execute_process` +to use ``AUTO`` by default if no ``ENCODING`` is specified. The ``NEW`` +behavior for this policy is to use ``UTF-8`` as the default ``ENCODING``. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0177.rst b/Help/policy/CMP0177.rst new file mode 100644 index 000000000..3eca9f3b3 --- /dev/null +++ b/Help/policy/CMP0177.rst @@ -0,0 +1,38 @@ +CMP0177 +------- + +.. versionadded:: 3.31 + +:command:`install` ``DESTINATION`` paths are normalized. + +The :command:`install` command has a number of different forms, and most of +them take a ``DESTINATION`` keyword, some in more than one place. +CMake 3.30 and earlier used the value given after the ``DESTINATION`` keyword +as provided with no transformations. The :command:`install(EXPORT)` form +assumes the path contains no ``..`` or ``.`` path components when computing +a path relative to the ``DESTINATION``, and if the project provided a path +that violated that assumption, the computed path would be incorrect. + +CMake 3.31 normalizes all ``DESTINATION`` values given in any form of the +:command:`install` command, except for the ``INCLUDES DESTINATION`` of the +:command:`install(TARGETS)` form. The normalization performed is the same +as for the :command:`cmake_path` command (see :ref:`Normalization`). + +The ``OLD`` behavior of this policy performs no translation on the +``DESTINATION`` values of any :command:`install` command. They are used +exactly as provided. If a destination path contains ``..`` or ``.`` path +components, :command:`install(EXPORT)` will use the same wrong paths as +CMake 3.30 and earlier. + +The ``NEW`` behavior will normalize all ``DESTINATION`` values except for +``INCLUDES DESTINATION``. If a destination path contains a generator +expression, it will be wrapped in a ``$`` +generator expression. + +This policy was introduced in CMake version 3.31. +It may be set by :command:`cmake_policy` or :command:`cmake_minimum_required`. +If it is not set, CMake will warn if it detects a path that would be different +if normalized, and uses ``OLD`` behavior. If a destination path contains a +generator expression, no such warning will be issued regardless of the value. + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0178.rst b/Help/policy/CMP0178.rst new file mode 100644 index 000000000..f6152ebd6 --- /dev/null +++ b/Help/policy/CMP0178.rst @@ -0,0 +1,37 @@ +CMP0178 +------- + +.. versionadded:: 3.31 + +Test command lines preserve empty arguments. + +Empty values in the :prop_tgt:`TEST_LAUNCHER` and +:prop_tgt:`CROSSCOMPILING_EMULATOR` target properties are now preserved +for tests added by the following: + +* The :command:`add_test` command. +* The :command:`ExternalData_Add_Test` command from the :module:`ExternalData` + module. +* The :command:`gtest_add_tests` or :command:`gtest_discover_tests` commands + from the :module:`GoogleTest` module. + +For the :command:`gtest_add_tests` and :command:`gtest_discover_tests` +commands, empty elements in the values passed after the ``EXTRA_ARGS`` +keyword are also now preserved. + +The ``OLD`` behavior of this policy silently discards empty list items +from the :prop_tgt:`TEST_LAUNCHER` and :prop_tgt:`CROSSCOMPILING_EMULATOR` +target properties in the above-mentioned cases. It also silently discards +empty items from the values given after ``EXTRA_ARGS`` for the +:command:`gtest_add_tests` and :command:`gtest_discover_tests` commands. + +The ``NEW`` behavior of this policy preserves empty list items in the +:prop_tgt:`TEST_LAUNCHER` and :prop_tgt:`CROSSCOMPILING_EMULATOR` target +properties, and in values given after ``EXTRA_ARGS`` for +:command:`gtest_add_tests` and :command:`gtest_discover_tests`. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: warns +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0179.rst b/Help/policy/CMP0179.rst new file mode 100644 index 000000000..7c9498f11 --- /dev/null +++ b/Help/policy/CMP0179.rst @@ -0,0 +1,28 @@ +CMP0179 +------- + +.. versionadded:: 3.31 + +De-duplication of static libraries on link lines keeps first occurrence. +This policy is only relevant when policy :policy:`CMP0156` is set to ``NEW``. + +Based on the linker capabilities, the static libraries can +be de-duplicated. See policy :policy:`CMP0156` for more information. + +CMake 3.30 and below may choose to keep, on some platforms, the last occurrence +of the static libraries rather than the fist occurrence when they are +de-duplicated. + +CMake 3.31 and above prefer to keep, on all platforms, the first occurrence of +the static libraries when they are de-duplicated. + +The ``OLD`` behavior for this policy is to keep, on some platforms, the last +occurrence of the static libraries when they are de-duplicated. The ``NEW`` +behavior for this policy is to keep the first occurrence of the static +libraries when they are de-duplicated, regardless of the platform. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/policy/CMP0180.rst b/Help/policy/CMP0180.rst new file mode 100644 index 000000000..f69c0bcc4 --- /dev/null +++ b/Help/policy/CMP0180.rst @@ -0,0 +1,36 @@ +CMP0180 +------- + +.. versionadded:: 3.31 + +:command:`project` always sets ``_*`` as normal variables. + +In CMake 3.29 and below, the :command:`project` command set +:variable:`_SOURCE_DIR`, :variable:`_BINARY_DIR`, +and :variable:`_IS_TOP_LEVEL` as cache entries, but not as +normal variables. CMake 3.30 started setting them as normal variables, +but only if they are already set as normal variables. This was needed to +preserve support for some :module:`FetchContent` use cases under policy +:policy:`CMP0169`'s NEW behavior, while also preserving behavior of nested +directories that call :command:`project` with the same project name. +See release notes for 3.30.3, 3.30.4, and 3.30.5 for details. + +CMake 3.31 and later prefer to always set ``_SOURCE_DIR``, +``_BINARY_DIR``, and ``_IS_TOP_LEVEL``, as both +cache entries and normal variables, regardless of what cache or normal +variables already exist. This policy provides compatibility for projects +that have not been updated to expect this behavior. + +The ``OLD`` behavior for this policy will only set normal variables for +``_SOURCE_DIR``, ``_BINARY_DIR``, and +``_IS_TOP_LEVEL`` if there is already a normal variable by that +name when :command:`project` is called. +The ``NEW`` behavior for this policy will always set normal variables for +``_SOURCE_DIR``, ``_BINARY_DIR``, and +``_IS_TOP_LEVEL`` when :command:`project` is called. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.31 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: STANDARD_ADVICE.txt + +.. include:: DEPRECATED.txt diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst index c36306df5..5b326734f 100644 --- a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst +++ b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst @@ -17,7 +17,7 @@ pairs. Each such pair will be transformed into an entry in the solution global section. Whitespace around key and value is ignored. List elements which do not contain an equal sign are skipped. -This property only works for Visual Studio 12 and above; it is ignored +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. The property only applies when set on a directory whose ``CMakeLists.txt`` contains a :command:`project` command. diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst index c775ad526..179f1bbfe 100644 --- a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst +++ b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst @@ -17,6 +17,6 @@ pairs. Each such pair will be transformed into an entry in the solution global section. Whitespace around key and value is ignored. List elements which do not contain an equal sign are skipped. -This property only works for Visual Studio 12 and above; it is ignored +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. The property only applies when set on a directory whose ``CMakeLists.txt`` contains a :command:`project` command. diff --git a/Help/prop_gbl/INSTALL_PARALLEL.rst b/Help/prop_gbl/INSTALL_PARALLEL.rst index 7b6632ec3..c30d6c9b8 100644 --- a/Help/prop_gbl/INSTALL_PARALLEL.rst +++ b/Help/prop_gbl/INSTALL_PARALLEL.rst @@ -3,18 +3,22 @@ INSTALL_PARALLEL .. versionadded:: 3.30 -Enables parallel installation option for the Ninja generator. +Enables parallel installation option for a project. The install code for each +subdirectory added with ``add_subdirectory`` can run independently. -When this property is ``ON``, ``install/local`` targets have the -console pool disabled, allowing them to run concurrently. +When using the Ninja generator, setting this property to ``ON``, causes +``install/local`` targets have the console pool disabled, allowing them to run +concurrently. This property also provides the target ``install/parallel``, which has an -explicit dependency on the ``install/local`` target for each subdirectory, -recursing down the project. +explicit dependency on the ``install/local`` target for each subdirectory. -Setting this property has no affect on the behavior of ``cmake --install``. -The install must be invoked by building the ``install/parallel`` target -directly. + .. versionadded:: 3.31 + + When this property is ``ON``, ``cmake --install`` can be given the ``-j `` + or ``--parallel `` option to specify a maximum number of jobs. + The :envvar:`CMAKE_INSTALL_PARALLEL_LEVEL` environment variable specifies a + default parallel level if this option is not provided. Calls to :command:`install(CODE)` or :command:`install(SCRIPT)` might depend on actions performed by an earlier :command:`install` command in a different diff --git a/Help/prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE.rst b/Help/prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE.rst new file mode 100644 index 000000000..d0106c56f --- /dev/null +++ b/Help/prop_tgt/AIX_SHARED_LIBRARY_ARCHIVE.rst @@ -0,0 +1,20 @@ +AIX_SHARED_LIBRARY_ARCHIVE +-------------------------- + +.. versionadded:: 3.31 + +On AIX, enable creation of a shared library archive. This places +the shared object ``.so`` file inside an archive ``.a`` file. + +By default, CMake creates shared libraries on AIX as plain +shared object ``.so`` files for consistency with other UNIX platforms. +Alternatively, set this property to a true value to create a shared +library archive instead, as is AIX convention. + +The shared object name in the archive encodes version information from +the :prop_tgt:`SOVERSION` target property, if set, and otherwise from +the :prop_tgt:`VERSION` target property, if set. + +This property defaults to :variable:`CMAKE_AIX_SHARED_LIBRARY_ARCHIVE` +if that variable is set when a ``SHARED`` library target is created +by :command:`add_library`. diff --git a/Help/prop_tgt/COMPILE_FLAGS.rst b/Help/prop_tgt/COMPILE_FLAGS.rst index 8fe651b4a..5229d467d 100644 --- a/Help/prop_tgt/COMPILE_FLAGS.rst +++ b/Help/prop_tgt/COMPILE_FLAGS.rst @@ -7,5 +7,8 @@ The ``COMPILE_FLAGS`` property sets additional compiler flags used to build sources within the target. Use :prop_tgt:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions. -This property is deprecated. Use the :prop_tgt:`COMPILE_OPTIONS` -property or the :command:`target_compile_options` command instead. +.. note:: + + This property has been superseded by the :prop_tgt:`COMPILE_OPTIONS` property. + Alternatively, you can also use the :command:`target_compile_options` command + instead. diff --git a/Help/prop_tgt/EXPORT_BUILD_DATABASE.rst b/Help/prop_tgt/EXPORT_BUILD_DATABASE.rst new file mode 100644 index 000000000..6f68b47d0 --- /dev/null +++ b/Help/prop_tgt/EXPORT_BUILD_DATABASE.rst @@ -0,0 +1,15 @@ +EXPORT_BUILD_DATABASE +--------------------- + +.. versionadded:: 3.31 + +Enable/Disable output of a build database for a target. + +This property is initialized by the value of the variable +:variable:`CMAKE_EXPORT_BUILD_DATABASE` if it is set when a target is created. + +.. note :: + + This property is meaningful only when experimental support for build + databases has been enabled by the + ``CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE`` gate. diff --git a/Help/prop_tgt/LINK_LIBRARIES.rst b/Help/prop_tgt/LINK_LIBRARIES.rst index b5c1d8907..b449aa151 100644 --- a/Help/prop_tgt/LINK_LIBRARIES.rst +++ b/Help/prop_tgt/LINK_LIBRARIES.rst @@ -28,3 +28,8 @@ In advanced use cases, the list of direct link dependencies specified by this property may be updated by usage requirements from dependencies. See the :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` and :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE` target properties. + +See the :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and +corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target property +for details on how CMake orders direct link dependencies on linker +command lines. diff --git a/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst b/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst new file mode 100644 index 000000000..f8db75015 --- /dev/null +++ b/Help/prop_tgt/LINK_LIBRARIES_STRATEGY.rst @@ -0,0 +1,87 @@ +LINK_LIBRARIES_STRATEGY +----------------------- + +.. versionadded:: 3.31 + +Specify a strategy for ordering a target's direct link dependencies +on linker command lines. This property is initialized by the value of the +:variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable if it is set when a +target is created. + +CMake generates a target's link line using its :ref:`Target Link Properties`. +In particular, the :prop_tgt:`LINK_LIBRARIES` target property records the +target's direct link dependencies, typically populated by calls to +:command:`target_link_libraries`. Indirect link dependencies are +propagated from those entries of :prop_tgt:`LINK_LIBRARIES` that name +library targets by following the transitive closure of their +:prop_tgt:`INTERFACE_LINK_LIBRARIES` properties. CMake supports multiple +strategies for nominally ordering direct and indirect link dependencies, +which are then filtered for `Toolchain-Specific Behavior`_. + +Consider this example for the strategies below: + +.. code-block:: cmake + + add_library(A STATIC ...) + add_library(B STATIC ...) + add_library(C STATIC ...) + add_executable(main ...) + target_link_libraries(B PRIVATE A) + target_link_libraries(C PRIVATE A) + target_link_libraries(main PRIVATE A B C) + +The supported strategies are: + +``REORDER_MINIMALLY`` + Entries of :prop_tgt:`LINK_LIBRARIES` always appear first and in their + original order. Indirect link dependencies not satisfied by the + original entries may be reordered and de-duplicated with respect to + one another, but are always appended after the original entries. + This may result in less efficient link lines, but gives projects + control of ordering among independent entries. Such control may be + important when intermixing link flags with libraries, or when multiple + libraries provide a given symbol. + + This is the default. + + In the above example, this strategy computes a link line for ``main`` + by starting with its original entries ``A B C``, and then appends + another ``A`` to satisfy the dependencies of ``B`` and ``C`` on ``A``. + The nominal order produced by this strategy is ``A B C A``. + + Note that additional filtering for `Toolchain-Specific Behavior`_ + may de-duplicate ``A`` on the actual linker invocation in the + generated build system, resulting in either ``A B C`` or ``B C A``. + +``REORDER_FREELY`` + Entries of :prop_tgt:`LINK_LIBRARIES` may be reordered, de-duplicated, + and intermixed with indirect link dependencies. This may result in + more efficient link lines, but does not give projects any control of + ordering among independent entries. + + In the above example, this strategy computes a link line for ``main`` + by re-ordering its original entries ``A B C`` to satisfy the + dependencies of ``B`` and ``C`` on ``A``. + The nominal order produced by this strategy is ``B C A``. + +Toolchain-Specific Behavior +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +After one of the above strategies produces a nominal order among +direct and indirect link dependencies, the actual linker invocation +in the generated build system may de-duplicate entries based on +platform-specific requirements and linker capabilities. +See policy :policy:`CMP0156`. + +For example, if the ``REORDER_MINIMALLY`` strategy produces ``A B C A``, +the actual link line may de-duplicate ``A`` as follows: + +* If ``A`` is a static library and the linker re-scans automatically, + the first occurrence is kept, resulting in ``A B C``. + See policy :policy:`CMP0179` + +* If ``A`` is a shared library on Windows, the first + occurrence is kept, resulting in ``A B C``. + +* If ``A`` is a shared library on macOS or UNIX platforms, the last + occurrence is kept, resulting in ``B C A``. diff --git a/Help/prop_tgt/MACOSX_FRAMEWORK_INFO_PLIST.rst b/Help/prop_tgt/MACOSX_FRAMEWORK_INFO_PLIST.rst index 82fdcc0fb..a1f7c6660 100644 --- a/Help/prop_tgt/MACOSX_FRAMEWORK_INFO_PLIST.rst +++ b/Help/prop_tgt/MACOSX_FRAMEWORK_INFO_PLIST.rst @@ -12,12 +12,20 @@ file name which may be a full path. The following target properties may be set to specify content to be configured into the file: +``MACOSX_FRAMEWORK_BUNDLE_NAME`` + .. versionadded:: 3.31 + + Sets ``CFBundleName``. + ``MACOSX_FRAMEWORK_BUNDLE_VERSION`` Sets ``CFBundleVersion``. + ``MACOSX_FRAMEWORK_ICON_FILE`` Sets ``CFBundleIconFile``. + ``MACOSX_FRAMEWORK_IDENTIFIER`` Sets ``CFBundleIdentifier``. + ``MACOSX_FRAMEWORK_SHORT_VERSION_STRING`` Sets ``CFBundleShortVersionString``. diff --git a/Help/prop_tgt/UNITY_BUILD.rst b/Help/prop_tgt/UNITY_BUILD.rst index 577b0c9bf..ee001713e 100644 --- a/Help/prop_tgt/UNITY_BUILD.rst +++ b/Help/prop_tgt/UNITY_BUILD.rst @@ -38,6 +38,9 @@ Unity builds are supported for the following languages: ``CXX`` .. versionadded:: 3.16 +``CUDA`` + .. versionadded:: 3.31 + ``OBJC`` .. versionadded:: 3.29 diff --git a/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst b/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst index 8c136f24c..75ff8502a 100644 --- a/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst +++ b/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst @@ -11,5 +11,5 @@ project file. This property is initialized by the value of the variable :variable:`CMAKE_VS_DEBUGGER_COMMAND` if it is set when a target is created. -This property only works for Visual Studio 12 2013 and above; +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. diff --git a/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst b/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst index 2656826aa..f49cb7064 100644 --- a/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst +++ b/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst @@ -11,5 +11,5 @@ project file. This property is initialized by the value of the variable :variable:`CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS` if it is set when a target is created. -This property only works for Visual Studio 12 2013 and above; +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. diff --git a/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst b/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst index d78d594ef..0352cd3ba 100644 --- a/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst +++ b/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst @@ -11,5 +11,5 @@ project file. This property is initialized by the value of the variable :variable:`CMAKE_VS_DEBUGGER_ENVIRONMENT` if it is set when a target is created. -This property only works for Visual Studio 12 2013 and above; +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. diff --git a/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst b/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst index 1026dfae8..dbf544267 100644 --- a/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst +++ b/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst @@ -11,5 +11,5 @@ project file. This property is initialized by the value of the variable :variable:`CMAKE_VS_DEBUGGER_WORKING_DIRECTORY` if it is set when a target is created. -This property only works for Visual Studio 12 2013 and above; +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. diff --git a/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst b/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst index eeb7ddad2..8c69fdcb7 100644 --- a/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst +++ b/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst @@ -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 12 2013 and above; +This property only works for :ref:`Visual Studio Generators`; it is ignored on other generators. .. code-block:: cmake diff --git a/Help/prop_tgt/VS_FRAMEWORK_REFERENCES.rst b/Help/prop_tgt/VS_FRAMEWORK_REFERENCES.rst new file mode 100644 index 000000000..925ac68c3 --- /dev/null +++ b/Help/prop_tgt/VS_FRAMEWORK_REFERENCES.rst @@ -0,0 +1,12 @@ +VS_FRAMEWORK_REFERENCES +----------------------- + +.. versionadded:: 3.31 + +Visual Studio framework references. +Specify a :ref:`semicolon-separated list ` of framework references +to be added to a generated Visual Studio project. For example: + +* "Microsoft.WindowsDesktop.App.WPF" for WPF applications +* "Microsoft.WindowsDesktop.App.WindowsForms" for WinForms applications +* "Microsoft.WindowsDesktop.App" for applications using both frameworks diff --git a/Help/prop_tgt/VS_KEYWORD.rst b/Help/prop_tgt/VS_KEYWORD.rst index b2ce78a69..8fbebfe06 100644 --- a/Help/prop_tgt/VS_KEYWORD.rst +++ b/Help/prop_tgt/VS_KEYWORD.rst @@ -6,4 +6,4 @@ for the :generator:`Visual Studio 9 2008` generator, and older, but all of those generators have been removed. Use the :prop_tgt:`VS_GLOBAL_KEYWORD` target property to set the -keyword for Visual Studio 12 (2013) and newer. +keyword for remaining :ref:`Visual Studio Generators`. diff --git a/Help/release/3.29.rst b/Help/release/3.29.rst index e6717d9fd..7d08b5468 100644 --- a/Help/release/3.29.rst +++ b/Help/release/3.29.rst @@ -64,6 +64,10 @@ Commands * The :command:`if` command gained new tests ``IS_READABLE``, ``IS_WRITABLE`` and ``IS_EXECUTABLE`` to check file or directory permissions. +* The :command:`try_compile` and :command:`try_run` commands gained a + ``LINKER_LANGUAGE`` option to specify the :prop_tgt:`LINKER_LANGUAGE` + target property in the generated test project. + Variables --------- diff --git a/Help/release/3.31.rst b/Help/release/3.31.rst new file mode 100644 index 000000000..606fb951f --- /dev/null +++ b/Help/release/3.31.rst @@ -0,0 +1,259 @@ +CMake 3.31 Release Notes +************************ + +.. only:: html + + .. contents:: + +Changes made since CMake 3.30 include the following. + +New Features +============ + +Presets +------- + +* :manual:`cmake-presets(7)` files may now include comments using the key + ``$comment`` at any level within the JSON object to provide documentation. + +* :manual:`cmake-presets(7)` files may now request graphviz output using + the ``graphviz`` key in a configure preset. + +Generators +---------- + +* The :ref:`Ninja Generators` and :ref:`Makefile Generators` now produce + a ``codegen`` build target. See policy :policy:`CMP0171`. It drives a + subset of the build graph sufficient to run custom commands created with + :command:`add_custom_command`'s new ``CODEGEN`` option. + +Command-Line +------------ + +* The :option:`cmake --workflow` mode now accepts a preset name as the first + argument, allowing the simpler command line + :option:`cmake --workflow \ `. + +* The :option:`cmake -LR[A][H]` option was added to list cache entries + whose names match a regular expression. + +Compilers +--------- + +* The LFortran compiler is now supported with + :variable:`compiler id _COMPILER_ID>` ``LFortran``. + +Commands +-------- + +* The :command:`add_custom_command` command gained a ``CODEGEN`` option + to mark a custom command's outputs as dependencies of a ``codegen`` target. + See policy :policy:`CMP0171`. + +* The :command:`cmake_pkg_config` command was added as an endpoint for using + CMake's native pkg-config format parser. The only supported option in this + release is ``EXTRACT``, which provides low-level access to the values + produced by parsing a pkg-config file. For most users, this is not yet a + suitable replacement for the :module:`FindPkgConfig` module. + +* The :command:`file(ARCHIVE_CREATE)` command gained a ``WORKING_DIRECTORY`` + option to specify a working directory for the archiving process. + +* The :command:`file(MAKE_DIRECTORY)` command gained a ``RESULT`` option + to capture failure in a result variable. + +* The :command:`install(FILES)` and :command:`install(DIRECTORY)` commands' + ``TYPE`` argument gained support for a ``LIBEXEC`` type. + +Variables +--------- + +* The :variable:`CMAKE_AIX_SHARED_LIBRARY_ARCHIVE` variable and corresponding + :prop_tgt:`AIX_SHARED_LIBRARY_ARCHIVE` target property were added to + create shared libraries on AIX as shared library archives. + +* The :variable:`CMAKE_EXPORT_BUILD_DATABASE` variable, a corresponding + :envvar:`CMAKE_EXPORT_BUILD_DATABASE` environment variable, and an + :prop_tgt:`EXPORT_BUILD_DATABASE` target property, were added to + enable exporting C++ module compile commands. + This is only supported with :ref:`Ninja Generators`. + +* The :variable:`CMAKE_HOST_EXECUTABLE_SUFFIX` variable was added to + provide the suffix for executable names on the host platform. + +* The :variable:`CMAKE__HOST_COMPILER_ID` and + :variable:`CMAKE__HOST_COMPILER_VERSION` variables were added, + where ```` is either ``CUDA`` or ``HIP``. They are populated + when :variable:`CMAKE__COMPILER_ID` is ``NVIDIA`` to identify + NVCC's host compiler. + +* The :variable:`CMAKE__STANDARD_LINK_DIRECTORIES` variable was added. + Toolchain files can set this variable to control which link library directory + paths are always passed to the compiler for the specified language. + +* The :variable:`CMAKE_LINK_LIBRARIES_STRATEGY` variable and + corresponding :prop_tgt:`LINK_LIBRARIES_STRATEGY` target + property were added to optionally specify the strategy + CMake uses to generate link lines. + +Properties +---------- + +* The :prop_tgt:`MACOSX_FRAMEWORK_BUNDLE_NAME ` + target property was added to set the ``CFBundleName`` key in an Apple + :prop_tgt:`FRAMEWORK`'s ``Info.plist`` file. + +* The :prop_tgt:`UNITY_BUILD` target property now supports the + ``CUDA`` language. + +* The :prop_tgt:`VS_FRAMEWORK_REFERENCES` target property was added + to tell :ref:`Visual Studio Generators` to add framework references. + +Modules +------- + +* Check modules now support a ``CMAKE_REQUIRED_LINK_DIRECTORIES`` variable. + The following modules gained this support: + + * :module:`CMakePushCheckState` + * :module:`CheckCCompilerFlag` + * :module:`CheckCSourceCompiles` + * :module:`CheckCSourceRuns` + * :module:`CheckCXXCompilerFlag` + * :module:`CheckCXXSourceCompiles` + * :module:`CheckCXXSourceRuns` + * :module:`CheckCXXSymbolExists` + * :module:`CheckCompilerFlag` + * :module:`CheckFortranCompilerFlag` + * :module:`CheckFortranFunctionExists` + * :module:`CheckFortranSourceCompiles` + * :module:`CheckFortranSourceRuns` + * :module:`CheckFunctionExists` + * :module:`CheckIncludeFile` + * :module:`CheckIncludeFileCXX` + * :module:`CheckIncludeFiles` + * :module:`CheckOBJCCompilerFlag` + * :module:`CheckLibraryExists` + * :module:`CheckOBJCCompilerFlag` + * :module:`CheckOBJCSourceCompiles` + * :module:`CheckOBJCSourceRuns` + * :module:`CheckOBJCXXCompilerFlag` + * :module:`CheckOBJCXXSourceCompiles` + * :module:`CheckOBJCXXSourceRuns` + * :module:`CheckPrototypeDefinition` + * :module:`CheckSourceCompiles` + * :module:`CheckSourceRuns` + * :module:`CheckStructHasMember` + * :module:`CheckSymbolExists` + * :module:`CheckTypeSize` + * :module:`CheckVariableExists` + +* The :module:`CMakePackageConfigHelpers` module's + :command:`generate_apple_platform_selection_file` function + gained support for iOS Mac Catalyst. + +* The :module:`GoogleTest` module :command:`gtest_discover_tests` command + gained a new ``DISCOVERY_EXTRA_ARGS`` keyword. It allows extra arguments + to be appended to the command line when querying for the list of tests. + +* The :module:`FindCUDAToolkit` module now provides a ``CUDA::nvml_static`` + target. + +* The :module:`FindOpenMP` module gained support for the ``CUDA`` language. + +CTest +----- + +* The :command:`ctest_submit` command and :option:`ctest -T Submit ` + step now verify TLS server certificates for connections to ``https://`` URLs + by default. See the :variable:`CTEST_TLS_VERIFY` variable for details. + +* The :command:`ctest_submit` command and :option:`ctest -T Submit ` + step now require TLS 1.2 or higher for connections to ``https://`` URLs by + default. See the :variable:`CTEST_TLS_VERSION` variable for details. + +CPack +----- + +* The :cpack_gen:`CPack DEB Generator` gained a + :variable:`CPACK_DEBIAN_PACKAGE_MULTIARCH` option + to support multi-arch packages. + +* The :cpack_gen:`CPack IFW Generator` gained the new + :variable:`CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS` variable to + specify images associated with entries of + :variable:`CPACK_IFW_PACKAGE_PRODUCT_IMAGES`. + This feature is available for QtIFW 4.0 and newer. + +* The :cpack_gen:`CPack RPM Generator` gained support for ``zstd`` as a + :variable:`CPACK_RPM_COMPRESSION_TYPE` value. + +* The :module:`CPack` module enables per-machine installation by default + in the :cpack_gen:`CPack WIX Generator`. See policy :policy:`CMP0172` + and the :variable:`CPACK_WIX_INSTALL_SCOPE` variable. + +Deprecated and Removed Features +=============================== + +* Compatibility with versions of CMake older than 3.10 is now deprecated + and will be removed from a future version. Calls to + :command:`cmake_minimum_required` or :command:`cmake_policy` that set + the policy version to an older value now issue a deprecation diagnostic. + +* The :module:`CMakeFindFrameworks` module has been deprecated via + :policy:`CMP0173`. Projects should use :command:`find_library` instead. + +* The :generator:`Visual Studio 12 2013` generator has been removed. + +Other Changes +============= + +* When static libraries on link lines are de-duplicated (by policy + :policy:`CMP0156`), the first occurrence is now kept on all platforms. + See policy :policy:`CMP0179`. + +* Empty list elements in the :prop_tgt:`TEST_LAUNCHER` and + :prop_tgt:`CROSSCOMPILING_EMULATOR` target properties are now preserved by: + + * The :command:`add_test` command. + * The :command:`ExternalData_Add_Test` command from the + :module:`ExternalData` module. + * The :command:`gtest_add_tests` and :command:`gtest_discover_tests` + commands from the :module:`GoogleTest` module. + Empty list elements after the ``EXTRA_ARGS`` keyword of these + two commands are also now preserved. + + See policy :policy:`CMP0178`. + +* The :command:`execute_process` command's ``ENCODING`` option, + meaningful on Windows, now defaults to ``UTF-8``. + See policy :policy:`CMP0176`. + +* The :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands now + verify TLS server certificates for connections to ``https://`` URLs by + default. See the :variable:`CMAKE_TLS_VERIFY` variable for details. + This change was made without a policy so that users are protected + even when building projects that have not been updated. + Users may set the :envvar:`CMAKE_TLS_VERIFY` environment + variable to ``0`` to restore the old default. + +* The :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands now + require TLS 1.2 or higher for connections to ``https://`` URLs by default. + See the :variable:`CMAKE_TLS_VERSION` variable for details. + +* The :command:`file(GET_RUNTIME_DEPENDENCIES)` command was updated + to more closely match the dynamic loader's behavior on Linux. + +* The :command:`install` command's ``DESTINATION`` arguments are + now :ref:`normalized `, with the exception + of ``INCLUDES DESTINATION`` arguments in :command:`install(TARGETS)`. + See policy :policy:`CMP0177`. + +* The :command:`project` command now always sets + :variable:`_SOURCE_DIR`, :variable:`_BINARY_DIR`, + and :variable:`_IS_TOP_LEVEL` as both normal variables and + cache entries. See policy :policy:`CMP0180`. + +* The :command:`cmake_parse_arguments(PARSE_ARGV)` command now defines a + variable for an empty string after a single-value keyword. See policy + :policy:`CMP0174`. diff --git a/Help/release/index.rst b/Help/release/index.rst index 56bbf50cb..40767b703 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst @@ -13,6 +13,7 @@ Releases .. toctree:: :maxdepth: 1 + 3.31 <3.31> 3.30 <3.30> 3.29 <3.29> 3.28 <3.28> diff --git a/Help/variable/CMAKE_AIX_SHARED_LIBRARY_ARCHIVE.rst b/Help/variable/CMAKE_AIX_SHARED_LIBRARY_ARCHIVE.rst new file mode 100644 index 000000000..10fdf043c --- /dev/null +++ b/Help/variable/CMAKE_AIX_SHARED_LIBRARY_ARCHIVE.rst @@ -0,0 +1,10 @@ +CMAKE_AIX_SHARED_LIBRARY_ARCHIVE +-------------------------------- + +.. versionadded:: 3.31 + +On AIX, enable creation of shared library archives. + +This variable initializes the :prop_tgt:`AIX_SHARED_LIBRARY_ARCHIVE` +target property on ``SHARED`` library targets as they are created +by :command:`add_library`. See that target property for details. diff --git a/Help/variable/CMAKE_CFG_INTDIR.rst b/Help/variable/CMAKE_CFG_INTDIR.rst index 5a1f9e899..677538de8 100644 --- a/Help/variable/CMAKE_CFG_INTDIR.rst +++ b/Help/variable/CMAKE_CFG_INTDIR.rst @@ -18,7 +18,7 @@ Example values: :: - $(Configuration) = Visual Studio 12 and above + $(Configuration) = Visual Studio $(CONFIGURATION) = Xcode . = Make-based tools . = Ninja diff --git a/Help/variable/CMAKE_EXECUTABLE_SUFFIX.rst b/Help/variable/CMAKE_EXECUTABLE_SUFFIX.rst index bc4b9df47..e445c984e 100644 --- a/Help/variable/CMAKE_EXECUTABLE_SUFFIX.rst +++ b/Help/variable/CMAKE_EXECUTABLE_SUFFIX.rst @@ -1,10 +1,14 @@ CMAKE_EXECUTABLE_SUFFIX ----------------------- -The suffix for executables on this platform. +The suffix for executables on the target platform. The suffix to use for the end of an executable filename if any, ``.exe`` on Windows. :variable:`CMAKE_EXECUTABLE_SUFFIX_` overrides this for language ````. + + +See the :variable:`CMAKE_HOST_EXECUTABLE_SUFFIX` variable for the +executable suffix on the host platform. diff --git a/Help/variable/CMAKE_EXPORT_BUILD_DATABASE.rst b/Help/variable/CMAKE_EXPORT_BUILD_DATABASE.rst new file mode 100644 index 000000000..94d98423e --- /dev/null +++ b/Help/variable/CMAKE_EXPORT_BUILD_DATABASE.rst @@ -0,0 +1,86 @@ +CMAKE_EXPORT_BUILD_DATABASE +--------------------------- + +.. versionadded:: 3.31 + +.. note :: + + This variable is meaningful only when experimental support for build + databases has been enabled by the + ``CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE`` gate. + +Enable/Disable output of module compile commands during the build. + +If enabled, generates a ``build_database.json`` file containing the +information necessary to compile a target's C++ module sources with any +tooling. The format of the JSON file looks like: + +.. code-block:: javascript + + { + "version": 1, + "revision": 0, + "sets": [ + { + "family-name" : "export_build_database", + "name" : "export_build_database@Debug", + "translation-units" : [ + { + "arguments": [ + "/path/to/compiler", + "...", + ], + "baseline-arguments" : + [ + "...", + ], + "local-arguments" : + [ + "...", + ], + "object": "CMakeFiles/target.dir/source.cxx.o", + "private": true, + "provides": { + "importable": "path/to/bmi" + }, + "requires" : [], + "source": "path/to/source.cxx", + "work-directory": "/path/to/working/directory" + } + ], + "visible-sets" : [] + } + ] + } + +This is initialized by the :envvar:`CMAKE_EXPORT_BUILD_DATABASE` environment +variable, and initializes the :prop_tgt:`EXPORT_BUILD_DATABASE` target +property for all targets. + +.. note:: + This option is implemented only by the :ref:`Ninja Generators`. It is + ignored on other generators. + +When supported and enabled, numerous targets are created in order to make it +possible to build a file containing just the commands that are needed for the +tool in question. + +``cmake_build_database-`` + Writes ``build_database_.json``. Writes a build database for the + entire build for the given configuration and all languages. Not available if + the configuration name is the empty string. + +``cmake_build_database--`` + Writes ``build_database__.json``. Writes build database for + the entire build for the given configuration and language. Not available if + the configuration name is the empty string. + +``cmake_build_database-`` + Writes ``build_database_.json``. Writes build database for the entire + build for the given language and all configurations. In a multi-config + generator, other build configuration database may be assumed to exist. + +``cmake_build_database`` + Writes to ``build_database.json``. Writes build database for all languages + and configurations. In a multi-config generator, other build configuration + database may be assumed to exist. diff --git a/Help/variable/CMAKE_FIND_PACKAGE_SORT_ORDER.rst b/Help/variable/CMAKE_FIND_PACKAGE_SORT_ORDER.rst index 1725ba144..f1016d9cf 100644 --- a/Help/variable/CMAKE_FIND_PACKAGE_SORT_ORDER.rst +++ b/Help/variable/CMAKE_FIND_PACKAGE_SORT_ORDER.rst @@ -3,23 +3,26 @@ CMAKE_FIND_PACKAGE_SORT_ORDER .. versionadded:: 3.7 -The default order for sorting packages found using :command:`find_package`. -It can assume one of the following values: +The default order for sorting directories which match a search path containing +a glob expression found using :command:`find_package`. It can assume one of +the following values: ``NONE`` - Default. No attempt is done to sort packages. + Default. No attempt is done to sort directories. The first valid package found will be selected. ``NAME`` - Sort packages lexicographically before selecting one. + Sort directories lexicographically before searching. ``NATURAL`` - Sort packages using natural order (see ``strverscmp(3)`` manual), + Sort directories using natural order (see ``strverscmp(3)`` manual), i.e. such that contiguous digits are compared as whole numbers. Natural sorting can be employed to return the highest version when multiple -versions of the same library are found by :command:`find_package`. For -example suppose that the following libraries have been found: +versions of the same library are available to be found by +:command:`find_package`. For example suppose that the following libraries +have package configuration files on disk, in a directory of the same name, +with all such directories residing in the same parent directory: * libX-1.1.0 * libX-1.2.9 @@ -35,4 +38,4 @@ version number ``libX-1.2.10``. The sort direction can be controlled using the :variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION` variable -(by default decrescent, e.g. lib-B will be tested before lib-A). +(by default descending, e.g. lib-B will be tested before lib-A). diff --git a/Help/variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.rst b/Help/variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.rst index fc9b315e3..da603aa65 100644 --- a/Help/variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.rst +++ b/Help/variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL.rst @@ -3,7 +3,7 @@ CMAKE_FIND_PACKAGE_TARGETS_GLOBAL .. versionadded:: 3.24 -Setting to ``TRUE`` promotes all :prop_tgt:`IMPORTED` targets discoverd +Setting to ``TRUE`` promotes all :prop_tgt:`IMPORTED` targets discovered by :command:`find_package` to a ``GLOBAL`` scope. diff --git a/Help/variable/CMAKE_HOST_EXECUTABLE_SUFFIX.rst b/Help/variable/CMAKE_HOST_EXECUTABLE_SUFFIX.rst new file mode 100644 index 000000000..c455aae98 --- /dev/null +++ b/Help/variable/CMAKE_HOST_EXECUTABLE_SUFFIX.rst @@ -0,0 +1,12 @@ +CMAKE_HOST_EXECUTABLE_SUFFIX +---------------------------- + +.. versionadded:: 3.31 + +The suffix for executables on the host platform. This may differ from +the suffix for the target platform, :variable:`CMAKE_EXECUTABLE_SUFFIX`. + +The suffix to use for the end of an executable filename if any, ``.exe`` +on Windows. + +See also :variable:`CMAKE_EXECUTABLE_SUFFIX`. diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst index b1e268759..a4f899e24 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst @@ -32,6 +32,7 @@ Value Name ``Intel`` Intel Classic Compiler ``IntelLLVM`` `Intel LLVM-Based Compiler`_ ``LCC`` MCST Elbrus C/C++/Fortran Compiler +``LFortran`` LFortran Fortran Compiler ``MSVC`` `Microsoft Visual Studio`_ ``NVHPC`` `NVIDIA HPC Compiler`_ ``NVIDIA`` `NVIDIA CUDA Compiler`_ @@ -40,7 +41,7 @@ Value Name ``PGI`` The Portland Group ``PathScale`` PathScale ``SDCC`` `Small Device C Compiler`_ -``SunPro`` Oracle Solaris Studio +``SunPro`` Oracle Developer Studio ``Tasking`` `Tasking Compiler Toolsets`_ ``TI`` Texas Instruments ``TIClang`` `Texas Instruments Clang-based Compilers`_ diff --git a/Help/variable/CMAKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE.rst b/Help/variable/CMAKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE.rst new file mode 100644 index 000000000..de3329f7b --- /dev/null +++ b/Help/variable/CMAKE_LANG_CREATE_SHARED_LIBRARY_ARCHIVE.rst @@ -0,0 +1,10 @@ +CMAKE__CREATE_SHARED_LIBRARY_ARCHIVE +------------------------------------------ + +.. versionadded:: 3.31 + +Rule variable to create a shared library with archive. + +This is a rule variable that tells CMake how to create a shared +library with an archive for the language . This rule variable +is a ; delimited list of commands to run to perform the linking step. diff --git a/Help/variable/CMAKE_LANG_HOST_COMPILER.rst b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst index cf3ba62a3..ed9a05596 100644 --- a/Help/variable/CMAKE_LANG_HOST_COMPILER.rst +++ b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst @@ -42,3 +42,8 @@ variable is read-only and changes to it are undefined behavior. .. note:: Ignored when using :ref:`Visual Studio Generators`. + +See the :variable:`CMAKE__HOST_COMPILER_ID` and +:variable:`CMAKE__HOST_COMPILER_VERSION` variables for +information about the host compiler used by ``nvcc``, whether +by default or specified by ``CMAKE__HOST_COMPILER``. diff --git a/Help/variable/CMAKE_LANG_HOST_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_HOST_COMPILER_ID.rst new file mode 100644 index 000000000..a4da281f4 --- /dev/null +++ b/Help/variable/CMAKE_LANG_HOST_COMPILER_ID.rst @@ -0,0 +1,10 @@ +CMAKE__HOST_COMPILER_ID +----------------------------- + +.. versionadded:: 3.31 + +This variable is available when ```` is ``CUDA`` or ``HIP`` +and :variable:`CMAKE__COMPILER_ID` is ``NVIDIA``. +It contains the identity of the host compiler invoked by ``nvcc``, +either by default or as specified by :variable:`CMAKE__HOST_COMPILER`, +among possibilities documented by :variable:`CMAKE__COMPILER_ID`. diff --git a/Help/variable/CMAKE_LANG_HOST_COMPILER_VERSION.rst b/Help/variable/CMAKE_LANG_HOST_COMPILER_VERSION.rst new file mode 100644 index 000000000..eb2c2068f --- /dev/null +++ b/Help/variable/CMAKE_LANG_HOST_COMPILER_VERSION.rst @@ -0,0 +1,10 @@ +CMAKE__HOST_COMPILER_VERSION +---------------------------------- + +.. versionadded:: 3.31 + +This variable is available when ```` is ``CUDA`` or ``HIP`` +and :variable:`CMAKE__COMPILER_ID` is ``NVIDIA``. +It contains the version of the host compiler invoked by ``nvcc``, +either by default or as specified by :variable:`CMAKE__HOST_COMPILER`, +in the same format as :variable:`CMAKE__COMPILER_VERSION`. diff --git a/Help/variable/CMAKE_LANG_STANDARD_LINK_DIRECTORIES.rst b/Help/variable/CMAKE_LANG_STANDARD_LINK_DIRECTORIES.rst new file mode 100644 index 000000000..15aec235e --- /dev/null +++ b/Help/variable/CMAKE_LANG_STANDARD_LINK_DIRECTORIES.rst @@ -0,0 +1,14 @@ +CMAKE__STANDARD_LINK_DIRECTORIES +-------------------------------------- + +.. versionadded:: 3.31 + +Link directories specified for every executable and library linked +for language ````. This is meant for specification of system +link directories needed by the language for the current platform. + +This variable should not be set by project code. It is meant to be set by +CMake's platform information modules for the current toolchain, or by a +toolchain file when used with :variable:`CMAKE_TOOLCHAIN_FILE`. + +See also :variable:`CMAKE__STANDARD_LIBRARIES`. diff --git a/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst b/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst index 1867ad844..1c07a23c7 100644 --- a/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst +++ b/Help/variable/CMAKE_LINK_DEPENDS_USE_LINKER.rst @@ -13,8 +13,9 @@ target property is true. .. note:: - CMake version |release| defaults this variable to ``FALSE`` because - GNU binutils linkers (``ld``, ``ld.bfd``, ``ld.gold``) generate spurious + CMake version |release| defaults this variable to ``FALSE`` if the linker is + one from the GNU binutils linkers (``ld`` and ``ld.bfd`` for version less + than 2.41 or ``ld.gold`` for any version) because it generate spurious dependencies on temporary files when LTO is enabled. See `GNU bug 30568`_. .. _`GNU bug 30568`: https://sourceware.org/bugzilla/show_bug.cgi?id=30568 diff --git a/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst b/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst new file mode 100644 index 000000000..324ffcb77 --- /dev/null +++ b/Help/variable/CMAKE_LINK_LIBRARIES_STRATEGY.rst @@ -0,0 +1,11 @@ +CMAKE_LINK_LIBRARIES_STRATEGY +----------------------------- + +.. versionadded:: 3.31 + +Specify a strategy for ordering targets' direct link dependencies +on linker command lines. + +If set, this variable acts as the default value for the +:prop_tgt:`LINK_LIBRARIES_STRATEGY` target property when a target is created. +Set that property directly to specify a strategy for a single target. diff --git a/Help/variable/CMAKE_MAKE_PROGRAM.rst b/Help/variable/CMAKE_MAKE_PROGRAM.rst index 9769c7ae7..df0b1b92c 100644 --- a/Help/variable/CMAKE_MAKE_PROGRAM.rst +++ b/Help/variable/CMAKE_MAKE_PROGRAM.rst @@ -30,8 +30,7 @@ to configure the project: the CMake cache then CMake will use the specified value. * The :ref:`Visual Studio Generators` set this to the full path to - ``MSBuild.exe`` (VS >= 10), ``devenv.com`` (VS 7,8,9), or - ``VCExpress.exe`` (VS Express 8,9). + ``MSBuild.exe`` or ``devenv.com``. (See also variables :variable:`CMAKE_VS_MSBUILD_COMMAND` and :variable:`CMAKE_VS_DEVENV_COMMAND`. @@ -39,9 +38,9 @@ to configure the project: These generators prefer to lookup the build tool at build time rather than to store ``CMAKE_MAKE_PROGRAM`` in the CMake cache ahead of time. This is because the tools are version-specific - and can be located using the Windows Registry. It is also + and can be located using the Visual Studio Installer. It is also necessary because the proper build tool may depend on the - project content (e.g. the Intel Fortran plugin to VS 10 and 11 + project content (e.g. the Intel Fortran plugin to Visual Studio requires ``devenv.com`` to build its ``.vfproj`` project files even though ``MSBuild.exe`` is normally preferred to support the :variable:`CMAKE_GENERATOR_TOOLSET`). diff --git a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst index d4b256a36..22d640ae8 100644 --- a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst +++ b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst @@ -4,8 +4,8 @@ CMAKE_MSVCIDE_RUN_PATH .. versionadded:: 3.10 Extra PATH locations that should be used when executing -:command:`add_custom_command` or :command:`add_custom_target` when using the -:generator:`Visual Studio 12 2013` (or above) generator. This allows +:command:`add_custom_command` or :command:`add_custom_target` when using +:ref:`Visual Studio Generators`. This allows for running commands and using dll's that the IDE environment is not aware of. If not set explicitly the value is initialized by the ``CMAKE_MSVCIDE_RUN_PATH`` diff --git a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst index f844105e4..59fa0be53 100644 --- a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst +++ b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst @@ -38,6 +38,8 @@ only for the policies that do not warn by default: policy :policy:`CMP0129`. * ``CMAKE_POLICY_WARNING_CMP0133`` controls the warning for policy :policy:`CMP0133`. +* ``CMAKE_POLICY_WARNING_CMP0172`` controls the warning for + policy :policy:`CMP0172`. This variable should not be set by a project in CMake code. Project developers running CMake may set this variable in their cache to diff --git a/Help/variable/CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX.rst b/Help/variable/CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX.rst new file mode 100644 index 000000000..13943fcb7 --- /dev/null +++ b/Help/variable/CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX.rst @@ -0,0 +1,9 @@ +CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX +----------------------------------- + +.. versionadded:: 3.31 + +The suffix for archived shared libraries that you link to. + +The suffix to use for the end of a archive containing a +shared library, ``.a`` on AIX. diff --git a/Help/variable/CMAKE_SYSTEM_NAME.rst b/Help/variable/CMAKE_SYSTEM_NAME.rst index e9ffec47b..681ee03c5 100644 --- a/Help/variable/CMAKE_SYSTEM_NAME.rst +++ b/Help/variable/CMAKE_SYSTEM_NAME.rst @@ -85,6 +85,7 @@ Value Name ``UNIX_SV`` SCO UnixWare (pre release 7) ``UnixWare`` SCO UnixWare 7 ``visionOS`` Apple mixed reality operating system +``WASI`` WebAssembly System Interface ``watchOS`` Apple watch operating system ``Windows`` Windows stationary operating systems ``WindowsCE`` Windows Embedded Compact diff --git a/Help/variable/CMAKE_TLS_VERIFY.rst b/Help/variable/CMAKE_TLS_VERIFY.rst index 5871ac72f..0ecb70100 100644 --- a/Help/variable/CMAKE_TLS_VERIFY.rst +++ b/Help/variable/CMAKE_TLS_VERIFY.rst @@ -5,7 +5,12 @@ Specify the default value for the :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands' ``TLS_VERIFY`` options. If this variable is not set, the commands check the :envvar:`CMAKE_TLS_VERIFY` environment variable. -If neither is set, the default is *off*. +If neither is set, the default is *on*. + +.. versionchanged:: 3.31 + The default is on. Previously, the default was off. + Users may set the :envvar:`CMAKE_TLS_VERIFY` environment + variable to ``0`` to restore the old default. This variable is also used by the :module:`ExternalProject` and :module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`. diff --git a/Help/variable/CMAKE_TLS_VERSION.rst b/Help/variable/CMAKE_TLS_VERSION.rst index 3e7f2ce0a..ff0918bd0 100644 --- a/Help/variable/CMAKE_TLS_VERSION.rst +++ b/Help/variable/CMAKE_TLS_VERSION.rst @@ -7,6 +7,11 @@ Specify the default value for the :command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands' ``TLS_VERSION`` option. If this variable is not set, the commands check the :envvar:`CMAKE_TLS_VERSION` environment variable. +If neither is set, the default is TLS 1.2. + +.. versionchanged:: 3.31 + The default is TLS 1.2. + Previously, no minimum version was enforced by default. The value may be one of: diff --git a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst index 95e09b1ae..7aecc2585 100644 --- a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst +++ b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst @@ -1,14 +1,11 @@ CMAKE_VS_DEVENV_COMMAND ----------------------- -The generators for :generator:`Visual Studio 12 2013` and above set this -variable to the ``devenv.com`` command installed with the corresponding -Visual Studio version. Note that this variable may be empty on -Visual Studio Express editions because they do not provide this tool. +The :ref:`Visual Studio Generators` set this variable to the ``devenv.com`` +command installed with the corresponding Visual Studio version. 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 12 2013` and above. -See also the :variable:`CMAKE_MAKE_PROGRAM` variable. +See also the :variable:`CMAKE_VS_MSBUILD_COMMAND` and +:variable:`CMAKE_MAKE_PROGRAM` variables. diff --git a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst index 485726991..a2f072cfd 100644 --- a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst +++ b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst @@ -1,7 +1,7 @@ CMAKE_VS_INTEL_Fortran_PROJECT_VERSION -------------------------------------- -When generating for :generator:`Visual Studio 12 2013` or greater with the Intel +When generating for :generator:`Visual Studio 14 2015` or greater with the Intel Fortran plugin installed, this specifies the ``.vfproj`` project file format version. This is intended for internal use by CMake and should not be used by project code. diff --git a/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst b/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst index 96924d59c..32a56c74f 100644 --- a/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst +++ b/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst @@ -1,13 +1,11 @@ CMAKE_VS_MSBUILD_COMMAND ------------------------ -The generators for :generator:`Visual Studio 12 2013` and above set this -variable to the ``MSBuild.exe`` command installed with the corresponding -Visual Studio version. +The :ref:`Visual Studio Generators` set this variable to the ``MSBuild.exe`` +command installed with the corresponding Visual Studio version. This variable is not defined by other generators even if ``MSBuild.exe`` is installed on the computer. -The :variable:`CMAKE_VS_DEVENV_COMMAND` is also provided for the -non-Express editions of Visual Studio. -See also the :variable:`CMAKE_MAKE_PROGRAM` variable. +See also the :variable:`CMAKE_VS_DEVENV_COMMAND` and +:variable:`CMAKE_MAKE_PROGRAM` variables. diff --git a/Help/variable/CMAKE_VS_NUGET_PACKAGE_RESTORE.rst b/Help/variable/CMAKE_VS_NUGET_PACKAGE_RESTORE.rst index 716072630..380caa2d0 100644 --- a/Help/variable/CMAKE_VS_NUGET_PACKAGE_RESTORE.rst +++ b/Help/variable/CMAKE_VS_NUGET_PACKAGE_RESTORE.rst @@ -9,9 +9,10 @@ prior to a build. NuGet packages can be defined using the :prop_tgt:`VS_PACKAGE_REFERENCES` property on a target. If no package references are defined, this setting will do nothing. -The command line option ``--resolve-package-references`` can be used -alternatively to control the resolve behavior globally. This option -will take precedence over the cache variable. +The command line option +:option:`--resolve-package-references ` +can be used alternatively to control the resolve behavior globally. +This option will take precedence over the cache variable. Targets that use the :prop_tgt:`DOTNET_SDK` are required to run a restore before building. Disabling this option may cause the build diff --git a/Help/variable/CMAKE_WINDOWS_KMDF_VERSION.rst b/Help/variable/CMAKE_WINDOWS_KMDF_VERSION.rst new file mode 100644 index 000000000..f878f775d --- /dev/null +++ b/Help/variable/CMAKE_WINDOWS_KMDF_VERSION.rst @@ -0,0 +1,13 @@ +CMAKE_WINDOWS_KMDF_VERSION +-------------------------- + +.. versionadded:: 3.31 + +Specify the `Kernel-Mode Drive Framework`_ target version. + +A :variable:`toolchain file ` that sets +:variable:`CMAKE_SYSTEM_NAME` to ``WindowsKernelModeDriver`` +must also set ``CMAKE_WINDOWS_KMDF_VERSION`` to specify the +KMDF target version. + +.. _`Kernel-Mode Drive Framework`: https://learn.microsoft.com/en-us/windows-hardware/drivers/wdf/kmdf-version-history diff --git a/Help/variable/CTEST_TLS_VERIFY.rst b/Help/variable/CTEST_TLS_VERIFY.rst index 9b3d96c7e..b28384263 100644 --- a/Help/variable/CTEST_TLS_VERIFY.rst +++ b/Help/variable/CTEST_TLS_VERIFY.rst @@ -11,3 +11,9 @@ to a dashboard via ``https://`` URLs. If ``CTEST_TLS_VERIFY`` is not set, the :variable:`CMAKE_TLS_VERIFY` variable or :envvar:`CMAKE_TLS_VERIFY` environment variable is used instead. +If neither is set, the default is *on*. + +.. versionchanged:: 3.31 + The default is on. Previously, the default was off. + Users may set the :envvar:`CMAKE_TLS_VERIFY` environment + variable to ``0`` to restore the old default. diff --git a/Help/variable/WASI.rst b/Help/variable/WASI.rst new file mode 100644 index 000000000..eacfc3f9f --- /dev/null +++ b/Help/variable/WASI.rst @@ -0,0 +1,7 @@ +WASI +---- + +.. versionadded:: 3.31 + +Set to ``1`` when the target system is WebAssembly System Interface +(:variable:`CMAKE_SYSTEM_NAME` is ``WASI``). diff --git a/Modules/AndroidTestUtilities.cmake b/Modules/AndroidTestUtilities.cmake index ddccf586b..319710b8b 100644 --- a/Modules/AndroidTestUtilities.cmake +++ b/Modules/AndroidTestUtilities.cmake @@ -139,7 +139,6 @@ function(android_add_test_data test_name) get_filename_component(extern_data_basename ${output} NAME) add_custom_command( TARGET ${DATA_TARGET_NAME} POST_BUILD - DEPENDS ${extern_data_source} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${extern_data_source} ${DEST}/${extern_data_basename} ) endif() @@ -148,6 +147,8 @@ function(android_add_test_data test_name) if(ANDROID) string(REGEX REPLACE "DATA{([^ ;]+)}" "\\1" processed_FILES "${AST_FILES}") + # There's no target used for this command, so we don't need to do anything + # here for CMP0178. add_test( NAME ${test_name} COMMAND ${CMAKE_COMMAND} diff --git a/Modules/CMakeASMInformation.cmake b/Modules/CMakeASMInformation.cmake index 2dc1585df..703003440 100644 --- a/Modules/CMakeASMInformation.cmake +++ b/Modules/CMakeASMInformation.cmake @@ -96,5 +96,6 @@ if(NOT CMAKE_EXECUTABLE_RPATH_LINK_ASM${ASM_DIALECT}_FLAG) set(CMAKE_EXECUTABLE_RPATH_LINK_ASM${ASM_DIALECT}_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_ASM${ASM_DIALECT}_FLAG}) endif() +set(CMAKE_ASM${ASM_DIALECT}_USE_LINKER_INFORMATION TRUE) set(CMAKE_ASM${ASM_DIALECT}_INFOMATION_LOADED 1) diff --git a/Modules/CMakeASM_MARMASMInformation.cmake b/Modules/CMakeASM_MARMASMInformation.cmake index a47f7c2d0..51bd313fe 100644 --- a/Modules/CMakeASM_MARMASMInformation.cmake +++ b/Modules/CMakeASM_MARMASMInformation.cmake @@ -9,6 +9,7 @@ set(ASM_DIALECT "_MARMASM") set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm) set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT " -o ") +set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY " /out: ") # The ASM_MARMASM compiler id for this compiler is "MSVC", so fill out the runtime library table. set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") diff --git a/Modules/CMakeASM_MASMInformation.cmake b/Modules/CMakeASM_MASMInformation.cmake index 11b83662e..64ae0701a 100644 --- a/Modules/CMakeASM_MASMInformation.cmake +++ b/Modules/CMakeASM_MASMInformation.cmake @@ -9,6 +9,7 @@ set(ASM_DIALECT "_MASM") set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm) set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT " -c -Fo ") +set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY " /out: ") # The ASM_MASM compiler id for this compiler is "MSVC", so fill out the runtime library table. set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded "") diff --git a/Modules/CMakeAddNewLanguage.txt b/Modules/CMakeAddNewLanguage.txt index b0be590ae..011195c4b 100644 --- a/Modules/CMakeAddNewLanguage.txt +++ b/Modules/CMakeAddNewLanguage.txt @@ -20,12 +20,20 @@ CMake(LANG)Compiler.cmake.in -> used by CMakeDetermine(LANG)Compiler.cmake This file is used to store compiler information and is copied down into try compile directories so that try compiles do not need to re-determine and test the LANG -CMakeTest(LANG)Compiler.cmake -> test the compiler and set: - SET(CMAKE_(LANG)_COMPILER_WORKS 1 CACHE INTERNAL "") - -CMake(LANG)Information.cmake -> set up rule variables for LANG : +CMake(LANG)Information.cmake => set compiler configuration: CMAKE_(LANG)_CREATE_SHARED_LIBRARY CMAKE_(LANG)_CREATE_SHARED_MODULE CMAKE_(LANG)_CREATE_STATIC_LIBRARY CMAKE_(LANG)_COMPILE_OBJECT CMAKE_(LANG)_LINK_EXECUTABLE + + CMAKE_(LANG)_USE_LINKER_INFORMATION + +CMakeTest(LANG)Compiler.cmake -> test the compiler and set: + SET(CMAKE_(LANG)_COMPILER_WORKS 1 CACHE INTERNAL "") + + +If the variable CMAKE_(LANG)_USE_LINKER_INFORMATION has value TRUE, the file CMake(LANG)LinkerInformation.cmake +should be defined. + +CMake(LANG)LinkerInformation.cmake -> set up linker configuration for LANG. diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake index 72fb80144..977e51a3e 100644 --- a/Modules/CMakeCInformation.cmake +++ b/Modules/CMakeCInformation.cmake @@ -175,4 +175,6 @@ if(NOT CMAKE_C_LINK_EXECUTABLE) " -o ") endif() +set(CMAKE_C_USE_LINKER_INFORMATION TRUE) + set(CMAKE_C_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCSharpInformation.cmake b/Modules/CMakeCSharpInformation.cmake index 41cd4494c..f698bdfba 100644 --- a/Modules/CMakeCSharpInformation.cmake +++ b/Modules/CMakeCSharpInformation.cmake @@ -64,4 +64,7 @@ set(CMAKE_CSharp_CREATE_SHARED_MODULE "CSharp_NO_CREATE_SHARED_MODULE") set(CMAKE_CSharp_LINK_EXECUTABLE "CSharp_NO_LINK_EXECUTABLE") set(CMAKE_CSharp_USE_RESPONSE_FILE_FOR_OBJECTS 1) + +set(CMAKE_CSharp_USE_LINKER_INFORMATION FALSE) + set(CMAKE_CSharp_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in index c054e5cce..c6a176ba5 100644 --- a/Modules/CMakeCUDACompiler.cmake.in +++ b/Modules/CMakeCUDACompiler.cmake.in @@ -3,6 +3,8 @@ set(CMAKE_CUDA_HOST_COMPILER "@CMAKE_CUDA_HOST_COMPILER@") set(CMAKE_CUDA_HOST_LINK_LAUNCHER "@CMAKE_CUDA_HOST_LINK_LAUNCHER@") set(CMAKE_CUDA_COMPILER_ID "@CMAKE_CUDA_COMPILER_ID@") set(CMAKE_CUDA_COMPILER_VERSION "@CMAKE_CUDA_COMPILER_VERSION@") +set(CMAKE_CUDA_HOST_COMPILER_ID "@CMAKE_CUDA_HOST_COMPILER_ID@") +set(CMAKE_CUDA_HOST_COMPILER_VERSION "@CMAKE_CUDA_HOST_COMPILER_VERSION@") set(CMAKE_CUDA_DEVICE_LINKER "@CMAKE_CUDA_DEVICE_LINKER@") set(CMAKE_CUDA_FATBINARY "@CMAKE_CUDA_FATBINARY@") set(CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT "@CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT@") diff --git a/Modules/CMakeCUDACompilerId.cu.in b/Modules/CMakeCUDACompilerId.cu.in index cecb9488e..fdffd02d6 100644 --- a/Modules/CMakeCUDACompilerId.cu.in +++ b/Modules/CMakeCUDACompilerId.cu.in @@ -16,6 +16,28 @@ char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; @CMAKE_CUDA_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_CUDA_COMPILER_ID_ERROR_FOR_TEST@ +#ifdef HOST_COMPILER_ID +char const* info_host_compiler = "INFO" ":" "host_compiler[" HOST_COMPILER_ID "]"; +#endif +#ifdef HOST_COMPILER_VERSION +char const* info_host_compiler_version = "INFO" ":" "host_compiler_version[" HOST_COMPILER_VERSION "]"; +#elif defined(HOST_COMPILER_VERSION_MAJOR) +char const info_host_compiler_version[] = { + 'I', 'N', 'F', 'O', ':','h','o','s','t','_', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + HOST_COMPILER_VERSION_MAJOR, +# ifdef HOST_COMPILER_VERSION_MINOR + '.', HOST_COMPILER_VERSION_MINOR, +# ifdef HOST_COMPILER_VERSION_PATCH + '.', HOST_COMPILER_VERSION_PATCH, +# ifdef HOST_COMPILER_VERSION_TWEAK + '.', HOST_COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif + #define CXX_STD_11 201103L #define CXX_STD_14 201402L #define CXX_STD_17 201703L @@ -74,6 +96,12 @@ int main(int argc, char* argv[]) #endif #ifdef SIMULATE_VERSION_MAJOR require += info_simulate_version[argc]; +#endif +#ifdef HOST_COMPILER_ID + require += info_host_compiler[argc]; +#endif +#ifdef HOST_COMPILER_VERSION_MAJOR + require += info_host_compiler_version[argc]; #endif require += info_language_standard_default[argc]; require += info_language_extensions_default[argc]; diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake index 66a5faab9..fa197b2a2 100644 --- a/Modules/CMakeCUDAInformation.cmake +++ b/Modules/CMakeCUDAInformation.cmake @@ -150,4 +150,6 @@ endif() unset(__IMPLICIT_DLINK_FLAGS) +set(CMAKE_CUDA_USE_LINKER_INFORMATION TRUE) + set(CMAKE_CUDA_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index e521fb842..cc130ae17 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake @@ -183,4 +183,6 @@ mark_as_advanced( CMAKE_VERBOSE_MAKEFILE ) +set(CMAKE_CXX_USE_LINKER_INFORMATION TRUE) + set(CMAKE_CXX_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCommonLanguageInclude.cmake b/Modules/CMakeCommonLanguageInclude.cmake index 5511930fd..4907a2840 100644 --- a/Modules/CMakeCommonLanguageInclude.cmake +++ b/Modules/CMakeCommonLanguageInclude.cmake @@ -120,13 +120,4 @@ macro(_cmake_common_language_platform_flags lang) ${CMAKE_${type}_LINK_DYNAMIC_C_FLAGS}) endif() endforeach() - - if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() - endif() endmacro() diff --git a/Modules/CMakeDetermineCSharpCompiler.cmake b/Modules/CMakeDetermineCSharpCompiler.cmake index 652eb63e2..650689432 100644 --- a/Modules/CMakeDetermineCSharpCompiler.cmake +++ b/Modules/CMakeDetermineCSharpCompiler.cmake @@ -1,9 +1,9 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -if(NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])") +if(NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio") message(FATAL_ERROR - "C# is currently only supported for Microsoft Visual Studio 12 2013 and later.") + "C# is currently only supported by Visual Studio generators.") endif() include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index e58ae4279..5e880761a 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -347,8 +347,17 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) else() set(_archid "") endif() + if(CMAKE_${lang}_HOST_COMPILER_ID) + set(_hostcc " with host compiler ${CMAKE_${lang}_HOST_COMPILER_ID}") + if(CMAKE_${lang}_HOST_COMPILER_VERSION) + string(APPEND _hostcc " ${CMAKE_${lang}_HOST_COMPILER_VERSION}") + endif() + else() + set(_hostcc "") + endif() message(STATUS "The ${lang} compiler identification is " - "${CMAKE_${lang}_COMPILER_ID}${_archid}${_version}${_variant}") + "${CMAKE_${lang}_COMPILER_ID}${_archid}${_version}${_variant}${_hostcc}") + unset(_hostcc) unset(_archid) unset(_version) unset(_variant) @@ -373,6 +382,8 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) set(CMAKE_${lang}_COMPILER_WRAPPER "${CMAKE_${lang}_COMPILER_WRAPPER}" PARENT_SCOPE) set(CMAKE_${lang}_SIMULATE_ID "${CMAKE_${lang}_SIMULATE_ID}" PARENT_SCOPE) set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_${lang}_SIMULATE_VERSION}" PARENT_SCOPE) + set(CMAKE_${lang}_HOST_COMPILER_ID "${CMAKE_${lang}_HOST_COMPILER_ID}" PARENT_SCOPE) + set(CMAKE_${lang}_HOST_COMPILER_VERSION "${CMAKE_${lang}_HOST_COMPILER_VERSION}" PARENT_SCOPE) set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT}" PARENT_SCOPE) set(CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT "${CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT}" PARENT_SCOPE) set(CMAKE_${lang}_COMPILER_PRODUCED_OUTPUT "${COMPILER_${lang}_PRODUCED_OUTPUT}" PARENT_SCOPE) @@ -395,6 +406,22 @@ function(CMAKE_DETERMINE_COMPILER_ID_WRITE lang src) PLATFORM_DEFAULT_COMPILER ) + if(lang MATCHES "^(CUDA|HIP)$") + compiler_id_detection(CMAKE_${lang}_HOST_COMPILER_ID_CONTENT CXX + PREFIX HOST_ + ID_STRING + VERSION_STRINGS + ) + string(APPEND CMAKE_${lang}_COMPILER_ID_CONTENT + "\n" + "\n" + "/* Detect host compiler used by NVCC. */\n" + "#ifdef __NVCC__\n" + "${CMAKE_${lang}_HOST_COMPILER_ID_CONTENT}\n" + "#endif /* __NVCC__ */\n" + ) + endif() + unset(src_in CACHE) string(CONFIGURE "${ID_CONTENT_IN}" ID_CONTENT_OUT @ONLY) file(WRITE ${CMAKE_${lang}_COMPILER_ID_DIR}/${src} "${ID_CONTENT_OUT}") @@ -707,7 +734,7 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS} set(id_clang_cxx_library "CLANG_CXX_LIBRARY = \"${CMAKE_MATCH_3}\";") endif() endif() - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT MATCHES "^$|[Mm][Aa][Cc][Oo][Ss]") + if(CMAKE_OSX_SYSROOT MATCHES "[Mm][Aa][Cc][Oo][Ss]" OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT STREQUAL "")) set(id_code_sign_identity "-") # When targeting macOS, use only the host architecture. if (_CMAKE_APPLE_ARCHS_DEFAULT) @@ -941,6 +968,8 @@ function(CMAKE_DETERMINE_COMPILER_ID_CHECK lang file) set(ARCHITECTURE_ID) set(SIMULATE_ID) set(SIMULATE_VERSION) + set(HOST_COMPILER_ID) + set(HOST_COMPILER_VERSION) set(CMAKE_${lang}_COMPILER_ID_STRING_REGEX ".?I.?N.?F.?O.?:.?[A-Za-z0-9_]+\\[[^]]*\\]") foreach(encoding "" "ENCODING;UTF-16LE" "ENCODING;UTF-16BE") cmake_policy(PUSH) @@ -1027,6 +1056,13 @@ function(CMAKE_DETERMINE_COMPILER_ID_CHECK lang file) if("${info}" MATCHES "INFO:qnxnto\\[\\]") set(COMPILER_QNXNTO 1) endif() + if("${info}" MATCHES "INFO:host_compiler\\[([^]\"]*)\\]") + set(HOST_COMPILER_ID "${CMAKE_MATCH_1}") + endif() + if("${info}" MATCHES "INFO:host_compiler_version\\[([^]\"]*)\\]") + string(REGEX REPLACE "^0+([0-9]+)" "\\1" HOST_COMPILER_VERSION "${CMAKE_MATCH_1}") + string(REGEX REPLACE "\\.0+([0-9])" ".\\1" HOST_COMPILER_VERSION "${HOST_COMPILER_VERSION}") + endif() if("${info}" MATCHES "INFO:standard_default\\[([^]\"]*)\\]") set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_MATCH_1}") endif() @@ -1088,6 +1124,8 @@ function(CMAKE_DETERMINE_COMPILER_ID_CHECK lang file) set(CMAKE_${lang}_COMPILER_VERSION_INTERNAL "${COMPILER_VERSION_INTERNAL}") set(CMAKE_${lang}_SIMULATE_ID "${SIMULATE_ID}") set(CMAKE_${lang}_SIMULATE_VERSION "${SIMULATE_VERSION}") + set(CMAKE_${lang}_HOST_COMPILER_ID "${HOST_COMPILER_ID}") + set(CMAKE_${lang}_HOST_COMPILER_VERSION "${HOST_COMPILER_VERSION}") endif() # Check the compiler identification string. @@ -1095,7 +1133,12 @@ function(CMAKE_DETERMINE_COMPILER_ID_CHECK lang file) # The compiler identification was found. string(APPEND _CMAKE_${lang}_COMPILER_ID_LOG "The ${lang} compiler identification is ${CMAKE_${lang}_COMPILER_ID}, found in:\n" - " ${file}\n\n") + " ${file}\n") + if(CMAKE_${lang}_HOST_COMPILER_ID) + string(APPEND _CMAKE_${lang}_COMPILER_ID_LOG + "The host compiler identification is ${CMAKE_${lang}_HOST_COMPILER_ID}\n") + endif() + string(APPEND _CMAKE_${lang}_COMPILER_ID_LOG "\n") else() # The compiler identification could not be found. string(APPEND _CMAKE_${lang}_COMPILER_ID_LOG @@ -1144,6 +1187,8 @@ function(CMAKE_DETERMINE_COMPILER_ID_CHECK lang file) set(COMPILER_QNXNTO "${COMPILER_QNXNTO}" PARENT_SCOPE) set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT}" PARENT_SCOPE) set(CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT "${CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT}" PARENT_SCOPE) + set(CMAKE_${lang}_HOST_COMPILER_ID "${CMAKE_${lang}_HOST_COMPILER_ID}" PARENT_SCOPE) + set(CMAKE_${lang}_HOST_COMPILER_VERSION "${CMAKE_${lang}_HOST_COMPILER_VERSION}" PARENT_SCOPE) endfunction() #----------------------------------------------------------------------------- diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake index 613b0c4ef..f051163d8 100644 --- a/Modules/CMakeDetermineFortranCompiler.cmake +++ b/Modules/CMakeDetermineFortranCompiler.cmake @@ -60,6 +60,7 @@ else() # ifx: Intel Fortran LLVM-based compiler # ifort: Intel Classic Fortran compiler # nagfor: NAG Fortran compiler + # lfortran: LFortran Fortran Compiler # # GNU is last to be searched, # so if you paid for a compiler it is picked by default. @@ -108,6 +109,9 @@ else() # Intel on windows does not preprocess by default. "-fpp" + + # LFortran does not preprocess by default. + "--cpp-infer" ) endif() diff --git a/Modules/CMakeDetermineHIPCompiler.cmake b/Modules/CMakeDetermineHIPCompiler.cmake index e667099c9..d7a99b0d6 100644 --- a/Modules/CMakeDetermineHIPCompiler.cmake +++ b/Modules/CMakeDetermineHIPCompiler.cmake @@ -223,7 +223,7 @@ if(CMAKE_HIP_PLATFORM STREQUAL "amd") endif() unset(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS) endif() - if(CMAKE_HIP_COMPILER_ROCM_LIB MATCHES "/lib64$" AND NOT DEFINED CMAKE_SIZEOF_VOID_P) + if(NOT DEFINED CMAKE_SIZEOF_VOID_P) # We have not yet determined the target ABI but we need 'find_package' to # search lib64 directories to find hip-lang CMake package dependencies. # This will be replaced by ABI detection later. diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake index 5e85440b7..dc28f128d 100644 --- a/Modules/CMakeFindBinUtils.cmake +++ b/Modules/CMakeFindBinUtils.cmake @@ -89,8 +89,9 @@ if(("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC" AND if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Clang|LLVMFlang)$") set(_CMAKE_NM_NAMES "llvm-nm" "nm") list(PREPEND _CMAKE_AR_NAMES "llvm-lib") - # llvm-mt is not ready to be used as a replacement for mt.exe - # list(PREPEND _CMAKE_MT_NAMES "llvm-mt") + if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}" VERSION_GREATER_EQUAL 14.0.2) + list(PREPEND _CMAKE_MT_NAMES "llvm-mt") + endif() list(PREPEND _CMAKE_LINKER_NAMES "lld-link") list(APPEND _CMAKE_TOOL_VARS NM) elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xIntel") @@ -110,56 +111,45 @@ elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Open)?W list(APPEND _CMAKE_TOOL_VARS LINKER AR) elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^xIAR$") - # Small helper declaring an IAR tool (e.g. linker) to avoid repeating the same idiom every time - macro(__append_IAR_tool TOOL_VAR NAME) - set(_CMAKE_${TOOL_VAR}_NAMES "${NAME}" "${NAME}.exe") - list(APPEND _CMAKE_TOOL_VARS ${TOOL_VAR}) - endmacro() - - # Resolve hint path from an IAR compiler - function(__resolve_IAR_hints COMPILER RESULT) - get_filename_component(_CMAKE_IAR_HINT "${COMPILER}" REALPATH) - get_filename_component(_CMAKE_IAR_HINT "${_CMAKE_IAR_HINT}" DIRECTORY) - list(APPEND _IAR_HINTS "${_CMAKE_IAR_HINT}") - - get_filename_component(_CMAKE_IAR_HINT "${COMPILER}" DIRECTORY) - list(APPEND _IAR_HINTS "${_CMAKE_IAR_HINT}") - - set(${RESULT} "${_IAR_HINTS}" PARENT_SCOPE) - endfunction() - - __resolve_IAR_hints("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" _CMAKE_TOOLCHAIN_LOCATION) - set(_CMAKE_IAR_ITOOLS "ARM" "RX" "RH850" "RL78" "RISCV" "RISC-V" "STM8") - set(_CMAKE_IAR_XTOOLS "AVR" "MSP430" "V850" "8051") - - string(TOLOWER "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" _CMAKE_IAR_LOWER_ARCHITECTURE_ID) - - if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ITOOLS) - __append_IAR_tool(AR "iarchive") - __append_IAR_tool(LINKER "ilink${_CMAKE_IAR_LOWER_ARCHITECTURE_ID}") - - __append_IAR_tool(IAR_ELFDUMP "ielfdump${_CMAKE_IAR_LOWER_ARCHITECTURE_ID}") - __append_IAR_tool(IAR_ELFTOOL "ielftool") - __append_IAR_tool(IAR_OBJMANIP "iobjmanip") - __append_IAR_tool(IAR_SYMEXPORT "isymexport") - - elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_XTOOLS) - __append_IAR_tool(AR "xar") - if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR" AND - (CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION VERSION_GREATER_EQUAL 8)) - # IAR UBROF Linker V8.10+ for Microchip AVR is `xlinkavr` - __append_IAR_tool(LINKER "xlink${_CMAKE_IAR_LOWER_ARCHITECTURE_ID}") - else() - __append_IAR_tool(LINKER "xlink") - endif() - - else() - message(FATAL_ERROR "Failed to find linker and librarian for ${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID} on ${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}.") - endif() - - unset(_CMAKE_IAR_LOWER_ARCHITECTURE_ID) - unset(_CMAKE_IAR_ITOOLS) - unset(_CMAKE_IAR_XTOOLS) + # Get the architecture from the IAR compiler parent directory + get_filename_component(__iar_bin_dir "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY) + get_filename_component(__iar_toolkit_dir "${__iar_bin_dir}" DIRECTORY) + get_filename_component(__iar_arch_id "${__iar_toolkit_dir}" NAME) + # IAR Archive Tool + set(_CMAKE_AR_NAMES + "iarchive" "iarchive.exe" + "xar" "xar.exe" + ) + # IAR Linker + set(_CMAKE_LINKER_NAMES + "ilink${__iar_arch_id}" "ilink${__iar_arch_id}.exe" + "xlink${__iar_arch_id}" "xlink${__iar_arch_id}.exe" + "xlink" "xlink.exe" + ) + # IAR ELF Dumper + set(_CMAKE_IAR_ELFDUMP_NAMES + "ielfdump${__iar_arch_id}" "ielfdump${__iar_arch_id}.exe" + ) + # IAR ELF Tool + set(_CMAKE_IAR_ELFTOOL_NAMES + "ielftool" "ielftool.exe" + ) + # IAR ELF Exe to Object Tool + set(_CMAKE_IAR_EXE2OBJ_NAMES + "iexe2obj" "iexe2obj.exe" + ) + # IAR Object File Manipulator + set(_CMAKE_IAR_OBJMANIP_NAMES + "iobjmanip" "iobjmanip.exe" + ) + # IAR Absolute Symbol Exporter + set(_CMAKE_IAR_SYMEXPORT_NAMES + "isymexport" "isymexport.exe" + ) + list(APPEND _CMAKE_TOOL_VARS AR LINKER IAR_ELFDUMP IAR_ELFTOOL IAR_EXE2OBJ IAR_OBJMANIP IAR_SYMEXPORT) + unset(__iar_bin_dir) + unset(__iar_toolkit_dir) + unset(__iar_arch_id) # in all other cases search for ar, ranlib, etc. else() diff --git a/Modules/CMakeFindFrameworks.cmake b/Modules/CMakeFindFrameworks.cmake index 1aa392947..46010f59e 100644 --- a/Modules/CMakeFindFrameworks.cmake +++ b/Modules/CMakeFindFrameworks.cmake @@ -5,6 +5,9 @@ CMakeFindFrameworks ------------------- +.. deprecated:: 3.31 + This module does nothing, unless policy :policy:`CMP0173` is set to ``OLD``. + helper module to find OSX frameworks This module reads hints about search locations from variables:: @@ -12,6 +15,25 @@ This module reads hints about search locations from variables:: CMAKE_FIND_FRAMEWORK_EXTRA_LOCATIONS - Extra directories #]=======================================================================] +cmake_policy(GET CMP0173 _cmp0173) +if(_cmp0173 STREQUAL "NEW") + message(FATAL_ERROR + "CMakeFindFrameworks.cmake is not maintained and lacks support for more " + "recent framework handling. It will be removed in a future version of " + "CMake. Update the code to use find_library() instead. " + "Use of this module is now an error according to policy CMP0173." + ) +elseif(_cmp0173 STREQUAL "") + # CMake will have already emitted the standard policy warning for the point + # of inclusion. We only need to add the context-specific info here. + message(AUTHOR_WARNING + "CMakeFindFrameworks.cmake is not maintained and lacks support for more " + "recent framework handling. It will be removed in a future version of " + "CMake. Update the code to use find_library() instead." + ) +endif () +unset(_cmp0173) + if(NOT CMAKE_FIND_FRAMEWORKS_INCLUDED) set(CMAKE_FIND_FRAMEWORKS_INCLUDED 1) macro(CMAKE_FIND_FRAMEWORKS fwk) diff --git a/Modules/CMakeFortranCompilerId.F.in b/Modules/CMakeFortranCompilerId.F.in index a040073b1..d97bd684b 100644 --- a/Modules/CMakeFortranCompilerId.F.in +++ b/Modules/CMakeFortranCompilerId.F.in @@ -179,6 +179,11 @@ # elif defined(__FRT_version__) PRINT *, 'INFO:compiler_version['//__FRT_version__//']' # endif +#elif defined(__LFORTRAN__) + PRINT *, 'INFO:compiler[LFortran]' +#define COMPILER_VERSION_MAJOR DEC(__LFORTRAN_MAJOR__) +#define COMPILER_VERSION_MINOR DEC(__LFORTRAN_MINOR__) +#define COMPILER_VERSION_PATCH DEC(__LFORTRAN_PATCHLEVEL__) #else PRINT *, 'INFO:compiler[]' #endif diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index 984a39d64..a376e10ce 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake @@ -129,5 +129,7 @@ if(CMAKE_Fortran_STANDARD_LIBRARIES_INIT) mark_as_advanced(CMAKE_Fortran_STANDARD_LIBRARIES) endif() +set(CMAKE_Fortran_USE_LINKER_INFORMATION TRUE) + # set this variable so we can avoid loading this more than once. set(CMAKE_Fortran_INFORMATION_LOADED 1) diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake index ccfde60ef..88e3bfa59 100644 --- a/Modules/CMakeGenericSystem.cmake +++ b/Modules/CMakeGenericSystem.cmake @@ -78,6 +78,13 @@ if(NOT DEFINED CMAKE_EXPORT_COMPILE_COMMANDS AND CMAKE_GENERATOR MATCHES "Ninja| mark_as_advanced(CMAKE_EXPORT_COMPILE_COMMANDS) endif() +if(NOT DEFINED CMAKE_EXPORT_BUILD_DATABASE AND CMAKE_GENERATOR MATCHES "Ninja") + set(CMAKE_EXPORT_BUILD_DATABASE "$ENV{CMAKE_EXPORT_BUILD_DATABASE}" + CACHE BOOL "Enable/Disable output of build database during the build." + ) + mark_as_advanced(CMAKE_EXPORT_BUILD_DATABASE) +endif() + # GetDefaultWindowsPrefixBase # # Compute the base directory for CMAKE_INSTALL_PREFIX based on: diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 601ffafe7..e6a472071 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in @@ -3,6 +3,8 @@ set(CMAKE_HIP_HOST_COMPILER "@CMAKE_HIP_HOST_COMPILER@") set(CMAKE_HIP_HOST_LINK_LAUNCHER "@CMAKE_HIP_HOST_LINK_LAUNCHER@") set(CMAKE_HIP_COMPILER_ID "@CMAKE_HIP_COMPILER_ID@") set(CMAKE_HIP_COMPILER_VERSION "@CMAKE_HIP_COMPILER_VERSION@") +set(CMAKE_HIP_HOST_COMPILER_ID "@CMAKE_HIP_HOST_COMPILER_ID@") +set(CMAKE_HIP_HOST_COMPILER_VERSION "@CMAKE_HIP_HOST_COMPILER_VERSION@") set(CMAKE_HIP_STANDARD_COMPUTED_DEFAULT "@CMAKE_HIP_STANDARD_COMPUTED_DEFAULT@") set(CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT@") set(CMAKE_HIP_STANDARD_LATEST "@CMAKE_HIP_STANDARD_LATEST@") diff --git a/Modules/CMakeHIPCompilerId.hip.in b/Modules/CMakeHIPCompilerId.hip.in index fa9766702..54a051a3e 100644 --- a/Modules/CMakeHIPCompilerId.hip.in +++ b/Modules/CMakeHIPCompilerId.hip.in @@ -16,6 +16,28 @@ char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]"; @CMAKE_HIP_COMPILER_ID_PLATFORM_CONTENT@ @CMAKE_HIP_COMPILER_ID_ERROR_FOR_TEST@ +#ifdef HOST_COMPILER_ID +char const* info_host_compiler = "INFO" ":" "host_compiler[" HOST_COMPILER_ID "]"; +#endif +#ifdef HOST_COMPILER_VERSION +char const* info_host_compiler_version = "INFO" ":" "host_compiler_version[" HOST_COMPILER_VERSION "]"; +#elif defined(HOST_COMPILER_VERSION_MAJOR) +char const info_host_compiler_version[] = { + 'I', 'N', 'F', 'O', ':','h','o','s','t','_', + 'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[', + HOST_COMPILER_VERSION_MAJOR, +# ifdef HOST_COMPILER_VERSION_MINOR + '.', HOST_COMPILER_VERSION_MINOR, +# ifdef HOST_COMPILER_VERSION_PATCH + '.', HOST_COMPILER_VERSION_PATCH, +# ifdef HOST_COMPILER_VERSION_TWEAK + '.', HOST_COMPILER_VERSION_TWEAK, +# endif +# endif +# endif + ']','\0'}; +#endif +# #define CXX_STD_98 199711L #define CXX_STD_11 201103L #define CXX_STD_14 201402L @@ -66,6 +88,12 @@ int main(int argc, char* argv[]) #endif #ifdef SIMULATE_VERSION_MAJOR require += info_simulate_version[argc]; +#endif +#ifdef HOST_COMPILER_ID + require += info_host_compiler[argc]; +#endif +#ifdef HOST_COMPILER_VERSION_MAJOR + require += info_host_compiler_version[argc]; #endif require += info_language_standard_default[argc]; require += info_language_extensions_default[argc]; diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake index dc76c6341..c013df824 100644 --- a/Modules/CMakeHIPInformation.cmake +++ b/Modules/CMakeHIPInformation.cmake @@ -98,6 +98,8 @@ if(NOT CMAKE_HIP_LINK_EXECUTABLE) " -o ") endif() +set(CMAKE_HIP_USE_LINKER_INFORMATION TRUE) + set(CMAKE_HIP_INFORMATION_LOADED 1) # Load the file and find the relevant HIP runtime. diff --git a/Modules/CMakeISPCInformation.cmake b/Modules/CMakeISPCInformation.cmake index 5acb68287..4a4b72aa4 100644 --- a/Modules/CMakeISPCInformation.cmake +++ b/Modules/CMakeISPCInformation.cmake @@ -62,4 +62,6 @@ if(NOT CMAKE_ISPC_COMPILE_OBJECT) " -o --emit-obj -h ") endif() +set(CMAKE_ISPC_USE_LINKER_INFORMATION FALSE) + set(CMAKE_ISPC_INFORMATION_LOADED 1) diff --git a/Modules/CMakeJavaInformation.cmake b/Modules/CMakeJavaInformation.cmake index 989afc11b..0ca839599 100644 --- a/Modules/CMakeJavaInformation.cmake +++ b/Modules/CMakeJavaInformation.cmake @@ -47,3 +47,5 @@ if(WIN32 AND NOT CYGWIN) else() set(CMAKE_INCLUDE_FLAG_SEP_Java ":") endif() + +set(CMAKE_Java_USE_LINKER_INFORMATION FALSE) diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake index bcb8b11ae..56e8239bf 100644 --- a/Modules/CMakeOBJCInformation.cmake +++ b/Modules/CMakeOBJCInformation.cmake @@ -187,4 +187,6 @@ if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG) set(CMAKE_EXECUTABLE_RPATH_LINK_OBJC_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG}) endif() +set(CMAKE_OBJC_USE_LINKER_INFORMATION TRUE) + set(CMAKE_OBJC_INFORMATION_LOADED 1) diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake index 4fcd469e3..a242dab04 100644 --- a/Modules/CMakeOBJCXXInformation.cmake +++ b/Modules/CMakeOBJCXXInformation.cmake @@ -179,4 +179,6 @@ mark_as_advanced( CMAKE_VERBOSE_MAKEFILE ) +set(CMAKE_OBJCXX_USE_LINKER_INFORMATION TRUE) + set(CMAKE_OBJCXX_INFORMATION_LOADED 1) diff --git a/Modules/CMakePackageConfigHelpers.cmake b/Modules/CMakePackageConfigHelpers.cmake index 7d038d4c2..6e74efd47 100644 --- a/Modules/CMakePackageConfigHelpers.cmake +++ b/Modules/CMakePackageConfigHelpers.cmake @@ -219,6 +219,7 @@ Generating an Apple Platform Selection File [MACOS_INCLUDE_FILE ] [IOS_INCLUDE_FILE ] [IOS_SIMULATOR_INCLUDE_FILE ] + [IOS_CATALYST_INCLUDE_FILE ] [TVOS_INCLUDE_FILE ] [TVOS_SIMULATOR_INCLUDE_FILE ] [WATCHOS_INCLUDE_FILE ] @@ -254,6 +255,11 @@ Generating an Apple Platform Selection File ``IOS_SIMULATOR_INCLUDE_FILE `` File to include if the platform is iOS Simulator. + ``IOS_CATALYST_INCLUDE_FILE `` + .. versionadded:: 3.31 + + File to include if the platform is iOS Catalyst. + ``TVOS_INCLUDE_FILE `` File to include if the platform is tvOS. @@ -507,6 +513,7 @@ function(generate_apple_platform_selection_file _output_file) MACOS_INCLUDE_FILE IOS_INCLUDE_FILE IOS_SIMULATOR_INCLUDE_FILE + IOS_CATALYST_INCLUDE_FILE TVOS_INCLUDE_FILE TVOS_SIMULATOR_INCLUDE_FILE WATCHOS_INCLUDE_FILE diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake index dc09b20e2..d9ecbd885 100644 --- a/Modules/CMakeParseImplicitLinkInfo.cmake +++ b/Modules/CMakeParseImplicitLinkInfo.cmake @@ -34,6 +34,7 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj endif() endfunction() +# FIXME(#26157) linker for Intel legacy compilers is not identified function(cmake_parse_implicit_link_info2 text log_var obj_regex) set(implicit_libs_tmp "") set(implicit_objs_tmp "") @@ -48,14 +49,23 @@ function(cmake_parse_implicit_link_info2 text log_var obj_regex) set(multiValueArgs ) cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(is_lfortran_less_0_40 0) set(is_msvc 0) - if(EXTRA_PARSE_LANGUAGE AND - ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR - "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC")) - set(is_msvc 1) + if(EXTRA_PARSE_LANGUAGE) + if("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR + "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC") + set(is_msvc 1) + elseif("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xLFortran" + AND CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_VERSION VERSION_LESS "0.40") + set(is_lfortran_less_0_40 1) + endif() endif() # Parse implicit linker arguments. set(linker "ld[0-9]*(\\.[a-z]+)?") + if(is_lfortran_less_0_40) + # lfortran < 0.40 has no way to pass -v to clang/gcc driver. + string(APPEND linker "|clang|gcc") + endif() if(is_msvc) string(APPEND linker "|link\\.exe|lld-link(\\.exe)?") endif() @@ -76,6 +86,10 @@ function(cmake_parse_implicit_link_info2 text log_var obj_regex) set(linker_exclude_regex "collect2 version |^[A-Za-z0-9_]+=|/ldfe ") set(linker_tool_regex "^[ \t]*(->|\")?[ \t]*(([^\"]*[/\\])?(${linker}))(\"|,| |$)") set(linker_tool_exclude_regex "cuda-fake-ld|-fuse-ld=|^ExecuteExternalTool ") + if(is_lfortran_less_0_40) + # lfortran < 0.40 has no way to pass -v to clang/gcc driver. + string(APPEND linker_tool_exclude_regex "|^clang |^gcc ") + endif() set(linker_tool "NOTFOUND") set(linker_tool_fallback "") set(link_line_parsed 0) @@ -187,7 +201,7 @@ function(cmake_parse_implicit_link_info2 text log_var obj_regex) if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS) # Unix library. set(lib "${CMAKE_MATCH_1}") - if(search_static AND lib MATCHES "^(gfortran|stdc\\+\\+)$") + if(search_static AND lib MATCHES "^(gfortran|quadmath|stdc\\+\\+)$") # Search for the static library later, once all link dirs are known. set(lib "SEARCH_STATIC:${lib}") endif() diff --git a/Modules/CMakePushCheckState.cmake b/Modules/CMakePushCheckState.cmake index 3e519ee51..50bd90bc5 100644 --- a/Modules/CMakePushCheckState.cmake +++ b/Modules/CMakePushCheckState.cmake @@ -12,6 +12,7 @@ This module defines three macros: ``CMAKE_PUSH_CHECK_STATE()`` be used to save, restore and reset (i.e., clear contents) the state of the variables ``CMAKE_REQUIRED_FLAGS``, ``CMAKE_REQUIRED_DEFINITIONS``, ``CMAKE_REQUIRED_LINK_OPTIONS``, ``CMAKE_REQUIRED_LIBRARIES``, +``CMAKE_REQUIRED_LINK_DIRECTORIES``, ``CMAKE_REQUIRED_INCLUDES`` and ``CMAKE_EXTRA_INCLUDE_FILES`` used by the various Check-files coming with CMake, like e.g. ``check_function_exists()`` etc. @@ -45,6 +46,7 @@ macro(CMAKE_RESET_CHECK_STATE) set(CMAKE_REQUIRED_DEFINITIONS) set(CMAKE_REQUIRED_LINK_OPTIONS) set(CMAKE_REQUIRED_LIBRARIES) + set(CMAKE_REQUIRED_LINK_DIRECTORIES) set(CMAKE_REQUIRED_FLAGS) set(CMAKE_REQUIRED_QUIET) @@ -58,13 +60,14 @@ macro(CMAKE_PUSH_CHECK_STATE) math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1") - set(_CMAKE_EXTRA_INCLUDE_FILES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_EXTRA_INCLUDE_FILES}) - set(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_INCLUDES}) - set(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS}) - set(_CMAKE_REQUIRED_LINK_OPTIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LINK_OPTIONS}) - set(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LIBRARIES}) - set(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_FLAGS}) - set(_CMAKE_REQUIRED_QUIET_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_QUIET}) + set(_CMAKE_EXTRA_INCLUDE_FILES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_EXTRA_INCLUDE_FILES}) + set(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_INCLUDES}) + set(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS}) + set(_CMAKE_REQUIRED_LINK_OPTIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LINK_OPTIONS}) + set(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LIBRARIES}) + set(_CMAKE_REQUIRED_LINK_DIRECTORIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_LINK_DIRECTORIES}) + set(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_FLAGS}) + set(_CMAKE_REQUIRED_QUIET_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_QUIET}) if (${ARGC} GREATER 0 AND "${ARGV0}" STREQUAL "RESET") cmake_reset_check_state() @@ -77,13 +80,14 @@ macro(CMAKE_POP_CHECK_STATE) # don't pop more than we pushed if("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0") - set(CMAKE_EXTRA_INCLUDE_FILES ${_CMAKE_EXTRA_INCLUDE_FILES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_LINK_OPTIONS ${_CMAKE_REQUIRED_LINK_OPTIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_FLAGS ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) - set(CMAKE_REQUIRED_QUIET ${_CMAKE_REQUIRED_QUIET_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_EXTRA_INCLUDE_FILES ${_CMAKE_EXTRA_INCLUDE_FILES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_INCLUDES ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_LINK_OPTIONS ${_CMAKE_REQUIRED_LINK_OPTIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_LINK_DIRECTORIES ${_CMAKE_REQUIRED_LINK_DIRECTORIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_FLAGS ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) + set(CMAKE_REQUIRED_QUIET ${_CMAKE_REQUIRED_QUIET_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}}) math(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1") endif() diff --git a/Modules/CMakeRCInformation.cmake b/Modules/CMakeRCInformation.cmake index b634796b7..d7164691f 100644 --- a/Modules/CMakeRCInformation.cmake +++ b/Modules/CMakeRCInformation.cmake @@ -46,5 +46,7 @@ if(NOT CMAKE_RC_COMPILE_OBJECT) " /fo ") endif() +set(CMAKE_RC_USE_LINKER_INFORMATION FALSE) + # set this variable so we can avoid loading this more than once. set(CMAKE_RC_INFORMATION_LOADED 1) diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 869a50863..904a52824 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -108,15 +108,6 @@ else() endif() unset(__SWIFT_COMP_MODE_CMP0157) -if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") - if(NOT DEFINED CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG) - set(CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") - endif() - if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) - set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) - endif() -endif() - cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags") if(NOT CMAKE_Swift_NUM_THREADS MATCHES "^[0-9]+$") @@ -181,4 +172,6 @@ else() endif() endif() +set(CMAKE_Swift_USE_LINKER_INFORMATION TRUE) + set(CMAKE_Swift_INFORMATION_LOADED 1) diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake index ed9a7a5f5..3afe08266 100644 --- a/Modules/CPack.cmake +++ b/Modules/CPack.cmake @@ -885,6 +885,20 @@ endif() # WiX specific variables _cpack_set_default(CPACK_WIX_SIZEOF_VOID_P "${CMAKE_SIZEOF_VOID_P}") +if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE) + cmake_policy(GET CMP0172 _CPack_CMP0172) + if("x${_CPack_CMP0172}x" STREQUAL "xNEWx") + _cpack_set_default(CPACK_WIX_INSTALL_SCOPE perMachine) + elseif(NOT "x${_CPack_CMP0172}x" STREQUAL "xOLDx" AND CMAKE_POLICY_WARNING_CMP0172) + cmake_policy(GET_WARNING CMP0172 _CMP0172_warning) + message(AUTHOR_WARNING + "${_CMP0172_warning}\n" + "For compatibility, CMake will not enable per-machine installation by default in the CPack WIX Generator." + ) + unset(_CMP0172_warning) + endif() + unset(_CPack_CMP0172) +endif() # productbuild specific variables cmake_policy(GET CMP0161 _CPack_CMP0161) diff --git a/Modules/CheckCCompilerFlag.cmake b/Modules/CheckCCompilerFlag.cmake index 27b7ae879..7e58012c2 100644 --- a/Modules/CheckCCompilerFlag.cmake +++ b/Modules/CheckCCompilerFlag.cmake @@ -5,7 +5,7 @@ CheckCCompilerFlag ------------------ -Check whether the C compiler supports a given flag. +Check once whether the C compiler supports a given flag. .. command:: check_c_compiler_flag @@ -13,22 +13,25 @@ Check whether the C compiler supports a given flag. check_c_compiler_flag( ) - Check that the ```` is accepted by the compiler without - a diagnostic. Stores the result in an internal cache entry - named ````. +Check once that the ```` is accepted by the compiler without a diagnostic. +The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -The check is only performed once, with the result cached in the variable named -by ````. Every subsequent CMake run will reuse this cached value -rather than performing the check again, even if the ```` changes. In -order to force the check to be re-evaluated, the variable named by -```` must be manually removed from the cache. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. + +See also :command:`check_compiler_flag` for a more general command syntax. The compile and link commands can be influenced by setting any of the -following variables prior to calling ``check_c_compiler_flag()`` +following variables prior to calling ``check_c_compiler_flag()``. Unknown flags +in these variables can case a false negative result. .. include:: /module/CMAKE_REQUIRED_FLAGS.txt @@ -40,6 +43,8 @@ following variables prior to calling ``check_c_compiler_flag()`` .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckCSourceCompiles.cmake b/Modules/CheckCSourceCompiles.cmake index 79aca932a..74c54fa31 100644 --- a/Modules/CheckCSourceCompiles.cmake +++ b/Modules/CheckCSourceCompiles.cmake @@ -5,7 +5,7 @@ CheckCSourceCompiles -------------------- -Check if given C source compiles and links into an executable. +Check once if C source code can be built. .. command:: check_c_source_compiles @@ -14,19 +14,23 @@ Check if given C source compiles and links into an executable. check_c_source_compiles( [FAIL_REGEX [...]]) - Check that the source supplied in ```` can be compiled as a C source - file and linked as an executable (so it must contain at least a ``main()`` - function). The result will be stored in the internal cache variable specified - by ````, with a boolean true value for success and boolean false - for failure. If ``FAIL_REGEX`` is provided, then failure is determined by - checking if anything in the output matches any of the specified regular + Check once that the source supplied in ```` can be built. The result is + stored in the internal cache variable specified by ````, with + boolean ``true`` for success and boolean ``false`` for failure. + + If ``FAIL_REGEX`` is provided, then failure is determined by checking + if anything in the compiler output matches any of the specified regular expressions. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. + + See also :command:`check_source_compiles` for a more general command syntax. + + See also :command:`check_source_runs` to run compiled source. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_c_source_compiles()``: @@ -41,6 +45,8 @@ Check if given C source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckCSourceRuns.cmake b/Modules/CheckCSourceRuns.cmake index e06bcca6f..4559215c1 100644 --- a/Modules/CheckCSourceRuns.cmake +++ b/Modules/CheckCSourceRuns.cmake @@ -5,7 +5,7 @@ CheckCSourceRuns ---------------- -Check if given C source compiles and links into an executable and can +Check once if given C source compiles and links into an executable and can subsequently be run. .. command:: check_c_source_runs @@ -14,18 +14,16 @@ subsequently be run. check_c_source_runs( ) - Check that the source supplied in ```` can be compiled as a C source - file, linked as an executable and then run. The ```` must contain at - least a ``main()`` function. If the ```` could be built and run - successfully, the internal cache variable specified by ```` will - be set to 1, otherwise it will be set to an value that evaluates to boolean - false (e.g. an empty string or an error message). + Check once that the source supplied in ```` can be built, linked as an + executable, and then run. The ```` must contain at least a ``main()`` + function. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. + + See also :command:`check_source_runs` for a more general command syntax. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_c_source_runs()``: @@ -40,6 +38,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckCXXCompilerFlag.cmake b/Modules/CheckCXXCompilerFlag.cmake index 61ed640d0..ec719e17f 100644 --- a/Modules/CheckCXXCompilerFlag.cmake +++ b/Modules/CheckCXXCompilerFlag.cmake @@ -5,7 +5,7 @@ CheckCXXCompilerFlag ------------------------ -Check whether the CXX compiler supports a given flag. +Check once whether the CXX compiler supports a given flag. .. command:: check_cxx_compiler_flag @@ -13,18 +13,39 @@ Check whether the CXX compiler supports a given flag. check_cxx_compiler_flag( ) - Check that the ```` is accepted by the compiler without - a diagnostic. Stores the result in an internal cache entry - named ````. +Check once that the ```` is accepted by the compiler without a diagnostic. +The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -.. note:: - Since the :command:`try_compile` command forwards flags from variables - like :variable:`CMAKE_CXX_FLAGS _FLAGS>`, unknown flags - in such variables may cause a false negative for this check. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. + +See also :command:`check_compiler_flag` for a more general command syntax. + +The compile and link commands can be influenced by setting any of the +following variables prior to calling ``check_cxx_compiler_flag()``. Unknown flags +in these variables can case a false negative result. + +.. include:: /module/CMAKE_REQUIRED_FLAGS.txt + +.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt + +.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt + +.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt + +.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt + +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + +.. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] include_guard(GLOBAL) diff --git a/Modules/CheckCXXSourceCompiles.cmake b/Modules/CheckCXXSourceCompiles.cmake index 7531236e8..90c3e6a1a 100644 --- a/Modules/CheckCXXSourceCompiles.cmake +++ b/Modules/CheckCXXSourceCompiles.cmake @@ -5,7 +5,7 @@ CheckCXXSourceCompiles ---------------------- -Check if given C++ source compiles and links into an executable. +Check once if C++ source code can be built. .. command:: check_cxx_source_compiles @@ -14,19 +14,23 @@ Check if given C++ source compiles and links into an executable. check_cxx_source_compiles( [FAIL_REGEX [...]]) - Check that the source supplied in ```` can be compiled as a C++ source - file and linked as an executable (so it must contain at least a ``main()`` - function). The result will be stored in the internal cache variable specified - by ````, with a boolean true value for success and boolean false - for failure. If ``FAIL_REGEX`` is provided, then failure is determined by - checking if anything in the output matches any of the specified regular + Check once that the source supplied in ```` can be built. The result is + stored in the internal cache variable specified by ````, with + boolean ``true`` for success and boolean ``false`` for failure. + + If ``FAIL_REGEX`` is provided, then failure is determined by checking + if anything in the compiler output matches any of the specified regular expressions. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. + + See also :command:`check_source_compiles` for a more general command syntax. + + See also :command:`check_source_runs` to run compiled source. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_cxx_source_compiles()``: @@ -41,6 +45,8 @@ Check if given C++ source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckCXXSourceRuns.cmake b/Modules/CheckCXXSourceRuns.cmake index eb643eb12..f0a5ffcdd 100644 --- a/Modules/CheckCXXSourceRuns.cmake +++ b/Modules/CheckCXXSourceRuns.cmake @@ -5,7 +5,7 @@ CheckCXXSourceRuns ------------------ -Check if given C++ source compiles and links into an executable and can +Check once if given C++ source compiles and links into an executable and can subsequently be run. .. command:: check_cxx_source_runs @@ -14,18 +14,16 @@ subsequently be run. check_cxx_source_runs( ) - Check that the source supplied in ```` can be compiled as a C++ source - file, linked as an executable and then run. The ```` must contain at - least a ``main()`` function. If the ```` could be built and run - successfully, the internal cache variable specified by ```` will - be set to 1, otherwise it will be set to an value that evaluates to boolean - false (e.g. an empty string or an error message). + Check once that the source supplied in ```` can be built, linked as an + executable, and then run. The ```` must contain at least a ``main()`` + function. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. + + See also :command:`check_source_runs` for a more general command syntax. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_cxx_source_runs()``: @@ -40,6 +38,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake index d807c17d1..bcd514f2e 100644 --- a/Modules/CheckCXXSymbolExists.cmake +++ b/Modules/CheckCXXSymbolExists.cmake @@ -51,6 +51,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt For example: diff --git a/Modules/CheckCompilerFlag.cmake b/Modules/CheckCompilerFlag.cmake index 0f2ec4c3f..d50514284 100644 --- a/Modules/CheckCompilerFlag.cmake +++ b/Modules/CheckCompilerFlag.cmake @@ -7,7 +7,7 @@ CheckCompilerFlag .. versionadded:: 3.19 -Check whether the compiler supports a given flag. +Check once whether the ```` compiler supports a given flag. .. command:: check_compiler_flag @@ -15,21 +15,23 @@ Check whether the compiler supports a given flag. check_compiler_flag( ) -Check that the ```` is accepted by the compiler without a diagnostic. -Stores the result in an internal cache entry named ````. +Check once that the ```` is accepted by the ```` compiler without +a diagnostic. The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -The check is only performed once, with the result cached in the variable named -by ````. Every subsequent CMake run will reuse this cached value -rather than performing the check again, even if the ```` changes. In -order to force the check to be re-evaluated, the variable named by -```` must be manually removed from the cache. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. The compile and link commands can be influenced by setting any of the -following variables prior to calling ``check_compiler_flag()`` +following variables prior to calling ``check_compiler_flag()``. Unknown flags +in these variables can case a false negative result. .. include:: /module/CMAKE_REQUIRED_FLAGS.txt @@ -41,6 +43,8 @@ following variables prior to calling ``check_compiler_flag()`` .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckFortranCompilerFlag.cmake b/Modules/CheckFortranCompilerFlag.cmake index 81a234562..eee7deb2b 100644 --- a/Modules/CheckFortranCompilerFlag.cmake +++ b/Modules/CheckFortranCompilerFlag.cmake @@ -7,7 +7,7 @@ CheckFortranCompilerFlag .. versionadded:: 3.3 -Check whether the Fortran compiler supports a given flag. +Check once whether the Fortran compiler supports a given flag. .. command:: check_fortran_compiler_flag @@ -15,22 +15,25 @@ Check whether the Fortran compiler supports a given flag. check_fortran_compiler_flag( ) - Check that the ```` is accepted by the compiler without - a diagnostic. Stores the result in an internal cache entry - named ````. +Check once that the ```` is accepted by the compiler without a diagnostic. +The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -The check is only performed once, with the result cached in the variable named -by ````. Every subsequent CMake run will reuse this cached value -rather than performing the check again, even if the ```` changes. In -order to force the check to be re-evaluated, the variable named by -```` must be manually removed from the cache. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. + +See also :command:`check_compiler_flag` for a more general command syntax. The compile and link commands can be influenced by setting any of the -following variables prior to calling ``check_fortran_compiler_flag()`` +following variables prior to calling ``check_fortran_compiler_flag()``. Unknown +flags in these variables can case a false negative result. .. include:: /module/CMAKE_REQUIRED_FLAGS.txt @@ -42,6 +45,8 @@ following variables prior to calling ``check_fortran_compiler_flag()`` .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckFortranFunctionExists.cmake b/Modules/CheckFortranFunctionExists.cmake index 4e5a246da..6583c9a91 100644 --- a/Modules/CheckFortranFunctionExists.cmake +++ b/Modules/CheckFortranFunctionExists.cmake @@ -29,16 +29,11 @@ Check if a Fortran function exists. The following variables may be set before calling this macro to modify the way the check is run: -``CMAKE_REQUIRED_LINK_OPTIONS`` - .. versionadded:: 3.14 - A :ref:`;-list ` of options to add to the link - command (see :command:`try_compile` for further details). +.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt -``CMAKE_REQUIRED_LIBRARIES`` - A :ref:`;-list ` of libraries to add to the link - command. These can be the name of system libraries or they can be - :ref:`Imported Targets ` (see :command:`try_compile` for - further details). +.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt + +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt #]=======================================================================] include_guard(GLOBAL) @@ -58,6 +53,12 @@ macro(CHECK_FORTRAN_FUNCTION_EXISTS FUNCTION VARIABLE) else() set(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CFFE_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CFFE_LINK_DIRECTORIES) + endif() set(__CheckFunction_testFortranCompilerSource " program TESTFortran @@ -70,8 +71,11 @@ macro(CHECK_FORTRAN_FUNCTION_EXISTS FUNCTION VARIABLE) SOURCE_FROM_VAR testFortranCompiler.f __CheckFunction_testFortranCompilerSource ${CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS} ${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES} + CMAKE_FLAGS + "${_CFFE_LINK_DIRECTORIES}" ) unset(__CheckFunction_testFortranCompilerSource) + unset(_CFFE_LINK_DIRECTORIES) if(${VARIABLE}) set(${VARIABLE} 1 CACHE INTERNAL "Have Fortran function ${FUNCTION}") message(CHECK_PASS "found") diff --git a/Modules/CheckFortranSourceCompiles.cmake b/Modules/CheckFortranSourceCompiles.cmake index ed374ef11..68e289c04 100644 --- a/Modules/CheckFortranSourceCompiles.cmake +++ b/Modules/CheckFortranSourceCompiles.cmake @@ -7,7 +7,7 @@ CheckFortranSourceCompiles .. versionadded:: 3.1 -Check if given Fortran source compiles and links into an executable. +Check once if Fortran source code can be built. .. command:: check_fortran_source_compiles @@ -18,40 +18,27 @@ Check if given Fortran source compiles and links into an executable. [SRC_EXT ] ) - Checks that the source supplied in ```` can be compiled as a Fortran - source file and linked as an executable. The ```` must be a Fortran - ``program``. - - .. code-block:: cmake - - check_fortran_source_compiles("program test - error stop - end program" - HAVE_ERROR_STOP - SRC_EXT .F90) - - This command can help avoid costly build processes when a compiler lacks support - for a necessary feature, or a particular vendor library is not compatible with - the Fortran compiler version being used. This generate-time check may advise the - user of such before the main build process. See also the - :command:`check_fortran_source_runs` command to run the compiled code. - - The result will be stored in the internal cache - variable ````, with a boolean true value for success and boolean - false for failure. + Check once that the source supplied in ```` can be built. The result is + stored in the internal cache variable specified by ````, with + boolean ``true`` for success and boolean ``false`` for failure. If ``FAIL_REGEX`` is provided, then failure is determined by checking - if anything in the output matches any of the specified regular expressions. + if anything in the compiler output matches any of the specified regular + expressions. By default, the test source file will be given a ``.F`` file extension. The ``SRC_EXT`` option can be used to override this with ``.`` instead-- ``.F90`` is a typical choice. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + See also :command:`check_source_compiles` for a more general command syntax. + + See also :command:`check_source_runs` to run compiled source. + + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_fortran_source_compiles()``: @@ -66,6 +53,8 @@ Check if given Fortran source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckFortranSourceRuns.cmake b/Modules/CheckFortranSourceRuns.cmake index 9bf9fb2ee..808baf6cb 100644 --- a/Modules/CheckFortranSourceRuns.cmake +++ b/Modules/CheckFortranSourceRuns.cmake @@ -7,7 +7,7 @@ CheckFortranSourceRuns .. versionadded:: 3.14 -Check if given Fortran source compiles and links into an executable and can +Check once if given Fortran source compiles and links into an executable and can subsequently be run. .. command:: check_fortran_source_runs @@ -17,9 +17,13 @@ subsequently be run. check_fortran_source_runs( [SRC_EXT ]) - Check that the source supplied in ```` can be compiled as a Fortran source - file, linked as an executable and then run. The ```` must be a Fortran - ``program``. + Check once that the source supplied in ```` can be built, linked as an + executable, and then run. The ```` must contain a Fortran ``program``. + + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. .. code-block:: cmake @@ -29,25 +33,10 @@ subsequently be run. end program" HAVE_COARRAY) - This command can help avoid costly build processes when a compiler lacks support - for a necessary feature, or a particular vendor library is not compatible with - the Fortran compiler version being used. Some of these failures only occur at runtime - instead of linktime, and a trivial runtime example can catch the issue before the - main build process. - - If the ```` could be built and run - successfully, the internal cache variable specified by ```` will - be set to 1, otherwise it will be set to an value that evaluates to boolean - false (e.g. an empty string or an error message). - By default, the test source file will be given a ``.F90`` file extension. The ``SRC_EXT`` option can be used to override this with ``.`` instead. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + See also :command:`check_source_runs` for a more general command syntax. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_fortran_source_runs()``: @@ -62,6 +51,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckFunctionExists.cmake b/Modules/CheckFunctionExists.cmake index e7c47a4e7..711150946 100644 --- a/Modules/CheckFunctionExists.cmake +++ b/Modules/CheckFunctionExists.cmake @@ -5,7 +5,7 @@ CheckFunctionExists ------------------- -Check if a C function can be linked +Check once if a C function can be linked from system libraries. .. command:: check_function_exists @@ -14,8 +14,7 @@ Check if a C function can be linked check_function_exists( ) Checks that the ```` is provided by libraries on the system and store - the result in a ````, which will be created as an internal - cache variable. + the result in internal cache variable ````. The following variables may be set before calling this macro to modify the way the check is run: @@ -30,12 +29,14 @@ way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt .. note:: - Prefer using :Module:`CheckSymbolExists` instead of this module, - for the following reasons: + Prefer using :module:`CheckSymbolExists` or :module:`CheckSourceCompiles` + instead of this module, for the following reasons: * ``check_function_exists()`` can't detect functions that are inlined in headers or specified as a macro. @@ -68,6 +69,12 @@ macro(CHECK_FUNCTION_EXISTS FUNCTION VARIABLE) else() set(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CFE_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CFE_LINK_DIRECTORIES) + endif() if(CMAKE_REQUIRED_INCLUDES) set(CHECK_FUNCTION_EXISTS_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") @@ -90,8 +97,10 @@ macro(CHECK_FUNCTION_EXISTS FUNCTION VARIABLE) ${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} "${CHECK_FUNCTION_EXISTS_ADD_INCLUDES}" + "${_CFE_LINK_DIRECTORIES}" ) unset(_cfe_source) + unset(_CFE_LINK_DIRECTORIES) if(${VARIABLE}) set(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}") diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake index 1d8c9f766..d16c9f8a8 100644 --- a/Modules/CheckIncludeFile.cmake +++ b/Modules/CheckIncludeFile.cmake @@ -31,6 +31,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt See the :module:`CheckIncludeFiles` module to check for multiple headers @@ -87,6 +89,13 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE) unset(_CIF_CMP0075) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CIF_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CIF_LINK_DIRECTORIES) + endif() + try_compile(${VARIABLE} SOURCE_FROM_VAR CheckIncludeFile.c _CIF_SOURCE_CONTENT COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} @@ -95,9 +104,11 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE) CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_C_INCLUDE_DIRS}" + "${_CIF_LINK_DIRECTORIES}" ) unset(_CIF_LINK_OPTIONS) unset(_CIF_LINK_LIBRARIES) + unset(_CIF_LINK_DIRECTORIES) if(${ARGC} EQUAL 3) set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_SAVE}) diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake index 53d9a456e..f4ac0cfd3 100644 --- a/Modules/CheckIncludeFileCXX.cmake +++ b/Modules/CheckIncludeFileCXX.cmake @@ -31,6 +31,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFiles` @@ -86,6 +88,13 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE) unset(_CIF_CMP0075) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CIF_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CIF_LINK_DIRECTORIES) + endif() + try_compile(${VARIABLE} SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} @@ -94,9 +103,11 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE) CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS}" + "${_CIF_LINK_DIRECTORIES}" ) unset(_CIF_LINK_OPTIONS) unset(_CIF_LINK_LIBRARIES) + unset(_CIF_LINK_DIRECTORIES) if(${ARGC} EQUAL 3) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS_SAVE}) diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake index 071df0c60..332722210 100644 --- a/Modules/CheckIncludeFiles.cmake +++ b/Modules/CheckIncludeFiles.cmake @@ -37,6 +37,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFileCXX` @@ -126,6 +128,13 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE) unset(_CIF_CMP0075) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CIF_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CIF_LINK_DIRECTORIES) + endif() + if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_START "Looking for ${_description}") endif() @@ -137,9 +146,11 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE) CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILES_FLAGS} "${CHECK_INCLUDE_FILES_INCLUDE_DIRS}" + "${_CIF_LINK_DIRECTORIES}" ) unset(_CIF_LINK_OPTIONS) unset(_CIF_LINK_LIBRARIES) + unset(_CIF_LINK_DIRECTORIES) if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_PASS "found") diff --git a/Modules/CheckLibraryExists.cmake b/Modules/CheckLibraryExists.cmake index 834050080..58be34208 100644 --- a/Modules/CheckLibraryExists.cmake +++ b/Modules/CheckLibraryExists.cmake @@ -5,7 +5,7 @@ CheckLibraryExists ------------------ -Check if the function exists. +Check once if the function exists in system or specified library. .. command:: CHECK_LIBRARY_EXISTS @@ -18,10 +18,11 @@ Check if the function exists. LIBRARY - the name of the library you are looking for FUNCTION - the name of the function LOCATION - location where the library should be found - VARIABLE - variable to store the result - Will be created as an internal cache variable. - + VARIABLE - internal cache variable to store the result +Prefer using :module:`CheckSymbolExists` or :module:`CheckSourceCompiles` +instead of this module for more robust detection if a function is available in +a library. The following variables may be set before calling this macro to modify the way the check is run: @@ -34,6 +35,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] @@ -57,6 +60,12 @@ macro(CHECK_LIBRARY_EXISTS LIBRARY FUNCTION LOCATION VARIABLE) set(CHECK_LIBRARY_EXISTS_LIBRARIES ${CHECK_LIBRARY_EXISTS_LIBRARIES} ${CMAKE_REQUIRED_LIBRARIES}) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CLE_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${LOCATION};${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CLE_LINK_DIRECTORIES "-DLINK_DIRECTORIES:STRING=${LOCATION}") + endif() if(CMAKE_C_COMPILER_LOADED) set(_cle_source CheckFunctionExists.c) @@ -73,9 +82,10 @@ macro(CHECK_LIBRARY_EXISTS LIBRARY FUNCTION LOCATION VARIABLE) LINK_LIBRARIES ${CHECK_LIBRARY_EXISTS_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_LIBRARY_EXISTS_DEFINITION} - -DLINK_DIRECTORIES:STRING=${LOCATION} + "${_CLE_LINK_DIRECTORIES}" ) unset(_cle_source) + unset(_CLE_LINK_DIRECTORIES) if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET) diff --git a/Modules/CheckOBJCCompilerFlag.cmake b/Modules/CheckOBJCCompilerFlag.cmake index f6d259e6e..183c7ec6d 100644 --- a/Modules/CheckOBJCCompilerFlag.cmake +++ b/Modules/CheckOBJCCompilerFlag.cmake @@ -7,7 +7,7 @@ CheckOBJCCompilerFlag .. versionadded:: 3.16 -Check whether the Objective-C compiler supports a given flag. +Check once whether the Objective-C compiler supports a given flag. .. command:: check_objc_compiler_flag @@ -15,22 +15,25 @@ Check whether the Objective-C compiler supports a given flag. check_objc_compiler_flag( ) - Check that the ```` is accepted by the compiler without - a diagnostic. Stores the result in an internal cache entry - named ````. +Check once that the ```` is accepted by the compiler without a diagnostic. +The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -The check is only performed once, with the result cached in the variable named -by ````. Every subsequent CMake run will reuse this cached value -rather than performing the check again, even if the ```` changes. In -order to force the check to be re-evaluated, the variable named by -```` must be manually removed from the cache. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. + +See also :command:`check_compiler_flag` for a more general command syntax. The compile and link commands can be influenced by setting any of the -following variables prior to calling ``check_objc_compiler_flag()`` +following variables prior to calling ``check_objc_compiler_flag()``. Unknown +flags in these variables can case a false negative result. .. include:: /module/CMAKE_REQUIRED_FLAGS.txt @@ -42,6 +45,8 @@ following variables prior to calling ``check_objc_compiler_flag()`` .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckOBJCSourceCompiles.cmake b/Modules/CheckOBJCSourceCompiles.cmake index bc0cac1a1..11d8ea918 100644 --- a/Modules/CheckOBJCSourceCompiles.cmake +++ b/Modules/CheckOBJCSourceCompiles.cmake @@ -7,7 +7,7 @@ CheckOBJCSourceCompiles .. versionadded:: 3.16 -Check if given Objective-C source compiles and links into an executable. +Check once if Objective-C source can be built. .. command:: check_objc_source_compiles @@ -16,19 +16,23 @@ Check if given Objective-C source compiles and links into an executable. check_objc_source_compiles( [FAIL_REGEX [...]]) - Check that the source supplied in ```` can be compiled as a Objectie-C source - file and linked as an executable (so it must contain at least a ``main()`` - function). The result will be stored in the internal cache variable specified - by ````, with a boolean true value for success and boolean false - for failure. If ``FAIL_REGEX`` is provided, then failure is determined by - checking if anything in the output matches any of the specified regular + Check once that the source supplied in ```` can be built. The result is + stored in the internal cache variable specified by ````, with + boolean ``true`` for success and boolean ``false`` for failure. + + If ``FAIL_REGEX`` is provided, then failure is determined by checking + if anything in the compiler output matches any of the specified regular expressions. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. + + See also :command:`check_source_compiles` for a more general command syntax. + + See also :command:`check_source_runs` to run compiled source. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_objc_source_compiles()`` @@ -43,6 +47,8 @@ Check if given Objective-C source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckOBJCSourceRuns.cmake b/Modules/CheckOBJCSourceRuns.cmake index 511cac6ac..0611d6744 100644 --- a/Modules/CheckOBJCSourceRuns.cmake +++ b/Modules/CheckOBJCSourceRuns.cmake @@ -7,8 +7,8 @@ CheckOBJCSourceRuns .. versionadded:: 3.16 -Check if given Objective-C source compiles and links into an executable and can -subsequently be run. +Check once if given Objective-C source compiles and links into an executable and +can subsequently be run. .. command:: check_objc_source_runs @@ -16,18 +16,16 @@ subsequently be run. check_objc_source_runs( ) - Check that the source supplied in ```` can be compiled as a Objective-C source - file, linked as an executable and then run. The ```` must contain at - least a ``main()`` function. If the ```` could be built and run - successfully, the internal cache variable specified by ```` will - be set to 1, otherwise it will be set to an value that evaluates to boolean - false (e.g. an empty string or an error message). + Check once that the source supplied in ```` can be built, linked as an + executable, and then run. The ```` must contain at least a ``main()`` + function. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. + + See also :command:`check_source_runs` for a more general command syntax. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_objc_source_runs()`` @@ -42,6 +40,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckOBJCXXCompilerFlag.cmake b/Modules/CheckOBJCXXCompilerFlag.cmake index 32d50c9df..e185a3199 100644 --- a/Modules/CheckOBJCXXCompilerFlag.cmake +++ b/Modules/CheckOBJCXXCompilerFlag.cmake @@ -7,7 +7,7 @@ CheckOBJCXXCompilerFlag .. versionadded:: 3.16 -Check whether the Objective-C++ compiler supports a given flag. +Check once whether the Objective-C++ compiler supports a given flag. .. command:: check_objcxx_compiler_flag @@ -15,22 +15,25 @@ Check whether the Objective-C++ compiler supports a given flag. check_objcxx_compiler_flag( ) - Check that the ```` is accepted by the compiler without - a diagnostic. Stores the result in an internal cache entry - named ````. +Check once that the ```` is accepted by the compiler without a diagnostic. +The result is stored in the internal cache variable specified by +````, with boolean ``true`` for success and boolean ``false`` for +failure. -A positive result from this check indicates only that the compiler did not -issue a diagnostic message when given the flag. Whether the flag has any -effect or even a specific one is beyond the scope of this module. +``true`` indicates only that the compiler did not issue a diagnostic message +when given the flag. Whether the flag has any effect is beyond the scope of +this module. -The check is only performed once, with the result cached in the variable named -by ````. Every subsequent CMake run will reuse this cached value -rather than performing the check again, even if the ```` changes. In -order to force the check to be re-evaluated, the variable named by -```` must be manually removed from the cache. +Internally, :command:`try_compile` is used to perform the check. If +:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), +the check compiles and links an executable program. If set to +``STATIC_LIBRARY``, the check is compiled but not linked. + +See also :command:`check_compiler_flag` for a more general command syntax. The compile and link commands can be influenced by setting any of the -following variables prior to calling ``check_objcxx_compiler_flag()`` +following variables prior to calling ``check_objcxx_compiler_flag()``. Unknown +flags in these variables can case a false negative result. .. include:: /module/CMAKE_REQUIRED_FLAGS.txt @@ -42,6 +45,8 @@ following variables prior to calling ``check_objcxx_compiler_flag()`` .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckOBJCXXSourceCompiles.cmake b/Modules/CheckOBJCXXSourceCompiles.cmake index 366d7d5cd..35024d7b3 100644 --- a/Modules/CheckOBJCXXSourceCompiles.cmake +++ b/Modules/CheckOBJCXXSourceCompiles.cmake @@ -7,7 +7,7 @@ CheckOBJCXXSourceCompiles .. versionadded:: 3.16 -Check if given Objective-C++ source compiles and links into an executable. +Check once if Objective-C++ source can be built. .. command:: check_objcxx_source_compiles @@ -16,19 +16,23 @@ Check if given Objective-C++ source compiles and links into an executable. check_objcxx_source_compiles( [FAIL_REGEX [...]]) - Check that the source supplied in ```` can be compiled as a Objective-C++ source - file and linked as an executable (so it must contain at least a ``main()`` - function). The result will be stored in the internal cache variable specified - by ````, with a boolean true value for success and boolean false - for failure. If ``FAIL_REGEX`` is provided, then failure is determined by - checking if anything in the output matches any of the specified regular + Check once that the source supplied in ```` can be built. The result is + stored in the internal cache variable specified by ````, with + boolean ``true`` for success and boolean ``false`` for failure. + + If ``FAIL_REGEX`` is provided, then failure is determined by checking + if anything in the compiler output matches any of the specified regular expressions. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. + + See also :command:`check_source_compiles` for a more general command syntax. + + See also :command:`check_source_runs` to run compiled source. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_objcxx_source_compiles()`` @@ -43,6 +47,8 @@ Check if given Objective-C++ source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckOBJCXXSourceRuns.cmake b/Modules/CheckOBJCXXSourceRuns.cmake index 49db3cb8d..1192ff3ce 100644 --- a/Modules/CheckOBJCXXSourceRuns.cmake +++ b/Modules/CheckOBJCXXSourceRuns.cmake @@ -7,8 +7,8 @@ CheckOBJCXXSourceRuns .. versionadded:: 3.16 -Check if given Objective-C++ source compiles and links into an executable and can -subsequently be run. +Check once if given Objective-C++ source compiles and links into an executable +and can subsequently be run. .. command:: check_objcxx_source_runs @@ -16,18 +16,16 @@ subsequently be run. check_objcxx_source_runs( ) - Check that the source supplied in ```` can be compiled as a Objective-C++ source - file, linked as an executable and then run. The ```` must contain at - least a ``main()`` function. If the ```` could be built and run - successfully, the internal cache variable specified by ```` will - be set to 1, otherwise it will be set to an value that evaluates to boolean - false (e.g. an empty string or an error message). + Check once that the source supplied in ```` can be built, linked as an + executable, and then run. The ```` must contain at least a ``main()`` + function. - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. + + See also :command:`check_source_runs` for a more general command syntax. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_objcxx_source_runs()`` @@ -42,6 +40,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckPrototypeDefinition.cmake b/Modules/CheckPrototypeDefinition.cmake index c1a7a1c57..e2bba1e73 100644 --- a/Modules/CheckPrototypeDefinition.cmake +++ b/Modules/CheckPrototypeDefinition.cmake @@ -45,6 +45,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] @@ -83,6 +85,13 @@ function(check_prototype_definition _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIAB set(CMAKE_SYMBOL_EXISTS_INCLUDES) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CPD_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CPD_LINK_DIRECTORIES) + endif() + foreach(_FILE ${_HEADER}) string(APPEND CHECK_PROTOTYPE_DEFINITION_HEADER "#include <${_FILE}>\n") @@ -102,7 +111,9 @@ function(check_prototype_definition _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIAB ${CHECK_PROTOTYPE_DEFINITION_LIBS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} "${CMAKE_SYMBOL_EXISTS_INCLUDES}" + "${_CPD_LINK_DIRECTORIES}" ) + unset(_CPD_LINK_DIRECTORIES) if (${_VARIABLE}) set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") diff --git a/Modules/CheckSourceCompiles.cmake b/Modules/CheckSourceCompiles.cmake index af905a4e6..2ce6e85c3 100644 --- a/Modules/CheckSourceCompiles.cmake +++ b/Modules/CheckSourceCompiles.cmake @@ -8,7 +8,7 @@ CheckSourceCompiles .. versionadded:: 3.19 -Check if given source compiles and links into an executable. +Check once if source code can be built for a given language. .. command:: check_source_compiles @@ -18,40 +18,50 @@ Check if given source compiles and links into an executable. [FAIL_REGEX [...]] [SRC_EXT ]) - Check that the source supplied in ```` can be compiled as a source - file for the requested language and linked as an executable. The result - will be stored in the internal cache variable specified by ````, - with a boolean true value for success and boolean false for failure. If - ``FAIL_REGEX`` is provided, then failure is determined by checking if - anything in the compiler output matches any of the specified regular + Check once that the source supplied in ```` can be built for code + language ````. The result is stored in the internal cache variable + specified by ````, with boolean ``true`` for success and + boolean ``false`` for failure. + + If ``FAIL_REGEX`` is provided, then failure is determined by checking + if anything in the compiler output matches any of the specified regular expressions. By default, the test source file will be given a file extension that matches the requested language. The ``SRC_EXT`` option can be used to override this with ``.`` instead. - The ```` must contain a valid main program. For example: + The C example checks if the compiler supports the ``noreturn`` attribute: .. code-block:: cmake + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + check_source_compiles(C - "#include - #include - noreturn void f(){ exit(0); } - int main(void) { f(); return 1; }" + "#if !__has_c_attribute(noreturn) + #error \"No noreturn attribute\" + #endif" HAVE_NORETURN) + The Fortran example checks if the compiler supports the ``pure`` procedure + attribute: + + .. code-block:: cmake + + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + check_source_compiles(Fortran - "program test - error stop - end program" - HAVE_ERROR_STOP) + "pure subroutine foo() + end subroutine" + HAVE_PURE) - The check is only performed once, with the result cached in the variable - named by ````. Every subsequent CMake run will reuse this cached - value rather than performing the check again, even if the ```` changes. - In order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. + Internally, :command:`try_compile` is used to compile the source. If + :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``EXECUTABLE`` (default), + the source is compiled and linked as an executable program. If set to + ``STATIC_LIBRARY``, the source is compiled but not linked. In any case, all + functions must be declared as usual. + + See also :command:`check_source_runs` to run compiled source. The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_source_compiles()``: @@ -66,6 +76,8 @@ Check if given source compiles and links into an executable. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckSourceRuns.cmake b/Modules/CheckSourceRuns.cmake index 75636f48e..a0ddfee54 100644 --- a/Modules/CheckSourceRuns.cmake +++ b/Modules/CheckSourceRuns.cmake @@ -18,12 +18,14 @@ subsequently be run. check_source_runs( [SRC_EXT ]) - Check that the source supplied in ```` can be compiled as a source - file for the requested language, linked as an executable and then run. - If the ```` could be built and run successfully, the internal cache variable - specified by ```` will be set to 1, otherwise it will be set to - a value that evaluates to boolean false (e.g. an empty string or an error - message). + Check once that the ```` source supplied in ```` can be built, + linked as an executable, and then run. The ```` must contain at least + a ``main()`` function, or in Fortran a ``program``. + + The result is stored in the internal cache variable specified by + ````. Success of build and run is indicated by boolean ``true``. + Failure to build or run is indicated by boolean ``false`` such as an empty + string or an error message. By default, the test source file will be given a file extension that matches the requested language. The ``SRC_EXT`` option can be used to override this @@ -47,12 +49,6 @@ subsequently be run. end program" HAVE_COARRAY) - The check is only performed once, with the result cached in the variable named - by ````. Every subsequent CMake run will reuse this cached value - rather than performing the check again, even if the ```` changes. In - order to force the check to be re-evaluated, the variable named by - ```` must be manually removed from the cache. - The compile and link commands can be influenced by setting any of the following variables prior to calling ``check_source_runs()`` @@ -66,6 +62,8 @@ subsequently be run. .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt #]=======================================================================] diff --git a/Modules/CheckStructHasMember.cmake b/Modules/CheckStructHasMember.cmake index 959f8e517..a088202b6 100644 --- a/Modules/CheckStructHasMember.cmake +++ b/Modules/CheckStructHasMember.cmake @@ -36,6 +36,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake index 621df7573..a4950df72 100644 --- a/Modules/CheckSymbolExists.cmake +++ b/Modules/CheckSymbolExists.cmake @@ -41,6 +41,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt For example: @@ -121,6 +123,13 @@ macro(__CHECK_SYMBOL_EXISTS_IMPL SOURCEFILE SYMBOL FILES VARIABLE) else() set(CMAKE_SYMBOL_EXISTS_INCLUDES) endif() + + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CSE_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CSE_LINK_DIRECTORIES) + endif() foreach(FILE ${FILES}) string(APPEND _CSE_SOURCE "#include <${FILE}>\n") @@ -159,7 +168,9 @@ int main(int argc, char** argv) CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS} "${CMAKE_SYMBOL_EXISTS_INCLUDES}" + "${_CSE_LINK_DIRECTORIES}" ) + unset(_CSE_LINK_DIRECTORIES) if(${VARIABLE}) if(NOT CMAKE_REQUIRED_QUIET) message(CHECK_PASS "found") diff --git a/Modules/CheckTypeSize.cmake b/Modules/CheckTypeSize.cmake index 849d691b6..ee54d92e2 100644 --- a/Modules/CheckTypeSize.cmake +++ b/Modules/CheckTypeSize.cmake @@ -77,6 +77,8 @@ the way the check is run: .. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt +.. include:: /module/CMAKE_REQUIRED_LINK_DIRECTORIES.txt + .. include:: /module/CMAKE_REQUIRED_QUIET.txt ``CMAKE_EXTRA_INCLUDE_FILES`` @@ -140,6 +142,13 @@ function(__check_type_size_impl type var map builtin language) string(APPEND headers "#include \"${h}\"\n") endforeach() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CTS_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CTS_LINK_DIRECTORIES) + endif() + # Perform the check. set(bin ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.bin) file(READ ${__check_type_size_dir}/CheckTypeSize.c.in src_content) @@ -151,8 +160,10 @@ function(__check_type_size_impl type var map builtin language) CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS}" "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" + "${_CTS_LINK_DIRECTORIES}" COPY_FILE ${bin} ) + unset(_CTS_LINK_DIRECTORIES) if(HAVE_${var}) # The check compiled. Load information from the binary. diff --git a/Modules/CheckVariableExists.cmake b/Modules/CheckVariableExists.cmake index 9e5d710f3..37bef1b69 100644 --- a/Modules/CheckVariableExists.cmake +++ b/Modules/CheckVariableExists.cmake @@ -59,13 +59,23 @@ macro(CHECK_VARIABLE_EXISTS VAR VARIABLE) else() set(CHECK_VARIABLE_EXISTS_ADD_LIBRARIES) endif() + + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CVE_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CVE_LINK_DIRECTORIES) + endif() + try_compile(${VARIABLE} SOURCES ${CMAKE_ROOT}/Modules/CheckVariableExists.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_VARIABLE_EXISTS_ADD_LINK_OPTIONS} ${CHECK_VARIABLE_EXISTS_ADD_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_VARIABLE_DEFINITIONS} + "${_CVE_LINK_DIRECTORIES}" ) + unset(_CVE_LINK_DIRECTORIES) if(${VARIABLE}) set(${VARIABLE} 1 CACHE INTERNAL "Have variable ${VAR}") if(NOT CMAKE_REQUIRED_QUIET) diff --git a/Modules/Compiler/Clang-CUDA.cmake b/Modules/Compiler/Clang-CUDA.cmake index d9929f1f0..57d3a934d 100644 --- a/Modules/Compiler/Clang-CUDA.cmake +++ b/Modules/Compiler/Clang-CUDA.cmake @@ -27,8 +27,8 @@ set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG ${CMAKE_CUDA_LINKER_WRAPPER_FLAG}) set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG_SEP ${CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP}) # RulePlaceholderExpander expands crosscompile variables like sysroot and target only for CMAKE__COMPILER. Override the default. -set(CMAKE_CUDA_LINK_EXECUTABLE " -o ${__IMPLICIT_LINKS}") -set(CMAKE_CUDA_CREATE_SHARED_LIBRARY " -o ${__IMPLICIT_LINKS}") +set(CMAKE_CUDA_LINK_EXECUTABLE " -o ${__IMPLICIT_LINKS}") +set(CMAKE_CUDA_CREATE_SHARED_LIBRARY " -o ${__IMPLICIT_LINKS}") set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "STATIC") set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "cudadevrt;cudart_static") diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake index f09d36d76..e85cdb2a1 100644 --- a/Modules/Compiler/Clang.cmake +++ b/Modules/Compiler/Clang.cmake @@ -245,17 +245,25 @@ macro(__compiler_clang_cxx_standards lang) set(CMAKE_${lang}_STANDARD_LATEST 17) if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) - set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std:c++latest") - set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std:c++20") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std:c++20") - set(CMAKE_${lang}_STANDARD_LATEST 23) + set(CMAKE_${lang}_STANDARD_LATEST 20) elseif(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std:c++latest") set(CMAKE_${lang}_STANDARD_LATEST 20) endif() + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "17.0") + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-clang:-std=c++23") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-clang:-std=c++23") + set(CMAKE_${lang}_STANDARD_LATEST 23) + elseif(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) + set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std:c++latest") + set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std:c++latest") + set(CMAKE_${lang}_STANDARD_LATEST 23) + endif() + __compiler_check_default_language_standard(${lang} 3.9 14) else() # This version of clang-cl, or the MSVC version it simulates, does not have diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake index 3f705a9bf..83c818d49 100644 --- a/Modules/Compiler/GNU.cmake +++ b/Modules/Compiler/GNU.cmake @@ -52,60 +52,6 @@ macro(__compiler_gnu lang) set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT -MF ") endif() - # define flags for linker depfile generation - set(CMAKE_${lang}_LINKER_DEPFILE_FLAGS "LINKER:--dependency-file,") - set(CMAKE_${lang}_LINKER_DEPFILE_FORMAT gcc) - - if(NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) - ## Ensure ninja tool is recent enough... - if(CMAKE_GENERATOR MATCHES "^Ninja") - # Ninja 1.10 or upper is required - execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" --version - OUTPUT_VARIABLE _ninja_version - ERROR_VARIABLE _ninja_version) - if (_ninja_version MATCHES "[0-9]+(\\.[0-9]+)*") - set (_ninja_version "${CMAKE_MATCH_0}") - endif() - if (_ninja_version VERSION_LESS "1.10") - set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) - endif() - unset(_ninja_version) - endif() - - if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) - ## check if this feature is supported by the linker - if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD") - execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help - OUTPUT_VARIABLE _linker_capabilities - ERROR_VARIABLE _linker_capabilities) - if(_linker_capabilities MATCHES "--dependency-file") - set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) - else() - set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) - endif() - unset(_linker_capabilities) - else() - set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) - endif() - endif() - endif() - if (CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) - set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER TRUE) - else() - set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) - endif() - - # Due to GNU binutils ld bug when LTO is enabled (see GNU bug - # `30568 `_), - # deactivate this feature if the version is less than 2.41. - # For now, all known versions of gold linker have also this bug. - if (CMAKE_${lang}_COMPILER_LINKER_ID - AND (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNUgold" - OR (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNU" - AND CMAKE_${lang}_COMPILER_LINKER_VERSION VERSION_LESS "2.41"))) - set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) - endif() - # Initial configuration flags. string(APPEND CMAKE_${lang}_FLAGS_INIT " ") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake index c4907c52f..df8f0037c 100644 --- a/Modules/Compiler/IAR-C.cmake +++ b/Modules/Compiler/IAR-C.cmake @@ -10,8 +10,8 @@ include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) -if(NOT DEFINED CMAKE_C_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected. This should be automatic.") +if(NOT CMAKE_C_COMPILER_VERSION) + message(FATAL_ERROR "Could not detect CMAKE_C_COMPILER_VERSION. This should be automatic. Check your product license.\n") endif() # Unused after CMP0128 diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake index b598e36db..7dd03f35a 100644 --- a/Modules/Compiler/IAR-CXX.cmake +++ b/Modules/Compiler/IAR-CXX.cmake @@ -10,8 +10,8 @@ include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) -if(NOT DEFINED CMAKE_CXX_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") +if(NOT CMAKE_CXX_COMPILER_VERSION) + message(FATAL_ERROR "Could not detect CMAKE_CXX_COMPILER_VERSION. This should be automatic. Check your product license.\n") endif() # Whenever needed, override this default behavior using CMAKE_IAR_CXX_FLAG in your toolchain file. @@ -56,7 +56,7 @@ elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850") elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 2) - # # IAR C++ Compiler for RL78 prior version 2.xx uses XLINK. Support in CMake is not implemented. + # IAR C++ Compiler for RL78 prior version 2.xx uses XLINK. Support in CMake is not implemented. message(FATAL_ERROR "IAR C++ Compiler for RL78 version ${CMAKE_CXX_COMPILER_VERSION} not supported by CMake.") endif() __compiler_iar_ilink(CXX) diff --git a/Modules/Compiler/IntelLLVM.cmake b/Modules/Compiler/IntelLLVM.cmake index 079c8948f..b9f4ca08c 100644 --- a/Modules/Compiler/IntelLLVM.cmake +++ b/Modules/Compiler/IntelLLVM.cmake @@ -35,7 +35,7 @@ if(CMAKE_HOST_WIN32) if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "2021.4") set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-external:I") if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "2022.2") - set(_CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ") + set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ") endif() endif() endif() diff --git a/Modules/Compiler/LFortran-Fortran.cmake b/Modules/Compiler/LFortran-Fortran.cmake new file mode 100644 index 000000000..9090e1d57 --- /dev/null +++ b/Modules/Compiler/LFortran-Fortran.cmake @@ -0,0 +1,14 @@ +string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -g") +string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " ") +string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3") +string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g") +set(CMAKE_Fortran_MODDIR_FLAG "-J") +set(CMAKE_Fortran_VERBOSE_FLAG "-v -Wl,-v") +set(CMAKE_Fortran_FORMAT_FIXED_FLAG "--fixed-form") +set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "--cpp") +set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "--no-cpp") +set(CMAKE_Fortran_PREPROCESS_SOURCE " --cpp -E > ") +set(CMAKE_Fortran_COMPILE_OBJECT " --cpp-infer --generate-object-code -c -o ") +set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS "--shared") +set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-Wl,-export-dynamic") diff --git a/Modules/Compiler/MSVC.cmake b/Modules/Compiler/MSVC.cmake index 154b657ba..9c09af12d 100644 --- a/Modules/Compiler/MSVC.cmake +++ b/Modules/Compiler/MSVC.cmake @@ -20,6 +20,6 @@ macro(__compiler_msvc lang) # The `/external:I` flag was made non-experimental in 19.29.30036.3. if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30036.3) set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-external:I") - set(_CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ") + set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ") endif () endmacro() diff --git a/Modules/Compiler/NVHPC-CXX.cmake b/Modules/Compiler/NVHPC-CXX.cmake index 0d804f467..e849cbd26 100644 --- a/Modules/Compiler/NVHPC-CXX.cmake +++ b/Modules/Compiler/NVHPC-CXX.cmake @@ -20,4 +20,11 @@ else() # can't occur in the same invocation set(CMAKE_CXX_DEPENDS_EXTRA_COMMANDS " -x c++ -M -MT -MD") endif() + +if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 24.9) + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION -std=c++23) + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION -std=c++23) # -std=gnu++23 is missing + set(CMAKE_CXX_STANDARD_LATEST 23) +endif() + __compiler_nvhpc(CXX) diff --git a/Modules/Compiler/SunPro-C.cmake b/Modules/Compiler/SunPro-C.cmake index 7b4478ce4..35dbdcd5a 100644 --- a/Modules/Compiler/SunPro-C.cmake +++ b/Modules/Compiler/SunPro-C.cmake @@ -65,5 +65,9 @@ endif() __compiler_check_default_language_standard(C 5.11 90 5.14 11) +if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.15) + set(CMAKE_C_COMPILE_OPTIONS_VISIBILITY "-fvisibility=") +endif() + set(CMAKE_C_CREATE_PREPROCESSED_SOURCE " -E > ") set(CMAKE_C_CREATE_ASSEMBLY_SOURCE " -S -o ") diff --git a/Modules/Compiler/SunPro-CXX.cmake b/Modules/Compiler/SunPro-CXX.cmake index 1db6aa7fc..33ce9fc11 100644 --- a/Modules/Compiler/SunPro-CXX.cmake +++ b/Modules/Compiler/SunPro-CXX.cmake @@ -69,3 +69,7 @@ else() endif() __compiler_check_default_language_standard(CXX 1 98) + +if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 5.15) + set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY "-fvisibility=") +endif() diff --git a/Modules/Compiler/Tasking-C.cmake b/Modules/Compiler/Tasking-C.cmake index 938f46e3a..915090a12 100644 --- a/Modules/Compiler/Tasking-C.cmake +++ b/Modules/Compiler/Tasking-C.cmake @@ -2,13 +2,13 @@ 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_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_C99_EXTENSION_COMPILE_OPTION "--iso=99") set(CMAKE_C11_STANDARD_COMPILE_OPTION "--iso=11" "--strict") -set(CMAKE_C11_EXTENSION_COMPILE_OPTION "--iso=11" " ") +set(CMAKE_C11_EXTENSION_COMPILE_OPTION "--iso=11") set(CMAKE_C_STANDARD_LATEST 11) diff --git a/Modules/Compiler/Tasking-CXX.cmake b/Modules/Compiler/Tasking-CXX.cmake index 20e007ff2..1aa840abc 100644 --- a/Modules/Compiler/Tasking-CXX.cmake +++ b/Modules/Compiler/Tasking-CXX.cmake @@ -4,13 +4,13 @@ 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_CXX98_EXTENSION_COMPILE_OPTION "--c++=03") set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "--c++=11" "--strict") -set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "--c++=11" " ") +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" " ") +set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "--c++=14") set(CMAKE_CXX_STANDARD_LATEST 14) diff --git a/Modules/ExternalData.cmake b/Modules/ExternalData.cmake index 6826c7b46..055a0ee8d 100644 --- a/Modules/ExternalData.cmake +++ b/Modules/ExternalData.cmake @@ -72,6 +72,12 @@ Module Functions It passes its arguments through ``ExternalData_Expand_Arguments`` and then invokes the :command:`add_test` command using the results. + .. versionchanged:: 3.31 + If the arguments after ```` define a test with an executable + that is a CMake target, empty values in the :prop_tgt:`TEST_LAUNCHER` + and :prop_tgt:`CROSSCOMPILING_EMULATOR` properties of that target are + preserved. See policy :policy:`CMP0178`. + .. command:: ExternalData_Add_Target The ``ExternalData_Add_Target`` function creates a custom target to @@ -353,7 +359,17 @@ file or set a variable: function(ExternalData_add_test target) # Expand all arguments as a single string to preserve escaped semicolons. ExternalData_expand_arguments("${target}" testArgs "${ARGN}") - add_test(${testArgs}) + + # We need the caller's CMP0178 policy setting to apply here + cmake_policy(GET CMP0178 cmp0178 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + + # ExternalData_expand_arguments() escapes semicolons, so we should still be + # preserving empty elements from ARGN here. But CMP0178 is still important + # for correctly handling TEST_LAUNCHER and CROSSCOMPILING_EMULATOR target + # properties that contain empty elements. + add_test(${testArgs} __CMP0178 "${cmp0178}") endfunction() function(ExternalData_add_target target) diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 86d83f53b..3f4354230 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -1757,7 +1757,7 @@ endif() set(stderr_log "${logbase}-err.log") endif() set(code " -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION \${CMAKE_VERSION}) # this file comes with cmake ${code_cygpath_make} set(command \"${command}\") set(log_merged \"${log_merged}\") @@ -2157,6 +2157,12 @@ function(ExternalProject_Add_Step name step) byproducts ) + if(NOT "x${work_dir}" STREQUAL "x") + set(maybe_WORKING_DIRECTORY "WORKING_DIRECTORY [==[${work_dir}]==]") + else() + set(maybe_WORKING_DIRECTORY "") + endif() + # Custom comment? get_property(comment_set TARGET ${name} @@ -2176,9 +2182,9 @@ function(ExternalProject_Add_Step name step) PROPERTY _EP_${step}_USES_TERMINAL ) if(uses_terminal) - set(uses_terminal USES_TERMINAL) + set(maybe_USES_TERMINAL USES_TERMINAL) else() - set(uses_terminal "") + set(maybe_USES_TERMINAL "") endif() # Run every time? @@ -2245,14 +2251,14 @@ function(ExternalProject_Add_Step name step) add_custom_command( OUTPUT \${stamp_file} BYPRODUCTS \${byproducts} - COMMENT \${comment} + COMMENT [===[${comment}]===] COMMAND ${__cmdQuoted} - ${maybe_COMMAND_touch} - ${maybe_JOB_SERVER_AWARE} DEPENDS \${depends} - WORKING_DIRECTORY \${work_dir} VERBATIM - ${uses_terminal} + ${maybe_COMMAND_touch} + ${maybe_JOB_SERVER_AWARE} + ${maybe_WORKING_DIRECTORY} + ${maybe_USES_TERMINAL} )" ) set_property(TARGET ${name} APPEND PROPERTY _EP_STEPS ${step}) diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in index 77d43d7a1..5e8c42fc9 100644 --- a/Modules/ExternalProject/download.cmake.in +++ b/Modules/ExternalProject/download.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake function(check_file_hash has_hash hash_is_good) if("${has_hash}" STREQUAL "") diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in index 39daaff44..a601bd71c 100644 --- a/Modules/ExternalProject/extractfile.cmake.in +++ b/Modules/ExternalProject/extractfile.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake # Make file names absolute: # diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in index b80bcb167..77d6be2cb 100644 --- a/Modules/ExternalProject/gitclone.cmake.in +++ b/Modules/ExternalProject/gitclone.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake if(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND "@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@") diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in index fb0c6a66a..8a214df76 100644 --- a/Modules/ExternalProject/gitupdate.cmake.in +++ b/Modules/ExternalProject/gitupdate.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake # Even at VERBOSE level, we don't want to see the commands executed, but # enabling them to be shown for DEBUG may be useful to help diagnose problems. diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in index 96449aaa9..5d5e8caf5 100644 --- a/Modules/ExternalProject/hgclone.cmake.in +++ b/Modules/ExternalProject/hgclone.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake if(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND "@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@") diff --git a/Modules/ExternalProject/mkdirs.cmake.in b/Modules/ExternalProject/mkdirs.cmake.in index 03676a2a0..6d2ed28c6 100644 --- a/Modules/ExternalProject/mkdirs.cmake.in +++ b/Modules/ExternalProject/mkdirs.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake # If CMAKE_DISABLE_SOURCE_CHANGES is set to true and the source directory is an # existing directory in our source tree, calling file(MAKE_DIRECTORY) on it diff --git a/Modules/ExternalProject/shared_internal_commands.cmake b/Modules/ExternalProject/shared_internal_commands.cmake index 09b976493..149a8a73f 100644 --- a/Modules/ExternalProject/shared_internal_commands.cmake +++ b/Modules/ExternalProject/shared_internal_commands.cmake @@ -898,41 +898,33 @@ function(_ep_add_download_command name) message(FATAL_ERROR "error: could not find svn for checkout of ${name}") endif() - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") set(uses_terminal "${_EP_USES_TERMINAL_DOWNLOAD}") - # The --trust-server-cert option requires --non-interactive - if(uses_terminal AND NOT svn_trust_cert) - set(svn_interactive_args "") - else() - set(svn_interactive_args "--non-interactive") - endif() get_filename_component(src_name "${source_dir}" NAME) get_filename_component(work_dir "${source_dir}" PATH) set(comment "Performing download step (SVN checkout) for '${name}'") - set(svn_user_pw_args "") - if(DEFINED _EP_SVN_USERNAME) - set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") - endif() - if(DEFINED _EP_SVN_PASSWORD) - set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") - endif() - if(svn_trust_cert) - set(svn_trust_cert_args --trust-server-cert) - endif() set(cmd ${Subversion_SVN_EXECUTABLE} co ${svn_repository} - ${svn_revision} - ${svn_interactive_args} - ${svn_trust_cert_args} - ${svn_user_pw_args} - ${src_name} + ${_EP_SVN_REVISION} ) + # The --trust-server-cert option requires --non-interactive + if(svn_trust_cert OR NOT uses_terminal) + list(APPEND cmd "--non-interactive") + endif() + if(svn_trust_cert) + list(APPEND cmd "--trust-server-cert") + endif() + if(DEFINED _EP_SVN_USERNAME) + list(APPEND cmd "--username=${_EP_SVN_USERNAME}") + endif() + if(DEFINED _EP_SVN_PASSWORD) + list(APPEND cmd "--password=${_EP_SVN_PASSWORD}") + endif() + list(APPEND cmd ${src_name}) + if(arg_SCRIPT_FILE) _ep_add_script_commands( step_script_contents @@ -1446,7 +1438,9 @@ function(_ep_add_update_command name) _ep_get_update_disconnected(update_disconnected ${name}) set(work_dir) + set(cmd_disconnected) set(comment) + set(comment_disconnected) set(always) set(file_deps) @@ -1485,35 +1479,26 @@ function(_ep_add_update_command name) endif() set(work_dir ${source_dir}) set(comment "Performing update step (SVN update) for '${name}'") - set(svn_revision "${_EP_SVN_REVISION}") - set(svn_username "${_EP_SVN_USERNAME}") - set(svn_password "${_EP_SVN_PASSWORD}") set(svn_trust_cert "${_EP_SVN_TRUST_CERT}") set(uses_terminal "${_EP_USES_TERMINAL_UPDATE}") + set(cmd + ${Subversion_SVN_EXECUTABLE} + up + ${_EP_SVN_REVISION} + ) # The --trust-server-cert option requires --non-interactive - if(uses_terminal AND NOT svn_trust_cert) - set(svn_interactive_args "") - else() - set(svn_interactive_args "--non-interactive") + if(svn_trust_cert OR NOT uses_terminal) + list(APPEND cmd "--non-interactive") + endif() + if(svn_trust_cert) + list(APPEND cmd --trust-server-cert) endif() - set(svn_user_pw_args "") if(DEFINED _EP_SVN_USERNAME) - set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}") + list(APPEND cmd "--username=${_EP_SVN_USERNAME}") endif() if(DEFINED _EP_SVN_PASSWORD) - set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}") - endif() - if(svn_trust_cert) - set(svn_trust_cert_args --trust-server-cert) + list(APPEND cmd "--password=${_EP_SVN_PASSWORD}") endif() - set(cmd - ${Subversion_SVN_EXECUTABLE} - up - ${svn_revision} - ${svn_interactive_args} - ${svn_trust_cert_args} - ${svn_user_pw_args} - ) set(always 1) if(arg_SCRIPT_FILE) diff --git a/Modules/ExternalProject/stepscript.cmake.in b/Modules/ExternalProject/stepscript.cmake.in index 12e157ef4..ba832c372 100644 --- a/Modules/ExternalProject/stepscript.cmake.in +++ b/Modules/ExternalProject/stepscript.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake message(VERBOSE "Executing @step_name@ step for @name@") diff --git a/Modules/ExternalProject/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in index 30d04872e..3cf768ff9 100644 --- a/Modules/ExternalProject/verify.cmake.in +++ b/Modules/ExternalProject/verify.cmake.in @@ -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.5) +cmake_minimum_required(VERSION ${CMAKE_VERSION}) # this file comes with cmake if("@LOCAL@" STREQUAL "") message(FATAL_ERROR "LOCAL can't be empty") diff --git a/Modules/FindAVIFile.cmake b/Modules/FindAVIFile.cmake index d63b707ac..9763e1f64 100644 --- a/Modules/FindAVIFile.cmake +++ b/Modules/FindAVIFile.cmake @@ -5,9 +5,10 @@ FindAVIFile ----------- -Locate AVIFILE library and include paths +Locate `AVIFILE `_ +library and include paths. -AVIFILE (https://avifile.sourceforge.net/) is a set of libraries for +AVIFILE 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 diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 2fc01d0c7..0bbd5a812 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -293,12 +293,14 @@ if(BLA_PREFER_PKGCONFIG) set(BLA_PKGCONFIG_BLAS "blas") endif() find_package(PkgConfig QUIET) - 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}") - _add_blas_target() - return() + if(PKG_CONFIG_FOUND) + 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}") + _add_blas_target() + return() + endif() endif() endif() diff --git a/Modules/FindBZip2.cmake b/Modules/FindBZip2.cmake index 4714dfc27..a04251c91 100644 --- a/Modules/FindBZip2.cmake +++ b/Modules/FindBZip2.cmake @@ -41,7 +41,11 @@ Cache variables The following cache variables may also be set: ``BZIP2_INCLUDE_DIR`` - the BZip2 include directory + the directory containing the BZip2 headers +``BZIP2_LIBRARY_RELEASE`` + the path to the BZip2 library for release configurations +``BZIP2_LIBRARY_DEBUG`` + the path to the BZip2 library for debug configurations Legacy Variables ^^^^^^^^^^^^^^^^ diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 130b239e7..73cac250d 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -1093,7 +1093,7 @@ endif() # Set the CUDA_LIBRARIES variable. This is the set of stuff to link against if you are # using the CUDA runtime. For the dynamic version of the runtime, most of the -# dependencies are brough in, but for the static version there are additional libraries +# dependencies are brought in, but for the static version there are additional libraries # and linker commands needed. # Initialize to empty set(CUDA_LIBRARIES) @@ -1449,7 +1449,7 @@ function(CUDA_COMPUTE_BUILD_PATH path build_path) # Avoid spaces string(REPLACE " " "_" bpath "${bpath}") - # Strip off the filename. I wait until here to do it, since removin the + # Strip off the filename. I wait until here to do it, since removing the # basename can make a path that looked like path/../basename turn into # path/.. (notice the trailing slash). get_filename_component(bpath "${bpath}" PATH) diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index da33be2b5..1e54b3d7e 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake @@ -444,11 +444,14 @@ nvidia-ML """"""""" The `NVIDIA Management Library `_. -This is a shared library only. Targets Created: - ``CUDA::nvml`` +- ``CUDA::nvml_static`` starting in CUDA 12.4 + +.. versionadded:: 3.31 + Added ``CUDA::nvml_static``. .. _`cuda_toolkit_nvToolsExt`: @@ -782,7 +785,6 @@ else() set(CUDAToolkit_VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) set(CUDAToolkit_VERSION_PATCH "${CMAKE_MATCH_3}" PARENT_SCOPE) set(CUDAToolkit_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE) - message(STATUS "_CUDAToolkit_parse_version_file") endif() endif() endfunction() @@ -970,6 +972,14 @@ if(CMAKE_CROSSCOMPILING) endforeach() endif() +# Determine windows search path suffix for libraries +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") + set(_CUDAToolkit_win_search_dirs lib/x64) + set(_CUDAToolkit_win_stub_search_dirs lib/x64/stubs) + endif() +endif() + # If not already set we can simply use the toolkit root or it's a scattered installation. if(NOT CUDAToolkit_TARGET_DIR) # Not cross compiling @@ -1025,12 +1035,12 @@ unset(CUDAToolkit_CUBLAS_INCLUDE_DIR) find_library(CUDA_CUDART NAMES cudart PATHS ${CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES} - PATH_SUFFIXES lib64 lib/x64 + PATH_SUFFIXES lib64 ${_CUDAToolkit_win_search_dirs} ) find_library(CUDA_CUDART NAMES cudart PATHS ${CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES} - PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs + PATH_SUFFIXES lib64/stubs ${_CUDAToolkit_win_stub_search_dirs} lib/stubs stubs ) if(NOT CUDA_CUDART AND NOT CUDAToolkit_FIND_QUIETLY) @@ -1101,6 +1111,14 @@ if(CUDAToolkit_FOUND) unset(CUDAToolkit_search_loc) unset(CUDAToolkit_possible_lib_root) endif() +else() + # clear cache results when we fail + unset(_cmake_CUDAToolkit_implicit_link_directories CACHE) + unset(_cmake_CUDAToolkit_include_directories CACHE) + unset(CUDA_CUDART CACHE) + unset(CUDAToolkit_BIN_DIR CACHE) + unset(CUDAToolkit_NVCC_EXECUTABLE CACHE) + unset(CUDAToolkit_SENTINEL_FILE CACHE) endif() unset(CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES) unset(CUDAToolkit_INCLUDE_DIRECTORIES) @@ -1110,15 +1128,19 @@ unset(CUDAToolkit_INCLUDE_DIRECTORIES) if(CUDAToolkit_FOUND) function(_CUDAToolkit_find_and_add_import_lib lib_name) - cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS" ${ARGN}) + cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS;ONLY_SEARCH_FOR" ${ARGN}) - set(search_names ${lib_name} ${arg_ALT}) + if(arg_ONLY_SEARCH_FOR) + set(search_names ${arg_ONLY_SEARCH_FOR}) + else() + set(search_names ${lib_name} ${arg_ALT}) + endif() find_library(CUDA_${lib_name}_LIBRARY NAMES ${search_names} HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH - PATH_SUFFIXES nvidia/current lib64 lib/x64 lib + PATH_SUFFIXES nvidia/current lib64 ${_CUDAToolkit_win_search_dirs} lib # Support NVHPC splayed math library layout math_libs/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64 math_libs/lib64 @@ -1133,10 +1155,10 @@ if(CUDAToolkit_FOUND) NAMES ${search_names} HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH - PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs + PATH_SUFFIXES lib64/stubs ${_CUDAToolkit_win_stub_search_dirs} lib/stubs stubs ) endif() - if(CUDA_${lib_name}_LIBRARY MATCHES "/stubs/" AND NOT WIN32) + if(CUDA_${lib_name}_LIBRARY MATCHES "/stubs/" AND NOT CUDA_${lib_name}_LIBRARY MATCHES "\\.a$" AND NOT WIN32) # Use a SHARED library with IMPORTED_IMPLIB, but not IMPORTED_LOCATION, # to indicate that the stub is for linkers but not dynamic loaders. # It will not contribute any RPATH entry. When encountered as @@ -1333,11 +1355,11 @@ if(CUDAToolkit_FOUND) endif() _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins ALT nvrtc-builtins) - _CUDAToolkit_find_and_add_import_lib(nvrtc DEPS nvrtc_builtins nvJitLink) + _CUDAToolkit_find_and_add_import_lib(nvrtc) if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.5.0) _CUDAToolkit_find_and_add_import_lib(nvrtc_builtins_static ALT nvrtc-builtins_static) if(NOT TARGET CUDA::nvrtc_static) - _CUDAToolkit_find_and_add_import_lib(nvrtc_static DEPS nvrtc_builtins_static nvptxcompiler_static nvJitLink_static) + _CUDAToolkit_find_and_add_import_lib(nvrtc_static DEPS nvrtc_builtins_static nvptxcompiler_static) if(TARGET CUDA::nvrtc_static AND WIN32 AND NOT (BORLAND OR MINGW OR CYGWIN)) target_link_libraries(CUDA::nvrtc_static INTERFACE Ws2_32.lib) endif() @@ -1345,6 +1367,7 @@ if(CUDAToolkit_FOUND) endif() _CUDAToolkit_find_and_add_import_lib(nvml ALT nvidia-ml nvml) + _CUDAToolkit_find_and_add_import_lib(nvml_static ONLY_SEARCH_FOR libnvidia-ml.a libnvml.a) if(WIN32) # nvtools can be installed outside the CUDA toolkit directory @@ -1381,3 +1404,6 @@ if(_CUDAToolkit_Pop_ROOT_PATH) list(REMOVE_AT CMAKE_FIND_ROOT_PATH 0) unset(_CUDAToolkit_Pop_ROOT_PATH) endif() + +unset(_CUDAToolkit_win_search_dirs) +unset(_CUDAToolkit_win_stub_search_dirs) diff --git a/Modules/FindCxxTest.cmake b/Modules/FindCxxTest.cmake index a3283fa77..ceb6e2928 100644 --- a/Modules/FindCxxTest.cmake +++ b/Modules/FindCxxTest.cmake @@ -163,6 +163,8 @@ macro(CXXTEST_ADD_TEST _cxxtest_testname _cxxtest_outfname) set_source_files_properties(${_cxxtest_real_outfname} PROPERTIES GENERATED true) add_executable(${_cxxtest_testname} ${_cxxtest_real_outfname} ${ARGN}) + # There's no target used for these commands, so we don't need to do + # anything here for CMP0178. if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) add_test(${_cxxtest_testname} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${_cxxtest_testname}) elseif(EXECUTABLE_OUTPUT_PATH) diff --git a/Modules/FindDevIL.cmake b/Modules/FindDevIL.cmake index 7f726ffa0..611d761dc 100644 --- a/Modules/FindDevIL.cmake +++ b/Modules/FindDevIL.cmake @@ -5,10 +5,8 @@ FindDevIL --------- - - -This module locates the developer's image library. -https://openil.sourceforge.net/ +This module locates the Developer's Image Library, +`DevIL `_. IMPORTED Targets ^^^^^^^^^^^^^^^^ diff --git a/Modules/FindEXPAT.cmake b/Modules/FindEXPAT.cmake index 398a41525..825b7a61e 100644 --- a/Modules/FindEXPAT.cmake +++ b/Modules/FindEXPAT.cmake @@ -39,14 +39,19 @@ Hints Set to ``TRUE`` to use static libraries. + .. versionadded:: 3.31 + + Implemented on non-Windows platforms. + #]=======================================================================] cmake_policy(PUSH) cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_ find_package(PkgConfig QUIET) - -pkg_check_modules(PC_EXPAT QUIET expat) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_EXPAT QUIET expat) +endif() # Look for the header file. find_path(EXPAT_INCLUDE_DIR NAMES expat.h HINTS ${PC_EXPAT_INCLUDE_DIRS}) @@ -72,10 +77,24 @@ if(NOT EXPAT_LIBRARY) set(_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES) endif() + if(DEFINED CMAKE_FIND_LIBRARY_SUFFIXES) + set(_expat_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}") + else() + set(_expat_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) + endif() + if(WIN32) list(APPEND CMAKE_FIND_LIBRARY_PREFIXES "" "lib") endif() + if (EXPAT_USE_STATIC_LIBS) + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() + endif() + # Look for the library. find_library(EXPAT_LIBRARY_RELEASE NAMES ${EXPAT_NAMES} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib) find_library(EXPAT_LIBRARY_DEBUG NAMES ${EXPAT_NAMES_DEBUG} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib) @@ -87,6 +106,12 @@ if(NOT EXPAT_LIBRARY) set(CMAKE_FIND_LIBRARY_PREFIXES) endif() + if(DEFINED _expat_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) + set(CMAKE_FIND_LIBRARY_SUFFIXES "${_expat_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}") + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES) + endif() + include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) select_library_configurations(EXPAT) endif() diff --git a/Modules/FindFontconfig.cmake b/Modules/FindFontconfig.cmake index 9cce9522e..1cd84c1de 100644 --- a/Modules/FindFontconfig.cmake +++ b/Modules/FindFontconfig.cmake @@ -40,7 +40,9 @@ cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_ # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig QUIET) -pkg_check_modules(PKG_FONTCONFIG QUIET fontconfig) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PKG_FONTCONFIG QUIET fontconfig) +endif() set(Fontconfig_COMPILE_OPTIONS ${PKG_FONTCONFIG_CFLAGS_OTHER}) set(Fontconfig_VERSION ${PKG_FONTCONFIG_VERSION}) diff --git a/Modules/FindGSL.cmake b/Modules/FindGSL.cmake index e9d81096d..3bb927211 100644 --- a/Modules/FindGSL.cmake +++ b/Modules/FindGSL.cmake @@ -81,10 +81,11 @@ endif() # This will return ``GSL_INCLUDEDIR`` and ``GSL_LIBDIR`` used below. if( GSL_USE_PKGCONFIG ) find_package(PkgConfig QUIET) - pkg_check_modules( GSL QUIET gsl ) - - if( EXISTS "${GSL_INCLUDEDIR}" ) - get_filename_component( GSL_ROOT_DIR "${GSL_INCLUDEDIR}" DIRECTORY CACHE) + if(PKG_CONFIG_FOUND) + pkg_check_modules( GSL QUIET gsl ) + if( EXISTS "${GSL_INCLUDEDIR}" ) + get_filename_component( GSL_ROOT_DIR "${GSL_INCLUDEDIR}" DIRECTORY CACHE) + endif() endif() endif() diff --git a/Modules/FindGnuTLS.cmake b/Modules/FindGnuTLS.cmake index 782a72b2f..e4c42f3bc 100644 --- a/Modules/FindGnuTLS.cmake +++ b/Modules/FindGnuTLS.cmake @@ -43,7 +43,9 @@ if (NOT WIN32) # in the find_path() and find_library() calls # also fills in GNUTLS_DEFINITIONS, although that isn't normally useful find_package(PkgConfig QUIET) - PKG_CHECK_MODULES(PC_GNUTLS QUIET gnutls) + if(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(PC_GNUTLS QUIET gnutls) + endif() set(GNUTLS_DEFINITIONS ${PC_GNUTLS_CFLAGS_OTHER}) set(GNUTLS_VERSION ${PC_GNUTLS_VERSION}) # keep for backward compatibility diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake index bd21e3dcc..242d36fd0 100644 --- a/Modules/FindHDF5.cmake +++ b/Modules/FindHDF5.cmake @@ -974,7 +974,6 @@ if( NOT HDF5_FOUND ) # If the HDF5 include directory was found, open H5pubconf.h to determine if # HDF5 was compiled with parallel IO support set( HDF5_IS_PARALLEL FALSE ) - set( HDF5_VERSION "" ) foreach( _dir IN LISTS HDF5_INCLUDE_DIRS ) foreach(_hdr "${_dir}/H5pubconf.h" "${_dir}/H5pubconf-64.h" "${_dir}/H5pubconf-32.h") if( EXISTS "${_hdr}" ) diff --git a/Modules/FindIconv.cmake b/Modules/FindIconv.cmake index 879ff16f3..876af8d4f 100644 --- a/Modules/FindIconv.cmake +++ b/Modules/FindIconv.cmake @@ -134,6 +134,8 @@ else() find_path(Iconv_INCLUDE_DIR NAMES "iconv.h" + PATH_SUFFIXES + gnu-libiconv # GNU libiconv on Alpine Linux has header in a subdirectory. DOC "iconv include directory") set(Iconv_LIBRARY_NAMES "iconv" "libiconv") mark_as_advanced(Iconv_INCLUDE_DIR) diff --git a/Modules/FindImageMagick.cmake b/Modules/FindImageMagick.cmake index 6baf471f9..66e65ad34 100644 --- a/Modules/FindImageMagick.cmake +++ b/Modules/FindImageMagick.cmake @@ -93,6 +93,8 @@ Result Variables Compile options of . ``ImageMagick__LIBRARIES`` + .. versionadded:: 3.31 + Full path to libraries. @@ -113,7 +115,9 @@ find_package(PkgConfig QUIET) function(FIND_IMAGEMAGICK_API component header) set(ImageMagick_${component}_FOUND FALSE PARENT_SCOPE) - pkg_check_modules(PC_${component} QUIET ${component}) + if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_${component} QUIET ${component}) + endif() find_path(ImageMagick_${component}_INCLUDE_DIR NAMES ${header} @@ -168,6 +172,12 @@ function(FIND_IMAGEMAGICK_API component header) set(ImageMagick_${component}_INCLUDE_DIRS ${ImageMagick_${component}_INCLUDE_DIRS} PARENT_SCOPE) + set(ImageMagick_${component}_LIBRARIES + ${ImageMagick_${component}_LIBRARY} + ) + set(ImageMagick_${component}_LIBRARIES + ${ImageMagick_${component}_LIBRARIES} PARENT_SCOPE) + set(ImageMagick_${component}_COMPILE_OPTIONS ${PC_${component}_CFLAGS_OTHER}) # Add the per-component include directories to the full include dirs. @@ -185,11 +195,13 @@ function(FIND_IMAGEMAGICK_API component header) ) set(ImageMagick_COMPILE_OPTIONS ${ImageMagick_COMPILE_OPTIONS} PARENT_SCOPE) - add_library(ImageMagick::${component} UNKNOWN IMPORTED) - set_target_properties(ImageMagick::${component} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${ImageMagick_${component}_INCLUDE_DIRS}" - INTERFACE_COMPILE_OPTIONS "${ImageMagick_${component}_COMPILE_OPTIONS}" - IMPORTED_LOCATION "${ImageMagick_${component}_LIBRARY}") + if(NOT TARGET ImageMagick::${component}) + add_library(ImageMagick::${component} UNKNOWN IMPORTED) + set_target_properties(ImageMagick::${component} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${ImageMagick_${component}_INCLUDE_DIRS}" + INTERFACE_COMPILE_OPTIONS "${ImageMagick_${component}_COMPILE_OPTIONS}" + IMPORTED_LOCATION "${ImageMagick_${component}_LIBRARY}") + endif() endif() endfunction() diff --git a/Modules/FindJPEG.cmake b/Modules/FindJPEG.cmake index 08a0bd314..a2c5e4e5c 100644 --- a/Modules/FindJPEG.cmake +++ b/Modules/FindJPEG.cmake @@ -37,7 +37,7 @@ Cache variables The following cache variables may also be set: -``JPEG_INCLUDE_DIRS`` +``JPEG_INCLUDE_DIR`` where to find jpeglib.h, etc. ``JPEG_LIBRARY_RELEASE`` where to find the JPEG library (optimized). @@ -50,8 +50,6 @@ The following cache variables may also be set: Obsolete variables ^^^^^^^^^^^^^^^^^^ -``JPEG_INCLUDE_DIR`` - where to find jpeglib.h, etc. (same as JPEG_INCLUDE_DIRS) ``JPEG_LIBRARY`` where to find the JPEG library. #]=======================================================================] diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index bbf25b68b..b237a90b2 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -289,15 +289,17 @@ if(BLA_PREFER_PKGCONFIG) set(BLA_PKGCONFIG_LAPACK "lapack") endif() find_package(PkgConfig QUIET) - pkg_check_modules(PKGC_LAPACK QUIET ${BLA_PKGCONFIG_LAPACK}) - if(PKGC_LAPACK_FOUND) - set(LAPACK_FOUND TRUE) - set(LAPACK_LIBRARIES "${PKGC_LAPACK_LINK_LIBRARIES}") - if (BLAS_LIBRARIES) - list(APPEND LAPACK_LIBRARIES "${BLAS_LIBRARIES}") - endif() - _add_lapack_target() - return() + if(PKG_CONFIG_FOUND) + pkg_check_modules(PKGC_LAPACK QUIET ${BLA_PKGCONFIG_LAPACK}) + if(PKGC_LAPACK_FOUND) + set(LAPACK_FOUND TRUE) + set(LAPACK_LIBRARIES "${PKGC_LAPACK_LINK_LIBRARIES}") + if (BLAS_LIBRARIES) + list(APPEND LAPACK_LIBRARIES "${BLAS_LIBRARIES}") + endif() + _add_lapack_target() + return() + endif() endif() endif() diff --git a/Modules/FindLibXml2.cmake b/Modules/FindLibXml2.cmake index 1c5f9be0f..cc2d630b9 100644 --- a/Modules/FindLibXml2.cmake +++ b/Modules/FindLibXml2.cmake @@ -58,7 +58,9 @@ cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_ # use pkg-config to get the directories and then use these values # in the find_path() and find_library() calls find_package(PkgConfig QUIET) -PKG_CHECK_MODULES(PC_LIBXML QUIET libxml-2.0) +if(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(PC_LIBXML QUIET libxml-2.0) +endif() find_path(LIBXML2_INCLUDE_DIR NAMES libxml/xpath.h HINTS diff --git a/Modules/FindLibXslt.cmake b/Modules/FindLibXslt.cmake index 1154ae1e3..11d12cb9a 100644 --- a/Modules/FindLibXslt.cmake +++ b/Modules/FindLibXslt.cmake @@ -51,7 +51,9 @@ cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_ # use pkg-config to get the directories and then use these values # in the find_path() and find_library() calls find_package(PkgConfig QUIET) -PKG_CHECK_MODULES(PC_LIBXSLT QUIET libxslt) +if(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(PC_LIBXSLT QUIET libxslt) +endif() set(LIBXSLT_DEFINITIONS ${PC_LIBXSLT_CFLAGS_OTHER}) find_path(LIBXSLT_INCLUDE_DIR NAMES libxslt/xslt.h @@ -75,7 +77,9 @@ find_library(LIBXSLT_LIBRARY NAMES xslt libxslt set(LIBXSLT_LIBRARIES ${LIBXSLT_LIBRARY}) -PKG_CHECK_MODULES(PC_LIBXSLT_EXSLT QUIET libexslt) +if(PKG_CONFIG_FOUND) + PKG_CHECK_MODULES(PC_LIBXSLT_EXSLT QUIET libexslt) +endif() set(LIBXSLT_EXSLT_DEFINITIONS ${PC_LIBXSLT_EXSLT_CFLAGS_OTHER}) find_path(LIBXSLT_EXSLT_INCLUDE_DIR NAMES libexslt/exslt.h diff --git a/Modules/FindLibinput.cmake b/Modules/FindLibinput.cmake index 88d5b2fb3..291d828d4 100644 --- a/Modules/FindLibinput.cmake +++ b/Modules/FindLibinput.cmake @@ -38,7 +38,9 @@ This will define the following variables in your project: # Use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig QUIET) -pkg_check_modules(PKG_Libinput QUIET libinput) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PKG_Libinput QUIET libinput) +endif() set(Libinput_COMPILE_OPTIONS ${PKG_Libinput_CFLAGS_OTHER}) set(Libinput_VERSION ${PKG_Libinput_VERSION}) diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake index aa322ac97..0f06c577d 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake @@ -683,7 +683,7 @@ function (_MPI_interrogate_compiler LANG) endforeach() # Extract include paths from compile command line - string(REGEX MATCHALL "(^| )${_MPI_PREPROCESSOR_FLAG_REGEX}${CMAKE_INCLUDE_FLAG_${LANG}} *([^\" ]+|\"[^\"]+\")" + string(REGEX MATCHALL "(^|\n| )${_MPI_PREPROCESSOR_FLAG_REGEX}${CMAKE_INCLUDE_FLAG_${LANG}} *([^\" ]+|\"[^\"]+\")" MPI_ALL_INCLUDE_PATHS "${MPI_COMPILE_CMDLINE}") # If extracting failed to work, we'll try using -showme:incdirs. @@ -698,6 +698,7 @@ function (_MPI_interrogate_compiler LANG) foreach(_MPI_INCLUDE_PATH IN LISTS MPI_ALL_INCLUDE_PATHS) string(REGEX REPLACE "^ ?${_MPI_PREPROCESSOR_FLAG_REGEX}${CMAKE_INCLUDE_FLAG_${LANG}} *" "" _MPI_INCLUDE_PATH "${_MPI_INCLUDE_PATH}") + string(REPLACE "\n" "" _MPI_INCLUDE_PATH "${_MPI_INCLUDE_PATH}") string(REPLACE "\"" "" _MPI_INCLUDE_PATH "${_MPI_INCLUDE_PATH}") string(REPLACE "'" "" _MPI_INCLUDE_PATH "${_MPI_INCLUDE_PATH}") get_filename_component(_MPI_INCLUDE_PATH "${_MPI_INCLUDE_PATH}" REALPATH) diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 6cdf1a448..c94ef72d4 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -66,8 +66,9 @@ specific: Windows registry. The ``REGISTRY_VIEW`` argument may optionally be specified to manually control whether 32bit or 64bit versions shall be searched for. * macOS: The installed versions of Matlab/MCR are given by the MATLAB - default installation paths in ``/Application``. If no such application is - found, it falls back to the one that might be accessible from the ``PATH``. + default installation paths under ``$HOME/Applications`` and ``/Applications``. + If no such application is found, it falls back to the one that might be + accessible from the ``PATH``. * Unix: The desired Matlab should be accessible from the ``PATH``. This does not work for MCR installation and :variable:`Matlab_ROOT_DIR` should be specified on this platform. @@ -1011,6 +1012,10 @@ function(matlab_add_unit_test) endif() endif() + # The ${${prefix}_TEST_ARGS} and ${${prefix}_UNPARSED_ARGUMENTS} used below + # should have semicolons escaped, so empty arguments should be preserved. + # There's also no target used for the command, so we don't need to do + # anything here for CMP0178. add_test(NAME ${${prefix}_NAME} COMMAND ${CMAKE_COMMAND} "-Dtest_name=${${prefix}_NAME}" @@ -1508,7 +1513,7 @@ endfunction() function(_Matlab_find_instances_macos matlab_roots) set(_matlab_possible_roots) - # on mac, we look for the /Application paths + # on macOS, we look for the standard /Applications paths # this corresponds to the behavior on Windows. On Linux, we do not have # any other guess. matlab_get_supported_releases(_matlab_releases) @@ -1518,32 +1523,33 @@ function(_Matlab_find_instances_macos matlab_roots) endif() foreach(_matlab_current_release IN LISTS _matlab_releases) - matlab_get_version_from_release_name("${_matlab_current_release}" _matlab_current_version) - string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - set(_matlab_base_path "/Applications/MATLAB_${_matlab_current_release}.app") - - _Matlab_VersionInfoXML("${_matlab_base_path}" _matlab_version_tmp) - if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") - set(_matlab_current_version ${_matlab_version_tmp}) - endif() + foreach(_macos_app_base IN ITEMS "$ENV{HOME}/Applications" "/Applications") + matlab_get_version_from_release_name("${_matlab_current_release}" _matlab_current_version) + string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") + set(_matlab_base_path "${_macos_app_base}/MATLAB_${_matlab_current_release}.app") - # Check Matlab, has precedence over MCR - if(IS_DIRECTORY "${_matlab_base_path}") - if(MATLAB_FIND_DEBUG) - message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_base_path}") + _Matlab_VersionInfoXML("${_matlab_base_path}" _matlab_version_tmp) + if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") + set(_matlab_current_version ${_matlab_version_tmp}) endif() - list(APPEND _matlab_possible_roots "MATLAB" ${_matlab_current_version} ${_matlab_base_path}) - endif() - # Checks MCR - set(_mcr_path "/Applications/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}") - if(IS_DIRECTORY "${_mcr_path}") - if(MATLAB_FIND_DEBUG) - message(STATUS "[MATLAB] Found MCR version ${_matlab_current_release} (${_matlab_current_version}) in ${_mcr_path}") + # Check Matlab, has precedence over MCR + if(IS_DIRECTORY "${_matlab_base_path}") + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_base_path}") + endif() + list(APPEND _matlab_possible_roots "MATLAB" ${_matlab_current_version} ${_matlab_base_path}) endif() - list(APPEND _matlab_possible_roots "MCR" ${_matlab_current_version} ${_mcr_path}) - endif() + # Checks MCR + set(_mcr_path "${_macos_app_base}/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}") + if(IS_DIRECTORY "${_mcr_path}") + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Found MCR version ${_matlab_current_release} (${_matlab_current_version}) in ${_mcr_path}") + endif() + list(APPEND _matlab_possible_roots "MCR" ${_matlab_current_version} ${_mcr_path}) + endif() + endforeach() endforeach() set(${matlab_roots} ${_matlab_possible_roots} PARENT_SCOPE) diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index f88e43c8e..07eaf6618 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -37,6 +37,10 @@ Result Variables The module exposes the components ``C``, ``CXX``, and ``Fortran``. Each of these controls the various languages to search OpenMP support for. +.. versionadded:: 3.31 + The ``CUDA`` language component is supported when using a CUDA compiler + that supports OpenMP on the host. + Depending on the enabled components the following variables will be set: ``OpenMP_FOUND`` @@ -48,7 +52,7 @@ Depending on the enabled components the following variables will be set: or all enabled languages if no components were specified. This module will set the following variables per language in your -project, where ```` is one of C, CXX, or Fortran: +project, where ```` is one of C, CXX, CUDA, or Fortran: ``OpenMP__FOUND`` Variable indicating if OpenMP support for ```` was detected. @@ -153,11 +157,17 @@ function(_OPENMP_FLAG_CANDIDATES LANG) set(OMP_FLAG_Fujitsu "-Kopenmp" "-KOMP") set(OMP_FLAG_FujitsuClang "-fopenmp" "-Kopenmp") + if(CMAKE_${LANG}_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_${LANG}_HOST_COMPILER_ID) + set(compiler_id "${CMAKE_${LANG}_HOST_COMPILER_ID}") + else() + set(compiler_id "${CMAKE_${LANG}_COMPILER_ID}") + endif() + # If we know the correct flags, use those - if(DEFINED OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}) - set(OpenMP_FLAG_CANDIDATES "${OMP_FLAG_${CMAKE_${LANG}_COMPILER_ID}}") - # Fall back to reasonable default tries otherwise + if(DEFINED OMP_FLAG_${compiler_id}) + set(OpenMP_FLAG_CANDIDATES "${OMP_FLAG_${compiler_id}}") else() + # Fall back to reasonable default tries otherwise set(OpenMP_FLAG_CANDIDATES "-openmp" "-fopenmp" "-mp" " ") endif() set(OpenMP_${LANG}_FLAG_CANDIDATES "${OpenMP_FLAG_CANDIDATES}" PARENT_SCOPE) @@ -173,12 +183,10 @@ set(OpenMP_C_CXX_TEST_SOURCE int main(void) { #ifdef _OPENMP omp_get_max_threads(); - return 0; -#elif defined(__HIP_DEVICE_COMPILE__) - return 0; -#else - breaks_on_purpose +#elif !defined(__CUDA_ARCH__) && !defined(__HIP_DEVICE_COMPILE__) +# error \"_OPENMP not defined!\" #endif + return 0; } ") @@ -204,6 +212,9 @@ macro(_OPENMP_PREPARE_SOURCE LANG CONTENT_ID NAME_PREFIX FULLNAME_VAR CONTENT_VA elseif("${LANG}" STREQUAL "CXX") set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp") set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}") + elseif("${LANG}" STREQUAL "CUDA") + set(${FULLNAME_VAR} "${NAME_PREFIX}.cu") + set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}") elseif("${LANG}" STREQUAL "Fortran") set(${FULLNAME_VAR} "${NAME_PREFIX}.F90") string(CONFIGURE "${OpenMP_Fortran_${CONTENT_ID}}" ${CONTENT_VAR} @ONLY) @@ -217,29 +228,32 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) _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}") - foreach(_VERBOSE_OPTION IN LISTS OpenMP_VERBOSE_OPTIONS) - if(NOT _VERBOSE_OPTION MATCHES "^-Wl,") - list(APPEND OpenMP_VERBOSE_COMPILE_OPTIONS ${_VERBOSE_OPTION}) - endif() - endforeach() foreach(OPENMP_FLAG IN LISTS OpenMP_${LANG}_FLAG_CANDIDATES) - set(OPENMP_FLAGS_TEST "${OPENMP_FLAG}") - if(OpenMP_VERBOSE_COMPILE_OPTIONS) - string(APPEND OPENMP_FLAGS_TEST " ${OpenMP_VERBOSE_COMPILE_OPTIONS}") - endif() string(REGEX REPLACE "[-/=+]" "" OPENMP_PLAIN_FLAG "${OPENMP_FLAG}") unset(_includeDirFlags) if(OpenMP_${LANG}_INCLUDE_DIR) set(_includeDirFlags "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}") endif() + if(CMAKE_${LANG}_COMPILER_ID STREQUAL "NVIDIA") + # With NVCC we drive linking directly through the host compiler, but + # without language-wide flags since they may be specific to nvcc. + # Pass the candidate OpenMP flag to the host compiler when linking. + set(_OpenMP_LINK_OPTIONS "${OPENMP_FLAG}") + # Exclude CUDA runtime libraries that we may add ourselves. + # See the Compiler/NVIDIA module. Do not exclude pthread, + # as that is typically a dependency of OpenMP too. + set(_OpenMP_EXCLUDE_IMPLICIT_LIBS cudart cudart_static cudadevrt rt dl) + else() + set(_OpenMP_LINK_OPTIONS "") + set(_OpenMP_EXCLUDE_IMPLICIT_LIBS "") + endif() try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT LOG_DESCRIPTION "Detecting ${LANG} OpenMP compiler info" - CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" ${_includeDirFlags} - LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} + CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAG}" ${_includeDirFlags} + LINK_OPTIONS ${OpenMP_VERBOSE_OPTIONS} ${_OpenMP_LINK_OPTIONS} OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT ) @@ -290,6 +304,7 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PLAIN_ESC "${_OPENMP_IMPLICIT_LIB_PLAIN}") string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" _OPENMP_IMPLICIT_LIB_PATH_ESC "${_OPENMP_IMPLICIT_LIB}") if(NOT ( "${_OPENMP_IMPLICIT_LIB}" IN_LIST CMAKE_${LANG}_IMPLICIT_LINK_LIBRARIES + OR "${_OPENMP_IMPLICIT_LIB}" IN_LIST _OpenMP_EXCLUDE_IMPLICIT_LIBS OR "${CMAKE_${LANG}_STANDARD_LIBRARIES}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)" OR "${CMAKE_${LANG}_LINK_EXECUTABLE}" MATCHES "(^| )(-Wl,)?(-l)?(${_OPENMP_IMPLICIT_LIB_PLAIN_ESC}|${_OPENMP_IMPLICIT_LIB_PATH_ESC})( |$)" ) ) if(_OPENMP_IMPLICIT_LIB_DIR) @@ -310,6 +325,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) list(APPEND _OPENMP_LIB_NAMES ${_OPENMP_IMPLICIT_LIB_PLAIN}) endif() endforeach() + list(REVERSE _OPENMP_LIB_NAMES) + list(REMOVE_DUPLICATES _OPENMP_LIB_NAMES) + list(REVERSE _OPENMP_LIB_NAMES) set("${OPENMP_LIB_NAMES_VAR}" "${_OPENMP_LIB_NAMES}" PARENT_SCOPE) else() # We do not know how to extract implicit OpenMP libraries for this compiler. @@ -336,8 +354,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT LOG_DESCRIPTION "Trying ${LANG} OpenMP compiler with '${OpenMP_libomp_LIBRARY}'" - CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" - LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY} + COMPILE_DEFINITIONS ${OPENMP_FLAG} + LINK_LIBRARIES ${OpenMP_libomp_LIBRARY} ) if(NOT OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}) find_path(OpenMP_${LANG}_INCLUDE_DIR omp.h) @@ -347,9 +365,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT LOG_DESCRIPTION "Trying ${LANG} OpenMP compiler with '${OpenMP_libomp_LIBRARY}' and '${OpenMP_${LANG}_INCLUDE_DIR}'" - CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" - "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}" - LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}" + COMPILE_DEFINITIONS ${OPENMP_FLAG} + LINK_LIBRARIES ${OpenMP_libomp_LIBRARY} ) endif() endif() @@ -370,8 +388,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT LOG_DESCRIPTION "Trying ${LANG} OpenMP compiler with '${OpenMP_libomp_LIBRARY}'" - CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}" - LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY} + COMPILE_DEFINITIONS ${OPENMP_FLAG} + LINK_LIBRARIES ${OpenMP_libomp_LIBRARY} ) if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}) set("${OPENMP_FLAG_VAR}" "${OPENMP_FLAG}" PARENT_SCOPE) @@ -383,8 +401,6 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) set("${OPENMP_LIB_NAMES_VAR}" "NOTFOUND" PARENT_SCOPE) set("${OPENMP_FLAG_VAR}" "NOTFOUND" PARENT_SCOPE) endforeach() - - unset(OpenMP_VERBOSE_COMPILE_OPTIONS) endfunction() set(OpenMP_C_CXX_CHECK_VERSION_SOURCE @@ -499,7 +515,7 @@ macro(_OPENMP_SET_VERSION_BY_SPEC_DATE LANG) unset(OpenMP_SPEC_DATE_MAP) endmacro() -foreach(LANG IN ITEMS C CXX) +foreach(LANG IN ITEMS C CXX CUDA) if(CMAKE_${LANG}_COMPILER_LOADED) 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") @@ -565,7 +581,7 @@ if(CMAKE_Fortran_COMPILER_LOADED) endif() if(NOT OpenMP_FIND_COMPONENTS) - set(OpenMP_FINDLIST C CXX Fortran) + set(OpenMP_FINDLIST C CXX CUDA Fortran) else() set(OpenMP_FINDLIST ${OpenMP_FIND_COMPONENTS}) endif() @@ -644,7 +660,7 @@ foreach(LANG IN LISTS OpenMP_FINDLIST) endforeach() unset(_OpenMP_REQ_VARS) -foreach(LANG IN ITEMS C CXX Fortran) +foreach(LANG IN ITEMS C CXX CUDA Fortran) if((NOT OpenMP_FIND_COMPONENTS AND CMAKE_${LANG}_COMPILER_LOADED) OR LANG IN_LIST OpenMP_FIND_COMPONENTS) list(APPEND _OpenMP_REQ_VARS "OpenMP_${LANG}_FOUND") endif() @@ -666,8 +682,8 @@ if(CMAKE_Fortran_COMPILER_LOADED AND OpenMP_Fortran_FOUND) endif() endif() -if(NOT ( CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED OR CMAKE_Fortran_COMPILER_LOADED )) - message(SEND_ERROR "FindOpenMP requires the C, CXX or Fortran languages to be enabled") +if(NOT ( CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED OR CMAKE_CUDA_COMPILER_LOADED OR CMAKE_Fortran_COMPILER_LOADED )) + message(SEND_ERROR "FindOpenMP requires the C, CXX, CUDA, or Fortran languages to be enabled") endif() unset(OpenMP_C_CXX_TEST_SOURCE) diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index 01b18f31a..33a07650c 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -207,7 +207,9 @@ endfunction() if (UNIX) find_package(PkgConfig QUIET) - pkg_check_modules(_OPENSSL QUIET openssl) + if(PKG_CONFIG_FOUND) + pkg_check_modules(_OPENSSL QUIET openssl) + endif() endif () # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake index 8ad3270ba..197e71f96 100644 --- a/Modules/FindProtobuf.cmake +++ b/Modules/FindProtobuf.cmake @@ -292,6 +292,12 @@ function(protobuf_generate) return() endif() + if(NOT TARGET protobuf::protoc) + message(SEND_ERROR "protoc executable not found. " + "Please define the Protobuf_PROTOC_EXECUTABLE variable or ensure that protoc is in CMake's search path.") + return() + endif() + if(protobuf_generate_APPEND_PATH) # Create an include path for each file specified foreach(_file ${protobuf_generate_PROTOS}) @@ -302,8 +308,6 @@ function(protobuf_generate) list(APPEND _protobuf_include_path -I ${_abs_dir}) endif() endforeach() - else() - set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) endif() foreach(DIR ${protobuf_generate_IMPORT_DIRS}) @@ -314,6 +318,10 @@ function(protobuf_generate) endif() endforeach() + if(NOT protobuf_generate_APPEND_PATH) + list(APPEND _protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + set(_generated_srcs_all) foreach(_proto ${protobuf_generate_PROTOS}) get_filename_component(_abs_file ${_proto} ABSOLUTE) @@ -323,7 +331,19 @@ function(protobuf_generate) set(_possible_rel_dir) if (NOT protobuf_generate_APPEND_PATH) - set(_possible_rel_dir ${_rel_dir}/) + foreach(DIR ${_protobuf_include_path}) + if(NOT DIR STREQUAL "-I") + file(RELATIVE_PATH _rel_dir ${DIR} ${_abs_dir}) + if(_rel_dir STREQUAL _abs_dir) + continue() + endif() + string(FIND "${_rel_dir}" "../" _is_in_parent_folder) + if (NOT ${_is_in_parent_folder} EQUAL 0) + break() + endif() + endif() + endforeach() + set(_possible_rel_dir ${_rel_dir}/) endif() set(_generated_srcs) diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake index 797891b9f..856b9ecea 100644 --- a/Modules/FindPython.cmake +++ b/Modules/FindPython.cmake @@ -451,7 +451,7 @@ Hints See `IronPython `_. * ``PyPy``: This implementation use ``RPython`` language and ``RPython translation toolchain`` to produce the python interpreter. - See `PyPy `_. + See `PyPy `_. The default value is: diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index faca5ae98..e0bdbc540 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -30,7 +30,7 @@ if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") - set(_${_PYTHON_PREFIX}_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + set(_${_PYTHON_PREFIX}_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2") set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake index eb2b5a8d0..cdb40380a 100644 --- a/Modules/FindPython2.cmake +++ b/Modules/FindPython2.cmake @@ -339,7 +339,7 @@ Hints See `IronPython `_. * ``PyPy``: This implementation use ``RPython`` language and ``RPython translation toolchain`` to produce the python interpreter. - See `PyPy `_. + See `PyPy `_. The default value is: diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake index 19b022461..2061a27a8 100644 --- a/Modules/FindPython3.cmake +++ b/Modules/FindPython3.cmake @@ -449,7 +449,7 @@ Hints See `IronPython `_. * ``PyPy``: This implementation use ``RPython`` language and ``RPython translation toolchain`` to produce the python interpreter. - See `PyPy `_. + See `PyPy `_. The default value is: diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake index 443c6ebf6..e5bcb5f5d 100644 --- a/Modules/FindPythonInterp.cmake +++ b/Modules/FindPythonInterp.cmake @@ -67,7 +67,7 @@ unset(_Python_NAMES) set(_PYTHON1_VERSIONS 1.6 1.5) set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) -set(_PYTHON3_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +set(_PYTHON3_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) if(PythonInterp_FIND_VERSION) if(PythonInterp_FIND_VERSION_COUNT GREATER 1) diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake index 112adaeba..6bf244e32 100644 --- a/Modules/FindPythonLibs.cmake +++ b/Modules/FindPythonLibs.cmake @@ -81,7 +81,10 @@ if(IS_ABSOLUTE "${PYTHON_EXECUTABLE}") endif() endif() -include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindFrameworks.cmake) +block(SCOPE_FOR POLICIES) + cmake_policy(SET CMP0173 OLD) + include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindFrameworks.cmake) +endblock() # Search for the python framework on Apple. CMAKE_FIND_FRAMEWORKS(Python) @@ -96,7 +99,7 @@ set(CMAKE_FIND_FRAMEWORK LAST) set(_PYTHON1_VERSIONS 1.6 1.5) set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) -set(_PYTHON3_VERSIONS 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +set(_PYTHON3_VERSIONS 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) if(PythonLibs_FIND_VERSION) if(PythonLibs_FIND_VERSION_COUNT GREATER 1) diff --git a/Modules/FindSquish.cmake b/Modules/FindSquish.cmake index 9a17fdbaf..bb4caa189 100644 --- a/Modules/FindSquish.cmake +++ b/Modules/FindSquish.cmake @@ -200,6 +200,8 @@ macro(squish_v3_add_test testName testAUT testCase envVars testWraper) message(STATUS "Using squish_v3_add_test(), but SQUISH_VERSION_MAJOR is ${SQUISH_VERSION_MAJOR}.\nThis may not work.") endif() + # There's no target used for this command, so we don't need to do anything + # here for CMP0178. add_test(${testName} ${CMAKE_COMMAND} -V -VV "-Dsquish_version:STRING=3" @@ -258,6 +260,8 @@ function(squish_v4_add_test testName) message("SETTINGSGROUP is deprecated and will be ignored.") endif() + # There's no target used for this command, so we don't need to do anything + # here for CMP0178. add_test(NAME ${testName} COMMAND ${CMAKE_COMMAND} -V -VV "-Dsquish_version:STRING=4" diff --git a/Modules/FindTCL.cmake b/Modules/FindTCL.cmake index 9b771dcd7..eb536674f 100644 --- a/Modules/FindTCL.cmake +++ b/Modules/FindTCL.cmake @@ -45,7 +45,10 @@ variables were moved or removed. Changes compared to CMake 2.4 are: and dig from there. #]=======================================================================] -include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindFrameworks.cmake) +block(SCOPE_FOR POLICIES) + cmake_policy(SET CMP0173 OLD) + include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindFrameworks.cmake) +endblock() include(${CMAKE_CURRENT_LIST_DIR}/FindTclsh.cmake) include(${CMAKE_CURRENT_LIST_DIR}/FindWish.cmake) diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index a01c010ef..92ee25d62 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake @@ -210,6 +210,8 @@ function(xctest_add_test name bundle) # register test + # There's no target used for this command, so we don't need to do anything + # here for CMP0178. add_test( NAME ${name} COMMAND ${XCTest_EXECUTABLE} $) diff --git a/Modules/FindwxWidgets.cmake b/Modules/FindwxWidgets.cmake index b42a85ec6..88aaf9620 100644 --- a/Modules/FindwxWidgets.cmake +++ b/Modules/FindwxWidgets.cmake @@ -467,7 +467,7 @@ if(wxWidgets_FIND_STYLE STREQUAL "win32") list(APPEND wxWidgets_LIBRARIES imm32) endif() - list(APPEND wxWidgets_LIBRARIES winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32) + list(APPEND wxWidgets_LIBRARIES gdiplus msimg32 winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32) endmacro() #------------------------------------------------------------------- diff --git a/Modules/FortranCInterface/CMakeLists.txt b/Modules/FortranCInterface/CMakeLists.txt index 2e8e14fea..56a045cc9 100644 --- a/Modules/FortranCInterface/CMakeLists.txt +++ b/Modules/FortranCInterface/CMakeLists.txt @@ -100,6 +100,10 @@ target_link_libraries(symbols PUBLIC myfort) # the C compiler produces PIC even if it is not its default. set_property(TARGET symbols PROPERTY POSITION_INDEPENDENT_CODE 1) +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran") + add_compile_options(--implicit-interface) +endif() + # Require symbols through Fortran. add_executable(FortranCInterface main.F call_sub.f ${call_mod}) target_link_libraries(FortranCInterface PUBLIC symbols) diff --git a/Modules/FortranCInterface/Detect.cmake b/Modules/FortranCInterface/Detect.cmake index c975a4b45..00b8ad562 100644 --- a/Modules/FortranCInterface/Detect.cmake +++ b/Modules/FortranCInterface/Detect.cmake @@ -96,8 +96,8 @@ set(_case_MYSUB "UPPER") set(_case_MY_SUB "UPPER") set(_global_regex "^(_*)(mysub|MYSUB)([_$]*)$") set(_global__regex "^(_*)(my_sub|MY_SUB)([_$]*)$") -set(_module_regex "^(_*)([A-Za-z$]*)(mymodule|MYMODULE)([A-Za-z_$]*)(mysub|MYSUB)([_$]*)$") -set(_module__regex "^(_*)([A-Za-z$]*)(my_module|MY_MODULE)([A-Za-z_$]*)(my_sub|MY_SUB)([_$]*)$") +set(_module_regex "^([A-Za-z_$]*)(mymodule|MYMODULE)([A-Za-z_$]*)(mysub|MYSUB)([_$]*)$") +set(_module__regex "^([A-Za-z_$]*)(my_module|MY_MODULE)([A-Za-z_$]*)(my_sub|MY_SUB)([_$]*)$") # Parse the symbol names. foreach(symbol ${FortranCInterface_SYMBOLS}) @@ -116,7 +116,7 @@ foreach(symbol ${FortranCInterface_SYMBOLS}) # Look for module symbols. string(REGEX REPLACE "${_module_${form}regex}" - "\\1\\2;\\3;\\4;\\5;\\6" pieces "${symbol}") + "\\1;\\2;\\3;\\4;\\5" pieces "${symbol}") list(LENGTH pieces len) if(len EQUAL 5) set(FortranCInterface_MODULE_${form}SYMBOL "${symbol}") diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index e187804c5..63745fe62 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -42,7 +42,7 @@ same as the Google Test name (i.e. ``suite.testcase``); see also gtest_add_tests(TARGET target [SOURCES src1...] - [EXTRA_ARGS arg1...] + [EXTRA_ARGS args...] [WORKING_DIRECTORY dir] [TEST_PREFIX prefix] [TEST_SUFFIX suffix] @@ -72,9 +72,12 @@ same as the Google Test name (i.e. ``suite.testcase``); see also this option is not given, the :prop_tgt:`SOURCES` property of the specified ``target`` will be used to obtain the list of sources. - ``EXTRA_ARGS arg1...`` + ``EXTRA_ARGS args...`` Any extra arguments to pass on the command line to each test case. + .. versionchanged:: 3.31 + Empty values in ``args...`` are preserved, see :policy:`CMP0178`. + ``WORKING_DIRECTORY dir`` Specifies the directory in which to run the discovered test cases. If this option is not provided, the current binary directory is used. @@ -101,6 +104,11 @@ same as the Google Test name (i.e. ``suite.testcase``); see also with the list of discovered test cases. This allows the caller to do things like manipulate test properties of the discovered tests. + .. versionchanged:: 3.31 + Empty values in the :prop_tgt:`TEST_LAUNCHER` and + :prop_tgt:`CROSSCOMPILING_EMULATOR` target properties are preserved, + see policy :policy:`CMP0178`. + Usage example: .. code-block:: cmake @@ -147,7 +155,7 @@ same as the Google Test name (i.e. ``suite.testcase``); see also for available tests:: gtest_discover_tests(target - [EXTRA_ARGS arg1...] + [EXTRA_ARGS args...] [WORKING_DIRECTORY dir] [TEST_PREFIX prefix] [TEST_SUFFIX suffix] @@ -158,19 +166,20 @@ same as the Google Test name (i.e. ``suite.testcase``); see also [DISCOVERY_TIMEOUT seconds] [XML_OUTPUT_DIR dir] [DISCOVERY_MODE ] + [DISCOVERY_EXTRA_ARGS args...] ) .. versionadded:: 3.10 - ``gtest_discover_tests()`` sets up a post-build command on the test executable - that generates the list of tests by parsing the output from running the test - with the ``--gtest_list_tests`` argument. Compared to the source parsing - approach of :command:`gtest_add_tests`, this ensures that the full list of - tests, including instantiations of parameterized tests, is obtained. Since - test discovery occurs at build time, it is not necessary to re-run CMake when - the list of tests changes. - However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set - in order to function in a cross-compiling environment. + ``gtest_discover_tests()`` sets up a post-build or pre-test command on the + test executable that generates the list of tests by parsing the output from + running the test executable with the ``--gtest_list_tests`` argument. + Compared to the source parsing approach of :command:`gtest_add_tests`, + this ensures that the full list of tests, including instantiations of + parameterized tests, is obtained. Since test discovery occurs at build + or test time, it is not necessary to re-run CMake when the list of tests + changes. However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` + is properly set in order to function in a cross-compiling environment. Additionally, setting properties on tests is somewhat less convenient, since the tests are not available at CMake time. Additional test properties may be @@ -178,7 +187,8 @@ same as the Google Test name (i.e. ``suite.testcase``); see also more fine-grained test control is needed, custom content may be provided through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` directory property. The set of discovered tests is made accessible to such a - script via the ``_TESTS`` variable. + script via the ``_TESTS`` variable (see the ``TEST_LIST`` option + below for further discussion and limitations). The options are: @@ -187,9 +197,12 @@ same as the Google Test name (i.e. ``suite.testcase``); see also executable target. CMake will substitute the location of the built executable when running the test. - ``EXTRA_ARGS arg1...`` + ``EXTRA_ARGS args...`` Any extra arguments to pass on the command line to each test case. + .. versionchanged:: 3.31 + Empty values in ``args...`` are preserved, see :policy:`CMP0178`. + ``WORKING_DIRECTORY dir`` Specifies the directory in which to run the discovered test cases. If this option is not provided, the current binary directory is used. @@ -236,6 +249,11 @@ same as the Google Test name (i.e. ``suite.testcase``); see also executable is being used in multiple calls to ``gtest_discover_tests()``. Note that this variable is only available in CTest. + Due to a limitation of CMake's parsing rules, any test with a square + bracket in its name will be omitted from the list of tests stored in + this variable. Such tests will still be defined and executed by + ``ctest`` as normal though. + ``DISCOVERY_TIMEOUT num`` .. versionadded:: 3.10.3 @@ -283,11 +301,25 @@ same as the Google Test name (i.e. ``suite.testcase``); see also for globally selecting a preferred test discovery behavior without having to modify each call site. + ``DISCOVERY_EXTRA_ARGS args...`` + .. versionadded:: 3.31 + + Any extra arguments to pass on the command line for the discovery command. + + .. versionadded:: 3.29 + The :prop_tgt:`TEST_LAUNCHER` target property is honored during test + discovery and test execution. + + .. versionchanged:: 3.31 + Empty values in the :prop_tgt:`TEST_LAUNCHER` and + :prop_tgt:`CROSSCOMPILING_EMULATOR` target properties are preserved, + see policy :policy:`CMP0178`. + #]=======================================================================] # Save project's policies -cmake_policy(PUSH) -cmake_policy(SET CMP0057 NEW) # if IN_LIST +block(SCOPE_FOR POLICIES) +cmake_policy(VERSION 3.30) #------------------------------------------------------------------------------ function(gtest_add_tests) @@ -312,37 +344,69 @@ function(gtest_add_tests) ) set(allKeywords ${options} ${oneValueArgs} ${multiValueArgs}) + cmake_policy(GET CMP0178 cmp0178 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + unset(sources) if("${ARGV0}" IN_LIST allKeywords) - cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(cmp0178 STREQUAL "NEW") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${options}" "${oneValueArgs}" "${multiValueArgs}" + ) + else() + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(NOT cmp0178 STREQUAL "OLD") + block(SCOPE_FOR VARIABLES) + cmake_parse_arguments(PARSE_ARGV 0 arg_new + "${options}" "${oneValueArgs}" "${multiValueArgs}" + ) + # Due to a quirk of cmake_parse_arguments(PARSE_ARGV), + # arg_new_EXTRA_ARGS will have semicolons already escaped, but + # arg_EXTRA_ARGS won't. We need to pass the former through one round + # of command argument parsing to de-escape them for comparison with + # the latter. + set(__newArgs ${arg_new_EXTRA_ARGS}) + if(NOT "${arg_EXTRA_ARGS}" STREQUAL "${__newArgs}") + cmake_policy(GET_WARNING CMP0178 cmp0178_warning) + message(AUTHOR_WARNING + "The EXTRA_ARGS contain one or more empty values. Those empty " + "values are being silently discarded to preserve backward " + "compatibility.\n" + "${cmp0178_warning}" + ) + endif() + endblock() + endif() + endif() set(autoAddSources YES) else() # Non-keyword syntax, convert to keyword form if (ARGC LESS 3) message(FATAL_ERROR "gtest_add_tests() without keyword options requires at least 3 arguments") endif() - set(ARGS_TARGET "${ARGV0}") - set(ARGS_EXTRA_ARGS "${ARGV1}") + set(arg_TARGET "${ARGV0}") + set(arg_EXTRA_ARGS "${ARGV1}") if(NOT "${ARGV2}" STREQUAL "AUTO") - set(ARGS_SOURCES "${ARGV}") - list(REMOVE_AT ARGS_SOURCES 0 1) + set(arg_SOURCES "${ARGV}") + list(REMOVE_AT arg_SOURCES 0 1) endif() endif() # The non-keyword syntax allows the first argument to be an arbitrary # executable rather than a target if source files are also provided. In all # other cases, both forms require a target. - if(NOT TARGET "${ARGS_TARGET}" AND NOT ARGS_SOURCES) - message(FATAL_ERROR "${ARGS_TARGET} does not define an existing CMake target") + if(NOT TARGET "${arg_TARGET}" AND NOT arg_SOURCES) + message(FATAL_ERROR "${arg_TARGET} does not define an existing CMake target") endif() - if(NOT ARGS_WORKING_DIRECTORY) + if(NOT arg_WORKING_DIRECTORY) unset(workDir) else() - set(workDir WORKING_DIRECTORY "${ARGS_WORKING_DIRECTORY}") + set(workDir WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}") endif() - if(NOT ARGS_SOURCES) - get_property(ARGS_SOURCES TARGET ${ARGS_TARGET} PROPERTY SOURCES) + if(NOT arg_SOURCES) + get_property(arg_SOURCES TARGET ${arg_TARGET} PROPERTY SOURCES) endif() unset(testList) @@ -351,8 +415,8 @@ function(gtest_add_tests) set(gtest_test_type_regex "(TYPED_TEST|TEST)_?[FP]?") set(each_line_regex "([^\r\n]*[\r\n])") - foreach(source IN LISTS ARGS_SOURCES) - if(NOT ARGS_SKIP_DEPENDENCY) + foreach(source IN LISTS arg_SOURCES) + if(NOT arg_SKIP_DEPENDENCY) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source}) endif() file(READ "${source}" contents) @@ -408,6 +472,11 @@ function(gtest_add_tests) continue() endif() + set(extra_args "") + foreach(arg IN LISTS arg_EXTRA_ARGS) + string(APPEND extra_args " [==[${arg}]==]") + endforeach() + # Make sure tests disabled in GTest get disabled in CTest if(gtest_test_name MATCHES "(^|\\.)DISABLED_") # Add the disabled test if CMake is new enough @@ -420,26 +489,32 @@ function(gtest_add_tests) orig_test_name "${gtest_test_name}" ) set(ctest_test_name - ${ARGS_TEST_PREFIX}${orig_test_name}${ARGS_TEST_SUFFIX} + ${arg_TEST_PREFIX}${orig_test_name}${arg_TEST_SUFFIX} ) - add_test(NAME ${ctest_test_name} - ${workDir} - COMMAND ${ARGS_TARGET} - --gtest_also_run_disabled_tests - --gtest_filter=${gtest_test_name} - ${ARGS_EXTRA_ARGS} + cmake_language(EVAL CODE " + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${arg_TARGET} + --gtest_also_run_disabled_tests + --gtest_filter=${gtest_test_name} + ${extra_args} + __CMP0178 [==[${cmp0178}]==] + )" ) set_tests_properties(${ctest_test_name} PROPERTIES DISABLED TRUE DEF_SOURCE_LINE "${source}:${accumulate_line}") list(APPEND testList ${ctest_test_name}) endif() else() - set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX}) - add_test(NAME ${ctest_test_name} - ${workDir} - COMMAND ${ARGS_TARGET} - --gtest_filter=${gtest_test_name} - ${ARGS_EXTRA_ARGS} + set(ctest_test_name ${arg_TEST_PREFIX}${gtest_test_name}${arg_TEST_SUFFIX}) + cmake_language(EVAL CODE " + add_test(NAME ${ctest_test_name} + ${workDir} + COMMAND ${arg_TARGET} + --gtest_filter=${gtest_test_name} + ${extra_args} + __CMP0178 [==[${cmp0178}]==] + )" ) # Makes sure a skipped GTest is reported as so by CTest set_tests_properties( @@ -453,49 +528,64 @@ function(gtest_add_tests) endforeach() endforeach() - if(ARGS_TEST_LIST) - set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE) + if(arg_TEST_LIST) + set(${arg_TEST_LIST} ${testList} PARENT_SCOPE) endif() endfunction() #------------------------------------------------------------------------------ -function(gtest_discover_tests TARGET) - cmake_parse_arguments( - "" - "NO_PRETTY_TYPES;NO_PRETTY_VALUES" - "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE" - "EXTRA_ARGS;PROPERTIES;TEST_FILTER" - ${ARGN} +function(gtest_discover_tests target) + set(options + NO_PRETTY_TYPES + NO_PRETTY_VALUES + ) + set(oneValueArgs + TEST_PREFIX + TEST_SUFFIX + WORKING_DIRECTORY + TEST_LIST + DISCOVERY_TIMEOUT + XML_OUTPUT_DIR + DISCOVERY_MODE + ) + set(multiValueArgs + EXTRA_ARGS + DISCOVERY_EXTRA_ARGS + PROPERTIES + TEST_FILTER + ) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${options}" "${oneValueArgs}" "${multiValueArgs}" ) - if(NOT _WORKING_DIRECTORY) - set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + if(NOT arg_WORKING_DIRECTORY) + set(arg_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() - if(NOT _TEST_LIST) - set(_TEST_LIST ${TARGET}_TESTS) + if(NOT arg_TEST_LIST) + set(arg_TEST_LIST ${target}_TESTS) endif() - if(NOT _DISCOVERY_TIMEOUT) - set(_DISCOVERY_TIMEOUT 5) + if(NOT arg_DISCOVERY_TIMEOUT) + set(arg_DISCOVERY_TIMEOUT 5) endif() - if(NOT _DISCOVERY_MODE) + if(NOT arg_DISCOVERY_MODE) if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE) set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") endif() - set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE}) + set(arg_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE}) endif() get_property( has_counter - TARGET ${TARGET} + TARGET ${target} PROPERTY CTEST_DISCOVERED_TEST_COUNTER SET ) if(has_counter) get_property( counter - TARGET ${TARGET} + TARGET ${target} PROPERTY CTEST_DISCOVERED_TEST_COUNTER ) math(EXPR counter "${counter} + 1") @@ -503,17 +593,17 @@ function(gtest_discover_tests TARGET) set(counter 1) endif() set_property( - TARGET ${TARGET} + TARGET ${target} PROPERTY CTEST_DISCOVERED_TEST_COUNTER ${counter} ) # Define rule to generate test list for aforementioned test executable - set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}[${counter}]") + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${target}[${counter}]") set(ctest_include_file "${ctest_file_base}_include.cmake") set(ctest_tests_file "${ctest_file_base}_tests.cmake") get_property(test_launcher - TARGET ${TARGET} + TARGET ${target} PROPERTY TEST_LAUNCHER ) cmake_policy(GET CMP0158 _CMP0158 @@ -521,7 +611,7 @@ function(gtest_discover_tests TARGET) ) if(NOT _CMP0158 OR _CMP0158 STREQUAL "OLD" OR _CMP0158 STREQUAL "NEW" AND CMAKE_CROSSCOMPILING) get_property(crosscompiling_emulator - TARGET ${TARGET} + TARGET ${target} PROPERTY CROSSCOMPILING_EMULATOR ) endif() @@ -536,26 +626,59 @@ function(gtest_discover_tests TARGET) set(test_executor "") endif() - if(_DISCOVERY_MODE STREQUAL "POST_BUILD") + cmake_policy(GET CMP0178 cmp0178 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) + if(NOT cmp0178 STREQUAL "NEW") + # Preserve old behavior where empty list items are silently discarded + set(test_executor_orig "${test_executor}") + set(test_executor ${test_executor}) + set(arg_EXTRA_ARGS_orig "${arg_EXTRA_ARGS}") + set(arg_EXTRA_ARGS ${arg_EXTRA_ARGS}) + if(NOT cmp0178 STREQUAL "OLD") + if(NOT "${test_executor}" STREQUAL "${test_executor_orig}") + cmake_policy(GET_WARNING CMP0178 cmp0178_warning) + message(AUTHOR_WARNING + "The '${target}' target's TEST_LAUNCHER or CROSSCOMPILING_EMULATOR " + "test properties contain one or more empty values. Those empty " + "values are being silently discarded to preserve backward " + "compatibility.\n" + "${cmp0178_warning}" + ) + endif() + if(NOT "${arg_EXTRA_ARGS}" STREQUAL "${arg_EXTRA_ARGS_orig}") + cmake_policy(GET_WARNING CMP0178 cmp0178_warning) + message(AUTHOR_WARNING + "The EXTRA_ARGS value contains one or more empty values. " + "Those empty values are being silently discarded to preserve " + "backward compatibility.\n" + "${cmp0178_warning}" + ) + endif() + endif() + endif() + + if(arg_DISCOVERY_MODE STREQUAL "POST_BUILD") add_custom_command( - TARGET ${TARGET} POST_BUILD + TARGET ${target} POST_BUILD BYPRODUCTS "${ctest_tests_file}" COMMAND "${CMAKE_COMMAND}" - -D "TEST_TARGET=${TARGET}" - -D "TEST_EXECUTABLE=$" + -D "TEST_TARGET=${target}" + -D "TEST_EXECUTABLE=$" -D "TEST_EXECUTOR=${test_executor}" - -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" - -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" - -D "TEST_PROPERTIES=${_PROPERTIES}" - -D "TEST_PREFIX=${_TEST_PREFIX}" - -D "TEST_SUFFIX=${_TEST_SUFFIX}" - -D "TEST_FILTER=${_TEST_FILTER}" - -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}" - -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}" - -D "TEST_LIST=${_TEST_LIST}" + -D "TEST_WORKING_DIR=${arg_WORKING_DIRECTORY}" + -D "TEST_EXTRA_ARGS=${arg_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${arg_PROPERTIES}" + -D "TEST_PREFIX=${arg_TEST_PREFIX}" + -D "TEST_SUFFIX=${arg_TEST_SUFFIX}" + -D "TEST_FILTER=${arg_TEST_FILTER}" + -D "NO_PRETTY_TYPES=${arg_NO_PRETTY_TYPES}" + -D "NO_PRETTY_VALUES=${arg_NO_PRETTY_VALUES}" + -D "TEST_LIST=${arg_TEST_LIST}" -D "CTEST_FILE=${ctest_tests_file}" - -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}" - -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}" + -D "TEST_DISCOVERY_TIMEOUT=${arg_DISCOVERY_TIMEOUT}" + -D "TEST_DISCOVERY_EXTRA_ARGS=${arg_DISCOVERY_EXTRA_ARGS}" + -D "TEST_XML_OUTPUT_DIR=${arg_XML_OUTPUT_DIR}" -P "${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake" VERBATIM ) @@ -564,10 +687,10 @@ function(gtest_discover_tests TARGET) "if(EXISTS \"${ctest_tests_file}\")\n" " include(\"${ctest_tests_file}\")\n" "else()\n" - " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n" + " add_test(${target}_NOT_BUILT ${target}_NOT_BUILT)\n" "endif()\n" ) - elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + elseif(arg_DISCOVERY_MODE STREQUAL "PRE_TEST") get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG @@ -578,46 +701,58 @@ function(gtest_discover_tests TARGET) endif() string(CONCAT ctest_include_content - "if(EXISTS \"$\")" "\n" + "if(EXISTS \"$\")" "\n" " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" - " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$\" OR\n" + " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$\" OR\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" " include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" " gtest_discover_tests_impl(" "\n" - " TEST_EXECUTABLE" " [==[" "$" "]==]" "\n" - " TEST_EXECUTOR" " [==[" "${test_executor}" "]==]" "\n" - " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" - " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" - " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" - " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" - " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" - " TEST_FILTER" " [==[" "${_TEST_FILTER}" "]==]" "\n" - " NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n" - " NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n" - " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" - " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" - " TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}" "]==]" "\n" - " TEST_XML_OUTPUT_DIR" " [==[" "${_XML_OUTPUT_DIR}" "]==]" "\n" + " TEST_EXECUTABLE" " [==[$]==]" "\n" + " TEST_EXECUTOR" " [==[${test_executor}]==]" "\n" + " TEST_WORKING_DIR" " [==[${arg_WORKING_DIRECTORY}]==]" "\n" + " TEST_EXTRA_ARGS" " [==[${arg_EXTRA_ARGS}]==]" "\n" + " TEST_PROPERTIES" " [==[${arg_PROPERTIES}]==]" "\n" + " TEST_PREFIX" " [==[${arg_TEST_PREFIX}]==]" "\n" + " TEST_SUFFIX" " [==[${arg_TEST_SUFFIX}]==]" "\n" + " TEST_FILTER" " [==[${arg_TEST_FILTER}]==]" "\n" + " NO_PRETTY_TYPES" " [==[${arg_NO_PRETTY_TYPES}]==]" "\n" + " NO_PRETTY_VALUES" " [==[${arg_NO_PRETTY_VALUES}]==]" "\n" + " TEST_LIST" " [==[${arg_TEST_LIST}]==]" "\n" + " CTEST_FILE" " [==[${ctest_tests_file}]==]" "\n" + " TEST_DISCOVERY_TIMEOUT" " [==[${arg_DISCOVERY_TIMEOUT}]==]" "\n" + " TEST_DISCOVERY_EXTRA_ARGS [==[${arg_DISCOVERY_EXTRA_ARGS}]==]" "\n" + " TEST_XML_OUTPUT_DIR" " [==[${arg_XML_OUTPUT_DIR}]==]" "\n" " )" "\n" " endif()" "\n" " include(\"${ctest_tests_file}\")" "\n" "else()" "\n" - " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" + " add_test(${target}_NOT_BUILT ${target}_NOT_BUILT)" "\n" "endif()" "\n" ) if(GENERATOR_IS_MULTI_CONFIG) foreach(_config ${CMAKE_CONFIGURATION_TYPES}) - file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $) + file(GENERATE + OUTPUT "${ctest_file_base}_include-${_config}.cmake" + CONTENT "${ctest_include_content}" + CONDITION $ + ) endforeach() - file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")") + file(WRITE "${ctest_include_file}" + "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" + ) else() - file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") - file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") + file(GENERATE + OUTPUT "${ctest_file_base}_include.cmake" + CONTENT "${ctest_include_content}" + ) + file(WRITE "${ctest_include_file}" + "include(\"${ctest_file_base}_include.cmake\")" + ) endif() else() - message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") + message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${arg_DISCOVERY_MODE}") endif() # Add discovered tests to directory TEST_INCLUDE_FILES @@ -630,4 +765,4 @@ endfunction() ############################################################################### # Restore project's policies -cmake_policy(POP) +endblock() diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake index eea267d65..0374b8f9b 100644 --- a/Modules/GoogleTestAddTests.cmake +++ b/Modules/GoogleTestAddTests.cmake @@ -1,14 +1,15 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION ${CMAKE_VERSION}) +cmake_minimum_required(VERSION 3.30) +cmake_policy(SET CMP0174 NEW) # TODO: Remove this when we can update the above to 3.31 -# Overwrite possibly existing ${_CTEST_FILE} with empty file +# Overwrite possibly existing ${arg_CTEST_FILE} with empty file set(flush_tests_MODE WRITE) -# Flushes script to ${_CTEST_FILE} +# Flushes script to ${arg_CTEST_FILE} macro(flush_script) - file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}") + file(${flush_tests_MODE} "${arg_CTEST_FILE}" "${script}") set(flush_tests_MODE APPEND PARENT_SCOPE) set(script "") @@ -20,7 +21,7 @@ macro(flush_tests_buffer) set(tests_buffer "") endmacro() -function(add_command NAME TEST_NAME) +function(add_command name test_name) set(args "") foreach(arg ${ARGN}) if(arg MATCHES "[^-./:a-zA-Z0-9_]") @@ -29,7 +30,7 @@ function(add_command NAME TEST_NAME) string(APPEND args " ${arg}") endif() endforeach() - string(APPEND script "${NAME}(${TEST_NAME} ${args})\n") + string(APPEND script "${name}(${test_name} ${args})\n") string(LENGTH "${script}" script_len) if(${script_len} GREATER "50000") flush_script() @@ -37,82 +38,117 @@ function(add_command NAME TEST_NAME) set(script "${script}" PARENT_SCOPE) endfunction() -function(generate_testname_guards OUTPUT OPEN_GUARD_VAR CLOSE_GUARD_VAR) +function(generate_testname_guards output open_guard_var close_guard_var) set(open_guard "[=[") set(close_guard "]=]") set(counter 1) - while("${OUTPUT}" MATCHES "${close_guard}") + while("${output}" MATCHES "${close_guard}") math(EXPR counter "${counter} + 1") string(REPEAT "=" ${counter} equals) set(open_guard "[${equals}[") set(close_guard "]${equals}]") endwhile() - set(${OPEN_GUARD_VAR} "${open_guard}" PARENT_SCOPE) - set(${CLOSE_GUARD_VAR} "${close_guard}" PARENT_SCOPE) + set(${open_guard_var} "${open_guard}" PARENT_SCOPE) + set(${close_guard_var} "${close_guard}" PARENT_SCOPE) endfunction() -function(escape_square_brackets OUTPUT BRACKET PLACEHOLDER PLACEHOLDER_VAR OUTPUT_VAR) - if("${OUTPUT}" MATCHES "\\${BRACKET}") - set(placeholder "${PLACEHOLDER}") - while("${OUTPUT}" MATCHES "${placeholder}") +function(escape_square_brackets output bracket placeholder placeholder_var output_var) + if("${output}" MATCHES "\\${bracket}") + set(placeholder "${placeholder}") + while("${output}" MATCHES "${placeholder}") set(placeholder "${placeholder}_") endwhile() - string(REPLACE "${BRACKET}" "${placeholder}" OUTPUT "${OUTPUT}") - set(${PLACEHOLDER_VAR} "${placeholder}" PARENT_SCOPE) - set(${OUTPUT_VAR} "${OUTPUT}" PARENT_SCOPE) + string(REPLACE "${bracket}" "${placeholder}" output "${output}") + set(${placeholder_var} "${placeholder}" PARENT_SCOPE) + set(${output_var} "${output}" PARENT_SCOPE) endif() endfunction() function(gtest_discover_tests_impl) - cmake_parse_arguments( - "" - "" - "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR;TEST_FILTER" - "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR" - ${ARGN} + set(options "") + set(oneValueArgs + NO_PRETTY_TYPES # These two take a value, unlike gtest_discover_tests() + NO_PRETTY_VALUES # + TEST_EXECUTABLE + TEST_WORKING_DIR + TEST_PREFIX + TEST_SUFFIX + TEST_LIST + CTEST_FILE + TEST_DISCOVERY_TIMEOUT + TEST_XML_OUTPUT_DIR + # The following are all multi-value arguments in gtest_discover_tests(), + # but they are each given to us as a single argument. We parse them that + # way to avoid problems with preserving empty list values and escaping. + TEST_FILTER + TEST_EXTRA_ARGS + TEST_DISCOVERY_EXTRA_ARGS + TEST_PROPERTIES + TEST_EXECUTOR + ) + set(multiValueArgs "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${options}" "${oneValueArgs}" "${multiValueArgs}" ) - set(prefix "${_TEST_PREFIX}") - set(suffix "${_TEST_SUFFIX}") - set(extra_args ${_TEST_EXTRA_ARGS}) - set(properties ${_TEST_PROPERTIES}) + set(prefix "${arg_TEST_PREFIX}") + set(suffix "${arg_TEST_SUFFIX}") set(script) set(suite) set(tests) set(tests_buffer) - if(_TEST_FILTER) - set(filter "--gtest_filter=${_TEST_FILTER}") + if(arg_TEST_FILTER) + set(filter "--gtest_filter=${arg_TEST_FILTER}") else() set(filter) endif() + # CMP0178 has already been handled in gtest_discover_tests(), so we only need + # to implement NEW behavior here. This means preserving empty arguments for + # TEST_EXECUTOR. For OLD or WARN, gtest_discover_tests() already removed any + # empty arguments. + set(launcherArgs "") + if(NOT "${arg_TEST_EXECUTOR}" STREQUAL "") + list(JOIN arg_TEST_EXECUTOR "]==] [==[" launcherArgs) + set(launcherArgs "[==[${launcherArgs}]==]") + endif() + # Run test executable to get list of available tests - if(NOT EXISTS "${_TEST_EXECUTABLE}") + if(NOT EXISTS "${arg_TEST_EXECUTABLE}") message(FATAL_ERROR "Specified test executable does not exist.\n" - " Path: '${_TEST_EXECUTABLE}'" + " Path: '${arg_TEST_EXECUTABLE}'" ) endif() - execute_process( - COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests ${filter} - WORKING_DIRECTORY "${_TEST_WORKING_DIR}" - TIMEOUT ${_TEST_DISCOVERY_TIMEOUT} - OUTPUT_VARIABLE output - RESULT_VARIABLE result + + set(discovery_extra_args "") + if(NOT "${arg_TEST_DISCOVERY_EXTRA_ARGS}" STREQUAL "") + list(JOIN arg_TEST_DISCOVERY_EXTRA_ARGS "]==] [==[" discovery_extra_args) + set(discovery_extra_args "[==[${discovery_extra_args}]==]") + endif() + + cmake_language(EVAL CODE + "execute_process( + COMMAND ${launcherArgs} [==[${arg_TEST_EXECUTABLE}]==] --gtest_list_tests ${filter} ${discovery_extra_args} + WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==] + TIMEOUT ${arg_TEST_DISCOVERY_TIMEOUT} + OUTPUT_VARIABLE output + RESULT_VARIABLE result + )" ) if(NOT ${result} EQUAL 0) string(REPLACE "\n" "\n " output "${output}") - if(_TEST_EXECUTOR) - set(path "${_TEST_EXECUTOR} ${_TEST_EXECUTABLE}") + if(arg_TEST_EXECUTOR) + set(path "${arg_TEST_EXECUTOR} ${arg_TEST_EXECUTABLE}") else() - set(path "${_TEST_EXECUTABLE}") + set(path "${arg_TEST_EXECUTABLE}") endif() message(FATAL_ERROR "Error running test executable.\n" " Path: '${path}'\n" - " Working directory: '${_TEST_WORKING_DIR}'\n" + " Working directory: '${arg_TEST_WORKING_DIR}'\n" " Result: ${result}\n" " Output:\n" " ${output}\n" @@ -136,7 +172,7 @@ function(gtest_discover_tests_impl) string(REGEX REPLACE "\\.( *#.*)?$" "" suite "${line}") if(line MATCHES "#") string(REGEX REPLACE "/[0-9].*" "" pretty_suite "${line}") - if(NOT _NO_PRETTY_TYPES) + if(NOT arg_NO_PRETTY_TYPES) string(REGEX REPLACE ".*/[0-9]+[ .#]+TypeParam = (.*)" "\\1" type_parameter "${line}") else() string(REGEX REPLACE ".*/([0-9]+)[ .#]+TypeParam = .*" "\\1" type_parameter "${line}") @@ -149,15 +185,15 @@ function(gtest_discover_tests_impl) string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}") else() string(STRIP "${line}" test) - if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES) + if(test MATCHES "#" AND NOT arg_NO_PRETTY_VALUES) string(REGEX REPLACE "/[0-9]+[ #]+GetParam\\(\\) = " "/" pretty_test "${test}") else() string(REGEX REPLACE " +#.*" "" pretty_test "${test}") endif() string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}") string(REGEX REPLACE " +#.*" "" test "${test}") - if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "") - set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml") + if(NOT "${arg_TEST_XML_OUTPUT_DIR}" STREQUAL "") + set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${arg_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml") else() unset(TEST_XML_OUTPUT_PARAM) endif() @@ -172,16 +208,36 @@ function(gtest_discover_tests_impl) endif() set(guarded_testname "${open_guard}${testname}${close_guard}") - # add to script - add_command(add_test - "${guarded_testname}" - ${_TEST_EXECUTOR} - "${_TEST_EXECUTABLE}" + # Add to script. Do not use add_command() here because it messes up the + # handling of empty values when forwarding arguments, and we need to + # preserve those carefully for arg_TEST_EXECUTOR and arg_EXTRA_ARGS. + string(APPEND script "add_test(${guarded_testname} ${launcherArgs}") + foreach(arg IN ITEMS + "${arg_TEST_EXECUTABLE}" "--gtest_filter=${suite}.${test}" "--gtest_also_run_disabled_tests" ${TEST_XML_OUTPUT_PARAM} - ${extra_args} - ) + ) + if(arg MATCHES "[^-./:a-zA-Z0-9_]") + string(APPEND script " [==[${arg}]==]") + else() + string(APPEND script " ${arg}") + endif() + endforeach() + if(arg_TEST_EXTRA_ARGS) + list(JOIN arg_TEST_EXTRA_ARGS "]==] [==[" extra_args) + string(APPEND script " [==[${extra_args}]==]") + endif() + string(APPEND script ")\n") + string(LENGTH "${script}" script_len) + if(${script_len} GREATER "50000") + # flush_script() expects to set variables in the parent scope, so we + # need to create one since we actually want the changes in our scope + block(SCOPE_FOR VARIABLES) + flush_script() + endblock() + endif() + if(suite MATCHES "^DISABLED_" OR test MATCHES "^DISABLED_") add_command(set_tests_properties "${guarded_testname}" @@ -192,12 +248,13 @@ function(gtest_discover_tests_impl) add_command(set_tests_properties "${guarded_testname}" PROPERTIES - WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + WORKING_DIRECTORY "${arg_TEST_WORKING_DIR}" SKIP_REGULAR_EXPRESSION "\\[ SKIPPED \\]" - ${properties} + ${arg_TEST_PROPERTIES} ) - # possibly unbalanced square brackets render lists invalid so skip such tests in ${_TEST_LIST} + # possibly unbalanced square brackets render lists invalid so skip such + # tests in ${arg_TEST_LIST} if(NOT "${testname}" MATCHES [=[(\[|\])]=]) # escape ; string(REPLACE [[;]] [[\\;]] testname "${testname}") @@ -215,7 +272,7 @@ function(gtest_discover_tests_impl) # Create a list of all discovered tests, which users may use to e.g. set # properties on the tests flush_tests_buffer() - add_command(set "" ${_TEST_LIST} "${tests}") + add_command(set "" ${arg_TEST_LIST} "${tests}") # Write CTest script flush_script() @@ -227,7 +284,7 @@ if(CMAKE_SCRIPT_MODE_FILE) NO_PRETTY_TYPES ${NO_PRETTY_TYPES} NO_PRETTY_VALUES ${NO_PRETTY_VALUES} TEST_EXECUTABLE ${TEST_EXECUTABLE} - TEST_EXECUTOR ${TEST_EXECUTOR} + TEST_EXECUTOR "${TEST_EXECUTOR}" TEST_WORKING_DIR ${TEST_WORKING_DIR} TEST_PREFIX ${TEST_PREFIX} TEST_SUFFIX ${TEST_SUFFIX} @@ -236,7 +293,8 @@ if(CMAKE_SCRIPT_MODE_FILE) CTEST_FILE ${CTEST_FILE} TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT} TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR} - TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} - TEST_PROPERTIES ${TEST_PROPERTIES} + TEST_EXTRA_ARGS "${TEST_EXTRA_ARGS}" + TEST_DISCOVERY_EXTRA_ARGS "${TEST_DISCOVERY_EXTRA_ARGS}" + TEST_PROPERTIES "${TEST_PROPERTIES}" ) endif() diff --git a/Modules/Internal/ApplePlatformSelection.cmake.in b/Modules/Internal/ApplePlatformSelection.cmake.in index c07f139a8..e28f6ec30 100644 --- a/Modules/Internal/ApplePlatformSelection.cmake.in +++ b/Modules/Internal/ApplePlatformSelection.cmake.in @@ -27,6 +27,8 @@ elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xrsimulator") @_branch_VISIONOS_SIMULATOR_INCLUDE_FILE@ elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xros") @_branch_VISIONOS_INCLUDE_FILE@ +elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS" AND _CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)macosx") + @_branch_IOS_CATALYST_INCLUDE_FILE@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") @_branch_MACOS_INCLUDE_FILE@ else() diff --git a/Modules/Internal/CMakeASM-ATTLinkerInformation.cmake b/Modules/Internal/CMakeASM-ATTLinkerInformation.cmake new file mode 100644 index 000000000..5e3ae7897 --- /dev/null +++ b/Modules/Internal/CMakeASM-ATTLinkerInformation.cmake @@ -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. + + +# support for AT&T syntax assemblers, e.g. GNU as + +# Load the generic ASMInformation file: +set(ASM_DIALECT "-ATT") +include(Internal/CMakeASMLinkerInformation) +set(ASM_DIALECT) diff --git a/Modules/Internal/CMakeASMLinkerInformation.cmake b/Modules/Internal/CMakeASMLinkerInformation.cmake new file mode 100644 index 000000000..5f81b1feb --- /dev/null +++ b/Modules/Internal/CMakeASMLinkerInformation.cmake @@ -0,0 +1,35 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID) + include(Linker/${CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID}-ASM${ASM_DIALECT} OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID}-ASM${ASM_DIALECT}-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_ASM${ASM_DIALECT}_COMPILER_LINKER_ID}-ASM${ASM_DIALECT} + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-ASM${ASM_DIALECT} OPTIONAL) +endif () + +set(CMAKE_ASM${ASM_DIALECT}_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeASM_MARMASMLinkerInformation.cmake b/Modules/Internal/CMakeASM_MARMASMLinkerInformation.cmake new file mode 100644 index 000000000..62497f2f8 --- /dev/null +++ b/Modules/Internal/CMakeASM_MARMASMLinkerInformation.cmake @@ -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. + + +# support for the MS assembler, masm and masm64 + +# Load the generic ASMInformation file: +set(ASM_DIALECT "_MASM") +include(Internal/CMakeASMLinkerInformation) +set(ASM_DIALECT) diff --git a/Modules/Internal/CMakeASM_MASMLinkerInformation.cmake b/Modules/Internal/CMakeASM_MASMLinkerInformation.cmake new file mode 100644 index 000000000..62497f2f8 --- /dev/null +++ b/Modules/Internal/CMakeASM_MASMLinkerInformation.cmake @@ -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. + + +# support for the MS assembler, masm and masm64 + +# Load the generic ASMInformation file: +set(ASM_DIALECT "_MASM") +include(Internal/CMakeASMLinkerInformation) +set(ASM_DIALECT) diff --git a/Modules/Internal/CMakeASM_NASMLinkerInformation.cmake b/Modules/Internal/CMakeASM_NASMLinkerInformation.cmake new file mode 100644 index 000000000..d48a01860 --- /dev/null +++ b/Modules/Internal/CMakeASM_NASMLinkerInformation.cmake @@ -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. + + +# support for the nasm assembler + +# Load the generic ASMInformation file: +set(ASM_DIALECT "_NASM") +include(Internal/CMakeASMLinkerInformation) +set(ASM_DIALECT) diff --git a/Modules/Internal/CMakeCLinkerInformation.cmake b/Modules/Internal/CMakeCLinkerInformation.cmake new file mode 100644 index 000000000..d918f8488 --- /dev/null +++ b/Modules/Internal/CMakeCLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_C_COMPILER_LINKER_ID) + include(Linker/${CMAKE_C_COMPILER_LINKER_ID}-C OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_C_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_C_COMPILER_LINKER_ID}-C-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_C_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_C_COMPILER_LINKER_ID}-C + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-C OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(C) + +set(CMAKE_C_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeCUDALinkerInformation.cmake b/Modules/Internal/CMakeCUDALinkerInformation.cmake new file mode 100644 index 000000000..4081e50be --- /dev/null +++ b/Modules/Internal/CMakeCUDALinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_CUDA_COMPILER_LINKER_ID) + include(Linker/${CMAKE_CUDA_COMPILER_LINKER_ID}-CUDA OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_CUDA_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_CUDA_COMPILER_LINKER_ID}-CUDA-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_CUDA_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_CUDA_COMPILER_LINKER_ID}-CUDA + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-CUDA OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(CUDA) + +set(CMAKE_CUDA_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeCXXLinkerInformation.cmake b/Modules/Internal/CMakeCXXLinkerInformation.cmake new file mode 100644 index 000000000..3929bbbb0 --- /dev/null +++ b/Modules/Internal/CMakeCXXLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C++ compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_CXX_COMPILER_LINKER_ID) + include(Linker/${CMAKE_CXX_COMPILER_LINKER_ID}-CXX OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_CXX_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_CXX_COMPILER_LINKER_ID}-CXX-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_CXX_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_CXX_COMPILER_LINKER_ID}-CXX + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-CXX OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(CXX) + +set(CMAKE_CXX_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeCommonLinkerInformation.cmake b/Modules/Internal/CMakeCommonLinkerInformation.cmake new file mode 100644 index 000000000..245e068bb --- /dev/null +++ b/Modules/Internal/CMakeCommonLinkerInformation.cmake @@ -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. + + +# This file contains common code blocks used by all the linker information +# files + +macro(_cmake_common_linker_platform_flags lang) + # Define configuration for LINK_WHAT_YOU_USE feature + if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") + if(NOT DEFINED CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG) + set(CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed") + endif() + if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK) + set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r) + endif() + endif() +endmacro () diff --git a/Modules/Internal/CMakeFortranLinkerInformation.cmake b/Modules/Internal/CMakeFortranLinkerInformation.cmake new file mode 100644 index 000000000..a39f3deef --- /dev/null +++ b/Modules/Internal/CMakeFortranLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_Fortran_COMPILER_LINKER_ID) + include(Linker/${CMAKE_Fortran_COMPILER_LINKER_ID}-Fortran OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_Fortran_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_Fortran_COMPILER_LINKER_ID}-Fortran-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_Fortran_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_Fortran_COMPILER_LINKER_ID}-Fortran + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-Fortran OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(Fortran) + +set(CMAKE_Fortran_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeHIPLinkerInformation.cmake b/Modules/Internal/CMakeHIPLinkerInformation.cmake new file mode 100644 index 000000000..d37d66d9e --- /dev/null +++ b/Modules/Internal/CMakeHIPLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_HIP_COMPILER_LINKER_ID) + include(Linker/${CMAKE_HIP_COMPILER_LINKER_ID}-HIP OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_HIP_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_HIP_COMPILER_LINKER_ID}-HIP-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_HIP_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_HIP_COMPILER_LINKER_ID}-HIP + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-HIP OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(HIP) + +set(CMAKE_HIP_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeOBJCLinkerInformation.cmake b/Modules/Internal/CMakeOBJCLinkerInformation.cmake new file mode 100644 index 000000000..ea9288008 --- /dev/null +++ b/Modules/Internal/CMakeOBJCLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the Objective-C compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_OBJC_COMPILER_LINKER_ID) + include(Linker/${CMAKE_OBJC_COMPILER_LINKER_ID}-OBJC OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_OBJC_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_LINKER_ID}-OBJC-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_OBJC_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJC_COMPILER_LINKER_ID}-OBJC + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-OBJC OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(OBJC) + +set(CMAKE_OBJC_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeOBJCXXLinkerInformation.cmake b/Modules/Internal/CMakeOBJCXXLinkerInformation.cmake new file mode 100644 index 000000000..3b51ba77b --- /dev/null +++ b/Modules/Internal/CMakeOBJCXXLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the Objective-C++ compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_OBJCXX_COMPILER_LINKER_ID) + include(Linker/${CMAKE_OBJCXX_COMPILER_LINKER_ID}-C OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_OBJCXX_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_LINKER_ID}-OBJCXX-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_OBJCXX_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_OBJCXX_COMPILER_LINKER_ID}-C + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-OBJCXX OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(OBJCXX) + +set(CMAKE_OBJCXX_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CMakeSwiftLinkerInformation.cmake b/Modules/Internal/CMakeSwiftLinkerInformation.cmake new file mode 100644 index 000000000..6d1881fa1 --- /dev/null +++ b/Modules/Internal/CMakeSwiftLinkerInformation.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + + +# This file sets the basic flags for the linker used by the Swift compiler in CMake. +# It also loads the available platform file for the system-linker +# if it exists. +# It also loads a system - linker - processor (or target hardware) +# specific file, which is mainly useful for crosscompiling and embedded systems. + +include(Internal/CMakeCommonLinkerInformation) + +set(_INCLUDED_FILE 0) + +# Load linker-specific information. +if(CMAKE_Swift_COMPILER_LINKER_ID) + include(Linker/${CMAKE_Swift_COMPILER_LINKER_ID}-Swift OPTIONAL) +endif() + +# load a hardware specific file, mostly useful for embedded compilers +if(CMAKE_SYSTEM_PROCESSOR AND CMAKE_Swift_COMPILER_LINKER_ID) + include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_Swift_COMPILER_LINKER_ID}-Swift-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + + +# load the system- and linker specific files +if(CMAKE_Swift_COMPILER_LINKER_ID) + include(Platform/Linker/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_C_COMPILER_LINKER_ID}-Swift + OPTIONAL RESULT_VARIABLE _INCLUDED_FILE) +endif() + +# We specify the platform linker information in the system file. +if (NOT _INCLUDED_FILE) + include(Platform/Linker/${CMAKE_SYSTEM_NAME}-Swift OPTIONAL) +endif () + +_cmake_common_linker_platform_flags(Swift) + +set(CMAKE_Swift_LINKER_INFORMATION_LOADED 1) diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index d4a47e4e9..0169f61ad 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -238,7 +238,7 @@ function(cpack_deb_prepare_package_vars) endforeach() # Only dynamically linked ELF files are included - # Extract only file name infront of ":" + # Extract only file name in front of ":" foreach(_FILE IN LISTS CPACK_DEB_INSTALL_FILES) if(_FILE MATCHES "ELF.*dynamically linked") string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}") @@ -567,7 +567,7 @@ function(cpack_deb_prepare_package_vars) # if per-component variable, overrides the global CPACK_DEBIAN_PACKAGE_${variable_type_} # automatic dependency discovery will be performed afterwards. if(CPACK_DEB_PACKAGE_COMPONENT) - foreach(value_type_ IN ITEMS DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES SOURCE SECTION PRIORITY NAME) + foreach(value_type_ IN ITEMS DEPENDS RECOMMENDS SUGGESTS PREDEPENDS ENHANCES BREAKS CONFLICTS PROVIDES REPLACES MULTIARCH SOURCE SECTION PRIORITY NAME) set(_component_var "CPACK_DEBIAN_${_local_component_name}_PACKAGE_${value_type_}") # if set, overrides the global variable @@ -589,7 +589,6 @@ function(cpack_deb_prepare_package_vars) list(JOIN COMPONENT_DEPENDS ", " COMPONENT_DEPENDS) if(COMPONENT_DEPENDS) list(PREPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${COMPONENT_DEPENDS}) - list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS) endif() endif() endif() @@ -599,9 +598,9 @@ function(cpack_deb_prepare_package_vars) # Append automatically discovered dependencies . if(CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS) list(APPEND CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}) - list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS) endif() + list(JOIN CPACK_DEBIAN_PACKAGE_DEPENDS ", " CPACK_DEBIAN_PACKAGE_DEPENDS) if(NOT CPACK_DEBIAN_PACKAGE_DEPENDS) message(STATUS "CPACK_DEBIAN_PACKAGE_DEPENDS not set, the package will have no dependencies.") endif() @@ -862,6 +861,7 @@ function(cpack_deb_prepare_package_vars) set(GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS "${CPACK_DEBIAN_PACKAGE_CONFLICTS}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_PROVIDES "${CPACK_DEBIAN_PACKAGE_PROVIDES}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_REPLACES "${CPACK_DEBIAN_PACKAGE_REPLACES}" PARENT_SCOPE) + set(GEN_CPACK_DEBIAN_PACKAGE_MULTIARCH "${CPACK_DEBIAN_PACKAGE_MULTIARCH}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_SHLIBS "${CPACK_DEBIAN_PACKAGE_SHLIBS_LIST}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake index 4a741ee8c..e3f8f5e38 100644 --- a/Modules/Internal/CPack/CPackRPM.cmake +++ b/Modules/Internal/CPack/CPackRPM.cmake @@ -1034,27 +1034,32 @@ function(cpack_rpm_generate_package) # CPACK_RPM_COMPRESSION_TYPE # if (CPACK_RPM_COMPRESSION_TYPE) - if(CPACK_RPM_PACKAGE_DEBUG) - message("CPackRPM:Debug: User Specified RPM compression type: ${CPACK_RPM_COMPRESSION_TYPE}") - endif() - if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "lzma") - set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.lzdio") - endif() - if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "xz") - if(CPACK_THREADS GREATER "0") - set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T${CPACK_THREADS}.xzdio") - else() - set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T.xzdio") - endif() - endif() - if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "bzip2") - set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.bzdio") - endif() - if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "gzip") - set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.gzdio") - endif() + if(CPACK_RPM_PACKAGE_DEBUG) + message("CPackRPM:Debug: User Specified RPM compression type: ${CPACK_RPM_COMPRESSION_TYPE}") + endif() + if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "lzma") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.lzdio") + elseif(CPACK_RPM_COMPRESSION_TYPE STREQUAL "xz") + if(CPACK_THREADS GREATER "0") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T${CPACK_THREADS}.xzdio") + else() + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T.xzdio") + endif() + elseif(CPACK_RPM_COMPRESSION_TYPE STREQUAL "bzip2") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.bzdio") + elseif(CPACK_RPM_COMPRESSION_TYPE STREQUAL "gzip") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.gzdio") + elseif(CPACK_RPM_COMPRESSION_TYPE STREQUAL "zstd") + if(CPACK_THREADS GREATER "0") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w19T${CPACK_THREADS}.zstdio") + else() + set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w19T0.zstdio") + endif() + else() + message(FATAL_ERROR "Specified CPACK_RPM_COMPRESSION_TYPE value is not supported: ${CPACK_RPM_COMPRESSION_TYPE}") + endif() else() - set(CPACK_RPM_COMPRESSION_TYPE_TMP "") + set(CPACK_RPM_COMPRESSION_TYPE_TMP "") endif() if(NOT CPACK_RPM_PACKAGE_SOURCES) diff --git a/Modules/Internal/CPack/ISScript.template.in b/Modules/Internal/CPack/ISScript.template.in index 117105893..db475fe9c 100644 --- a/Modules/Internal/CPack/ISScript.template.in +++ b/Modules/Internal/CPack/ISScript.template.in @@ -1,4 +1,4 @@ -; Script generated by the CPack Inno Setup generator. +; Script generated by the CPack Inno Setup generator. ; All changes made in this file will be lost when CPack is run again. @CPACK_INNOSETUP_INCLUDES_INTERNAL@ diff --git a/Modules/Internal/CheckFlagCommonConfig.cmake b/Modules/Internal/CheckFlagCommonConfig.cmake index 8c5703db1..557e9c746 100644 --- a/Modules/Internal/CheckFlagCommonConfig.cmake +++ b/Modules/Internal/CheckFlagCommonConfig.cmake @@ -27,7 +27,8 @@ macro(CMAKE_CHECK_FLAG_COMMON_INIT _FUNC _LANG _SRC _PATTERNS) FAIL_REGEX "argument unused during compilation: .*") # Clang elseif("${_LANG}" STREQUAL "Fortran") set(${_SRC} " program test\n stop\n end program") - set(${_PATTERNS} FAIL_REGEX "command[ -]line option .* is valid for .* but not for Fortran") + set(${_PATTERNS} FAIL_REGEX "command[ -]line option .* is valid for .* but not for Fortran" + FAIL_REGEX "argument unused during compilation: .*") # LLVMFlang elseif("${_LANG}" STREQUAL "HIP") set(${_SRC} "__host__ int main() { return 0; }") set(${_PATTERNS} FAIL_REGEX "argument unused during compilation: .*") # Clang diff --git a/Modules/Internal/CheckSourceCompiles.cmake b/Modules/Internal/CheckSourceCompiles.cmake index 14a9a61a8..1eadc8057 100644 --- a/Modules/Internal/CheckSourceCompiles.cmake +++ b/Modules/Internal/CheckSourceCompiles.cmake @@ -87,6 +87,13 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var) else() set(CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CSC_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CSC_LINK_DIRECTORIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) set(CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") @@ -105,7 +112,9 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var) ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} "${CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES}" + "${_CSC_LINK_DIRECTORIES}" OUTPUT_VARIABLE OUTPUT) + unset(_CSC_LINK_DIRECTORIES) foreach(_regex ${_FAIL_REGEX}) if("${OUTPUT}" MATCHES "${_regex}") diff --git a/Modules/Internal/CheckSourceRuns.cmake b/Modules/Internal/CheckSourceRuns.cmake index c01081eb9..761598f32 100644 --- a/Modules/Internal/CheckSourceRuns.cmake +++ b/Modules/Internal/CheckSourceRuns.cmake @@ -79,6 +79,12 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var) else() set(CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES) endif() + if(CMAKE_REQUIRED_LINK_DIRECTORIES) + set(_CSR_LINK_DIRECTORIES + "-DLINK_DIRECTORIES:STRING=${CMAKE_REQUIRED_LINK_DIRECTORIES}") + else() + set(_CSR_LINK_DIRECTORIES) + endif() if(CMAKE_REQUIRED_INCLUDES) set(CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") @@ -98,7 +104,9 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var) CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CMAKE_REQUIRED_FLAGS} -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} "${CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES}" + "${_CSR_LINK_DIRECTORIES}" ) + unset(_CSR_LINK_DIRECTORIES) # if it did not compile make the return value fail code of 1 if(NOT ${_var}_COMPILED) set(${_var}_EXITCODE 1) diff --git a/Modules/Linker/AIX-ASM.cmake b/Modules/Linker/AIX-ASM.cmake new file mode 100644 index 000000000..0c748fc40 --- /dev/null +++ b/Modules/Linker/AIX-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/AIX) + +__linker_aix(ASM) diff --git a/Modules/Linker/AIX-C.cmake b/Modules/Linker/AIX-C.cmake new file mode 100644 index 000000000..c1bfd4532 --- /dev/null +++ b/Modules/Linker/AIX-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/AIX) + +__linker_aix(C) diff --git a/Modules/Linker/AIX-CXX.cmake b/Modules/Linker/AIX-CXX.cmake new file mode 100644 index 000000000..34cdf20c8 --- /dev/null +++ b/Modules/Linker/AIX-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/AIX) + +__linker_aix(CXX) diff --git a/Modules/Linker/AIX-Fortran.cmake b/Modules/Linker/AIX-Fortran.cmake new file mode 100644 index 000000000..cf7407618 --- /dev/null +++ b/Modules/Linker/AIX-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/AIX) + +__linker_aix(Fortran) diff --git a/Modules/Linker/AIX.cmake b/Modules/Linker/AIX.cmake new file mode 100644 index 000000000..6639df3ac --- /dev/null +++ b/Modules/Linker/AIX.cmake @@ -0,0 +1,9 @@ +# 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 linkers; use include blocker. +include_guard() + +macro(__linker_aix lang) +endmacro() diff --git a/Modules/Linker/AppleClang-ASM.cmake b/Modules/Linker/AppleClang-ASM.cmake new file mode 100644 index 000000000..9f160d907 --- /dev/null +++ b/Modules/Linker/AppleClang-ASM.cmake @@ -0,0 +1,3 @@ +include(Linker/AppleClang) + +__linker_appleclang(ASM) diff --git a/Modules/Linker/AppleClang-C.cmake b/Modules/Linker/AppleClang-C.cmake new file mode 100644 index 000000000..0a98f2945 --- /dev/null +++ b/Modules/Linker/AppleClang-C.cmake @@ -0,0 +1,3 @@ +include(Linker/AppleClang) + +__linker_appleclang(C) diff --git a/Modules/Linker/AppleClang-CXX.cmake b/Modules/Linker/AppleClang-CXX.cmake new file mode 100644 index 000000000..09d7ab6e2 --- /dev/null +++ b/Modules/Linker/AppleClang-CXX.cmake @@ -0,0 +1,3 @@ +include(Linker/AppleClang) + +__linker_appleclang(CXX) diff --git a/Modules/Linker/AppleClang-OBJC.cmake b/Modules/Linker/AppleClang-OBJC.cmake new file mode 100644 index 000000000..73cd91430 --- /dev/null +++ b/Modules/Linker/AppleClang-OBJC.cmake @@ -0,0 +1,3 @@ +include(Linker/AppleClang) + +__linker_appleclang(OBJC) diff --git a/Modules/Linker/AppleClang-OBJCXX.cmake b/Modules/Linker/AppleClang-OBJCXX.cmake new file mode 100644 index 000000000..211e28a87 --- /dev/null +++ b/Modules/Linker/AppleClang-OBJCXX.cmake @@ -0,0 +1,3 @@ +include(Linker/AppleClang) + +__linker_appleclang(OBJCXX) diff --git a/Modules/Linker/AppleClang.cmake b/Modules/Linker/AppleClang.cmake new file mode 100644 index 000000000..4e4899817 --- /dev/null +++ b/Modules/Linker/AppleClang.cmake @@ -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. + + +# This module is shared by multiple linkers; use include blocker. + +include_guard() + +macro(__linker_appleclang lang) +endmacro() diff --git a/Modules/Linker/GNU-ASM.cmake b/Modules/Linker/GNU-ASM.cmake new file mode 100644 index 000000000..d927106db --- /dev/null +++ b/Modules/Linker/GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(ASM) diff --git a/Modules/Linker/GNU-C.cmake b/Modules/Linker/GNU-C.cmake new file mode 100644 index 000000000..9a19e7d03 --- /dev/null +++ b/Modules/Linker/GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(C) diff --git a/Modules/Linker/GNU-CUDA.cmake b/Modules/Linker/GNU-CUDA.cmake new file mode 100644 index 000000000..82e887fe9 --- /dev/null +++ b/Modules/Linker/GNU-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(CUDA) diff --git a/Modules/Linker/GNU-CXX.cmake b/Modules/Linker/GNU-CXX.cmake new file mode 100644 index 000000000..6b4d71940 --- /dev/null +++ b/Modules/Linker/GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(CXX) diff --git a/Modules/Linker/GNU-Fortran.cmake b/Modules/Linker/GNU-Fortran.cmake new file mode 100644 index 000000000..b4f688aac --- /dev/null +++ b/Modules/Linker/GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(Fortran) diff --git a/Modules/Linker/GNU-HIP.cmake b/Modules/Linker/GNU-HIP.cmake new file mode 100644 index 000000000..b2d0b2e16 --- /dev/null +++ b/Modules/Linker/GNU-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(HIP) diff --git a/Modules/Linker/GNU.cmake b/Modules/Linker/GNU.cmake new file mode 100644 index 000000000..ca3457a96 --- /dev/null +++ b/Modules/Linker/GNU.cmake @@ -0,0 +1,74 @@ +# 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 linkers; use include blocker. +include_guard() + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0140 NEW) + +function(__linker_gnu lang) + # define flags for linker depfile generation + set(CMAKE_${lang}_LINKER_DEPFILE_FLAGS "LINKER:--dependency-file,") + set(CMAKE_${lang}_LINKER_DEPFILE_FORMAT gcc) + + if(NOT CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") + # Only ELF binary format supports this capability + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + + if(NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + ## Ensure ninja tool is recent enough... + if(CMAKE_GENERATOR MATCHES "^Ninja") + # Ninja 1.10 or upper is required + execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" --version + OUTPUT_VARIABLE _ninja_version + ERROR_VARIABLE _ninja_version) + if (_ninja_version MATCHES "[0-9]+(\\.[0-9]+)*") + set (_ninja_version "${CMAKE_MATCH_0}") + endif() + if (_ninja_version VERSION_LESS "1.10") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + endif() + + if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + ## check if this feature is supported by the linker + if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD") + execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help + OUTPUT_VARIABLE _linker_capabilities + ERROR_VARIABLE _linker_capabilities) + if(_linker_capabilities MATCHES "--dependency-file") + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE) + else() + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + else() + set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE) + endif() + endif() + endif() + if (CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED) + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER TRUE) + else() + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) + endif() + + # Due to GNU binutils ld bug when LTO is enabled (see GNU bug + # `30568 `_), + # deactivate this feature if the version is less than 2.41. + if (CMAKE_${lang}_COMPILER_LINKER_ID + AND CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNU" + AND CMAKE_${lang}_COMPILER_LINKER_VERSION VERSION_LESS "2.41") + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) + endif() + + return(PROPAGATE CMAKE_${lang}_LINKER_DEPFILE_FLAGS + CMAKE_${lang}_LINKER_DEPFILE_FORMAT + CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED + CMAKE_${lang}_LINK_DEPENDS_USE_LINKER) +endfunction() + +endblock() diff --git a/Modules/Linker/GNUgold-ASM.cmake b/Modules/Linker/GNUgold-ASM.cmake new file mode 100644 index 000000000..9f920e52c --- /dev/null +++ b/Modules/Linker/GNUgold-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(ASM) diff --git a/Modules/Linker/GNUgold-C.cmake b/Modules/Linker/GNUgold-C.cmake new file mode 100644 index 000000000..a510626bc --- /dev/null +++ b/Modules/Linker/GNUgold-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(C) diff --git a/Modules/Linker/GNUgold-CUDA.cmake b/Modules/Linker/GNUgold-CUDA.cmake new file mode 100644 index 000000000..9cd52e040 --- /dev/null +++ b/Modules/Linker/GNUgold-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(CUDA) diff --git a/Modules/Linker/GNUgold-CXX.cmake b/Modules/Linker/GNUgold-CXX.cmake new file mode 100644 index 000000000..eeb624b0d --- /dev/null +++ b/Modules/Linker/GNUgold-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(CXX) diff --git a/Modules/Linker/GNUgold-Fortran.cmake b/Modules/Linker/GNUgold-Fortran.cmake new file mode 100644 index 000000000..97298eddb --- /dev/null +++ b/Modules/Linker/GNUgold-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(Fortran) diff --git a/Modules/Linker/GNUgold-HIP.cmake b/Modules/Linker/GNUgold-HIP.cmake new file mode 100644 index 000000000..900b9aa05 --- /dev/null +++ b/Modules/Linker/GNUgold-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNUgold) + +__linker_gnugold(HIP) diff --git a/Modules/Linker/GNUgold.cmake b/Modules/Linker/GNUgold.cmake new file mode 100644 index 000000000..468056488 --- /dev/null +++ b/Modules/Linker/GNUgold.cmake @@ -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. + + +# This module is shared by multiple linkers; use include blocker. +include_guard() + +include(Linker/GNU) + +macro(__linker_gnugold lang) + __linker_gnu(${lang}) + + # Due to GNU binutils ld bug when LTO is enabled (see GNU bug + # `30568 `_), + # deactivate this feature because all known versions of gold linker have + # this bug. + set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE) +endmacro() diff --git a/Modules/Linker/LLD-ASM.cmake b/Modules/Linker/LLD-ASM.cmake new file mode 100644 index 000000000..255f8c5cc --- /dev/null +++ b/Modules/Linker/LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(ASM) diff --git a/Modules/Linker/LLD-C.cmake b/Modules/Linker/LLD-C.cmake new file mode 100644 index 000000000..d3036b0d4 --- /dev/null +++ b/Modules/Linker/LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(C) diff --git a/Modules/Linker/LLD-CUDA.cmake b/Modules/Linker/LLD-CUDA.cmake new file mode 100644 index 000000000..aa7241312 --- /dev/null +++ b/Modules/Linker/LLD-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(CUDA) diff --git a/Modules/Linker/LLD-CXX.cmake b/Modules/Linker/LLD-CXX.cmake new file mode 100644 index 000000000..0a0d2de35 --- /dev/null +++ b/Modules/Linker/LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(CXX) diff --git a/Modules/Linker/LLD-Fortran.cmake b/Modules/Linker/LLD-Fortran.cmake new file mode 100644 index 000000000..babcd9a05 --- /dev/null +++ b/Modules/Linker/LLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(Fortran) diff --git a/Modules/Linker/LLD-HIP.cmake b/Modules/Linker/LLD-HIP.cmake new file mode 100644 index 000000000..8535830b3 --- /dev/null +++ b/Modules/Linker/LLD-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(HIP) diff --git a/Modules/Linker/LLD-OBJC.cmake b/Modules/Linker/LLD-OBJC.cmake new file mode 100644 index 000000000..7a2af7670 --- /dev/null +++ b/Modules/Linker/LLD-OBJC.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(OBJC) diff --git a/Modules/Linker/LLD-OBJCXX.cmake b/Modules/Linker/LLD-OBJCXX.cmake new file mode 100644 index 000000000..2361e9037 --- /dev/null +++ b/Modules/Linker/LLD-OBJCXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/LLD) + +__linker_lld(OBJCXX) diff --git a/Modules/Linker/LLD.cmake b/Modules/Linker/LLD.cmake new file mode 100644 index 000000000..6695f983f --- /dev/null +++ b/Modules/Linker/LLD.cmake @@ -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. + +include_guard() + +include(Linker/GNU) + +macro(__linker_lld lang) + __linker_gnu(${lang}) +endmacro() diff --git a/Modules/Linker/MOLD-ASM.cmake b/Modules/Linker/MOLD-ASM.cmake new file mode 100644 index 000000000..d927106db --- /dev/null +++ b/Modules/Linker/MOLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(ASM) diff --git a/Modules/Linker/MOLD-C.cmake b/Modules/Linker/MOLD-C.cmake new file mode 100644 index 000000000..9a19e7d03 --- /dev/null +++ b/Modules/Linker/MOLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(C) diff --git a/Modules/Linker/MOLD-CUDA.cmake b/Modules/Linker/MOLD-CUDA.cmake new file mode 100644 index 000000000..82e887fe9 --- /dev/null +++ b/Modules/Linker/MOLD-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(CUDA) diff --git a/Modules/Linker/MOLD-CXX.cmake b/Modules/Linker/MOLD-CXX.cmake new file mode 100644 index 000000000..6b4d71940 --- /dev/null +++ b/Modules/Linker/MOLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(CXX) diff --git a/Modules/Linker/MOLD-Fortran.cmake b/Modules/Linker/MOLD-Fortran.cmake new file mode 100644 index 000000000..b4f688aac --- /dev/null +++ b/Modules/Linker/MOLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(Fortran) diff --git a/Modules/Linker/MOLD-HIP.cmake b/Modules/Linker/MOLD-HIP.cmake new file mode 100644 index 000000000..b2d0b2e16 --- /dev/null +++ b/Modules/Linker/MOLD-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(HIP) diff --git a/Modules/Linker/MOLD-OBJC.cmake b/Modules/Linker/MOLD-OBJC.cmake new file mode 100644 index 000000000..a3c668d81 --- /dev/null +++ b/Modules/Linker/MOLD-OBJC.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(OBJC) diff --git a/Modules/Linker/MOLD-OBJCXX.cmake b/Modules/Linker/MOLD-OBJCXX.cmake new file mode 100644 index 000000000..0b7300363 --- /dev/null +++ b/Modules/Linker/MOLD-OBJCXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/GNU) + +__linker_gnu(OBJCXX) diff --git a/Modules/Linker/Solaris-ASM.cmake b/Modules/Linker/Solaris-ASM.cmake new file mode 100644 index 000000000..2bcdd71e7 --- /dev/null +++ b/Modules/Linker/Solaris-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/Solaris) + +__linker_solaris(ASM) diff --git a/Modules/Linker/Solaris-C.cmake b/Modules/Linker/Solaris-C.cmake new file mode 100644 index 000000000..23458d532 --- /dev/null +++ b/Modules/Linker/Solaris-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/Solaris) + +__linker_solaris(C) diff --git a/Modules/Linker/Solaris-CXX.cmake b/Modules/Linker/Solaris-CXX.cmake new file mode 100644 index 000000000..8ddf9099b --- /dev/null +++ b/Modules/Linker/Solaris-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/Solaris) + +__linker_solaris(CXX) diff --git a/Modules/Linker/Solaris-Fortran.cmake b/Modules/Linker/Solaris-Fortran.cmake new file mode 100644 index 000000000..111cf11fd --- /dev/null +++ b/Modules/Linker/Solaris-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Linker/Solaris) + +__linker_solaris(Fortran) diff --git a/Modules/Linker/Solaris.cmake b/Modules/Linker/Solaris.cmake new file mode 100644 index 000000000..fbd11996a --- /dev/null +++ b/Modules/Linker/Solaris.cmake @@ -0,0 +1,9 @@ +# 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 linkers; use include blocker. +include_guard() + +macro(__linker_solaris lang) +endmacro() diff --git a/Modules/MacOSXFrameworkInfo.plist.in b/Modules/MacOSXFrameworkInfo.plist.in index 18eaef2f7..f053fda7a 100644 --- a/Modules/MacOSXFrameworkInfo.plist.in +++ b/Modules/MacOSXFrameworkInfo.plist.in @@ -12,6 +12,8 @@ ${MACOSX_FRAMEWORK_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 + CFBundleName + ${MACOSX_FRAMEWORK_BUNDLE_NAME} CFBundlePackageType FMWK CFBundleSignature diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index f6616fb7c..c9f01a6b4 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake @@ -17,7 +17,6 @@ macro(__aix_compiler_gnu lang) set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1) unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) @@ -30,6 +29,14 @@ macro(__aix_compiler_gnu lang) " -Wl,-bE:/exports.exp -o " ) + # Create an archive for shared library if CMAKE_AIX_SHARED_LIBRARY_ARCHIVE is used. + string(REPLACE " -o " " -o " + CMAKE_${lang}_CREATE_SHARED_LIBRARY_ARCHIVE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}") + list(APPEND CMAKE_${lang}_CREATE_SHARED_LIBRARY_ARCHIVE + " -X32_64 rc " + "rm -f " + ) + set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o -c -l . " " -Wl,-bE: -o ") diff --git a/Modules/Platform/AIX-LLVMFlang-Fortran.cmake b/Modules/Platform/AIX-LLVMFlang-Fortran.cmake new file mode 100644 index 000000000..07772a716 --- /dev/null +++ b/Modules/Platform/AIX-LLVMFlang-Fortran.cmake @@ -0,0 +1,2 @@ +include(Platform/AIX-GNU) +__aix_compiler_gnu(Fortran) diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake index cd202cbbd..f7cab254e 100644 --- a/Modules/Platform/AIX-XL.cmake +++ b/Modules/Platform/AIX-XL.cmake @@ -17,7 +17,6 @@ macro(__aix_compiler_xl lang) set(CMAKE_SHARED_MODULE_${lang}_FLAGS " ") set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) set(_OBJECTS " ") if(DEFINED CMAKE_XL_CreateExportList AND CMAKE_XL_CreateExportList STREQUAL "") @@ -34,6 +33,14 @@ macro(__aix_compiler_xl lang) " -Wl,-bE:/exports.exp -o " ) + # Create an archive for shared library if CMAKE_AIX_SHARED_LIBRARY_ARCHIVE is used. + string(REPLACE " -o " " -o " + CMAKE_${lang}_CREATE_SHARED_LIBRARY_ARCHIVE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}") + list(APPEND CMAKE_${lang}_CREATE_SHARED_LIBRARY_ARCHIVE + " -X32_64 rc " + "rm -f " + ) + set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o -c -l . " " -Wl,-bE: -o ") diff --git a/Modules/Platform/AIX.cmake b/Modules/Platform/AIX.cmake index 03cef51af..dcc983725 100644 --- a/Modules/Platform/AIX.cmake +++ b/Modules/Platform/AIX.cmake @@ -1,5 +1,6 @@ set(CMAKE_SHARED_LIBRARY_PREFIX "lib") # lib set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") # .so +set(CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX ".a") # .a set(CMAKE_AIX_IMPORT_FILE_PREFIX "") set(CMAKE_AIX_IMPORT_FILE_SUFFIX ".imp") set(CMAKE_DL_LIBS "-lld") diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake index 342586d4b..bab604298 100644 --- a/Modules/Platform/Android-Determine.cmake +++ b/Modules/Platform/Android-Determine.cmake @@ -533,7 +533,7 @@ elseif(CMAKE_ANDROID_NDK) set(_ANDROID_APIS ${_ANDROID_APIS_1} ${_ANDROID_APIS_2}) unset(_ANDROID_APIS_1) unset(_ANDROID_APIS_2) - if(_ANDROID_APIS STREQUAL "") + if(NOT DEFINED _ANDROID_APIS OR _ANDROID_APIS STREQUAL "") message(FATAL_ERROR "Android: No APIs found in the NDK. No\n" " ${CMAKE_ANDROID_NDK}/platforms/android-*\n" diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake index 40fed4432..83357a7e2 100644 --- a/Modules/Platform/Apple-Clang.cmake +++ b/Modules/Platform/Apple-Clang.cmake @@ -15,8 +15,6 @@ macro(__apple_compiler_clang lang) set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") endif() - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) - set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework ") set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) set(CMAKE_${lang}_LINK_LIBRARY_FRAMEWORK_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) @@ -44,6 +42,8 @@ macro(__apple_compiler_clang lang) set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mwatchos-version-min=") elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/WatchSimulator") set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mwatchos-simulator-version-min=") + elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/MacOSX" AND CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "--target=-apple-ios-macabi") else() set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mmacosx-version-min=") endif() diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake index 01b5f912c..f3729555c 100644 --- a/Modules/Platform/CYGWIN-GNU.cmake +++ b/Modules/Platform/CYGWIN-GNU.cmake @@ -16,33 +16,6 @@ set(CMAKE_GNULD_IMAGE_VERSION set(CMAKE_GENERATOR_RC windres) -# Features for LINK_LIBRARY generator expression -## check linker capabilities -if(NOT DEFINED _CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - execute_process(COMMAND "${CMAKE_LINKER}" --help - OUTPUT_VARIABLE __linker_help - ERROR_VARIABLE __linker_help) - if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") - else() - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") - endif() - unset(__linker_help) -endif() -## WHOLE_ARCHIVE: Force loading all members of an archive -if(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" - "" - "LINKER:--pop-state") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - - macro(__cygwin_compiler_gnu lang) # Binary link rules. set(CMAKE_${lang}_CREATE_SHARED_MODULE diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index b26912d69..f334c5f5c 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -141,11 +141,6 @@ set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,}N set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE) set(CMAKE_LINK_LIBRARY_WEAK_LIBRARY_ATTRIBUTES LIBRARY_TYPE=STATIC,SHARED DEDUPLICATION=DEFAULT OVERRIDE=DEFAULT) -# Defines LINK_LIBRARY feature to Force loading of all members of an archive -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,") -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - # default to searching for frameworks first if(NOT DEFINED CMAKE_FIND_FRAMEWORK) set(CMAKE_FIND_FRAMEWORK FIRST) diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake index 891422eb8..c79c18118 100644 --- a/Modules/Platform/FreeBSD.cmake +++ b/Modules/Platform/FreeBSD.cmake @@ -27,33 +27,6 @@ foreach(type SHARED_LIBRARY SHARED_MODULE EXE) endforeach() -# Features for LINK_LIBRARY generator expression -## check linker capabilities -if(NOT DEFINED _CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - execute_process(COMMAND "${CMAKE_LINKER}" --help - OUTPUT_VARIABLE __linker_help - ERROR_VARIABLE __linker_help) - if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") - else() - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") - endif() - unset(__linker_help) -endif() -## WHOLE_ARCHIVE: Force loading all members of an archive -if(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" - "" - "LINKER:--pop-state") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - - # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is ## no pending undefined symbols diff --git a/Modules/Platform/Linker/AIX-AIX-ASM.cmake b/Modules/Platform/Linker/AIX-AIX-ASM.cmake new file mode 100644 index 000000000..714e6a83b --- /dev/null +++ b/Modules/Platform/Linker/AIX-AIX-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/AIX-AIX) + +__aix_linker_aix(ASM) diff --git a/Modules/Platform/Linker/AIX-AIX-C.cmake b/Modules/Platform/Linker/AIX-AIX-C.cmake new file mode 100644 index 000000000..4cd4b9c5b --- /dev/null +++ b/Modules/Platform/Linker/AIX-AIX-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/AIX-AIX) + +__aix_linker_aix(C) diff --git a/Modules/Platform/Linker/AIX-AIX-CXX.cmake b/Modules/Platform/Linker/AIX-AIX-CXX.cmake new file mode 100644 index 000000000..4c6f917f6 --- /dev/null +++ b/Modules/Platform/Linker/AIX-AIX-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/AIX-AIX) + +__aix_linker_aix(CXX) diff --git a/Modules/Platform/Linker/AIX-AIX-Fortran.cmake b/Modules/Platform/Linker/AIX-AIX-Fortran.cmake new file mode 100644 index 000000000..c7a43c5ca --- /dev/null +++ b/Modules/Platform/Linker/AIX-AIX-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/AIX-AIX) + +__aix_linker_aix(Fortran) diff --git a/Modules/Platform/Linker/AIX-AIX.cmake b/Modules/Platform/Linker/AIX-AIX.cmake new file mode 100644 index 000000000..36d795936 --- /dev/null +++ b/Modules/Platform/Linker/AIX-AIX.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +macro(__aix_linker_aix lang) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/AIX-ASM.cmake b/Modules/Platform/Linker/AIX-ASM.cmake new file mode 100644 index 000000000..6699054fc --- /dev/null +++ b/Modules/Platform/Linker/AIX-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AIX is the default linker +include(Platform/Linker/AIX-AIX-ASM) diff --git a/Modules/Platform/Linker/AIX-C.cmake b/Modules/Platform/Linker/AIX-C.cmake new file mode 100644 index 000000000..5eae8d91a --- /dev/null +++ b/Modules/Platform/Linker/AIX-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AIX is the default linker +include(Platform/Linker/AIX-AIX-C) diff --git a/Modules/Platform/Linker/AIX-CXX.cmake b/Modules/Platform/Linker/AIX-CXX.cmake new file mode 100644 index 000000000..bd770118e --- /dev/null +++ b/Modules/Platform/Linker/AIX-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AIX is the default linker +include(Platform/Linker/AIX-AIX-CXX) diff --git a/Modules/Platform/Linker/AIX-Fortran.cmake b/Modules/Platform/Linker/AIX-Fortran.cmake new file mode 100644 index 000000000..182af9b23 --- /dev/null +++ b/Modules/Platform/Linker/AIX-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AIX is the default linker +include(Platform/Linker/AIX-AIX-Fortran) diff --git a/Modules/Platform/Linker/Apple-ASM.cmake b/Modules/Platform/Linker/Apple-ASM.cmake new file mode 100644 index 000000000..8dc768405 --- /dev/null +++ b/Modules/Platform/Linker/Apple-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-ASM) diff --git a/Modules/Platform/Linker/Apple-AppleClang-ASM.cmake b/Modules/Platform/Linker/Apple-AppleClang-ASM.cmake new file mode 100644 index 000000000..9cf9ff2d6 --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(ASM) diff --git a/Modules/Platform/Linker/Apple-AppleClang-C.cmake b/Modules/Platform/Linker/Apple-AppleClang-C.cmake new file mode 100644 index 000000000..5333074d8 --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(C) diff --git a/Modules/Platform/Linker/Apple-AppleClang-CUDA.cmake b/Modules/Platform/Linker/Apple-AppleClang-CUDA.cmake new file mode 100644 index 000000000..87fa30619 --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(CUDA) diff --git a/Modules/Platform/Linker/Apple-AppleClang-CXX.cmake b/Modules/Platform/Linker/Apple-AppleClang-CXX.cmake new file mode 100644 index 000000000..d414824ff --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(CXX) diff --git a/Modules/Platform/Linker/Apple-AppleClang-Fortran.cmake b/Modules/Platform/Linker/Apple-AppleClang-Fortran.cmake new file mode 100644 index 000000000..5637f0fec --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(Fortran) diff --git a/Modules/Platform/Linker/Apple-AppleClang-OBJC.cmake b/Modules/Platform/Linker/Apple-AppleClang-OBJC.cmake new file mode 100644 index 000000000..4e39bddb0 --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-OBJC.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(OBJC) diff --git a/Modules/Platform/Linker/Apple-AppleClang-OBJCXX.cmake b/Modules/Platform/Linker/Apple-AppleClang-OBJCXX.cmake new file mode 100644 index 000000000..27791970a --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang-OBJCXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang) + +__apple_linker_appleclang(OBJCXX) diff --git a/Modules/Platform/Linker/Apple-AppleClang.cmake b/Modules/Platform/Linker/Apple-AppleClang.cmake new file mode 100644 index 000000000..7582ae9e5 --- /dev/null +++ b/Modules/Platform/Linker/Apple-AppleClang.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + +macro(__apple_linker_appleclang lang) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) + + # Features for LINK_LIBRARY generator expression + ## WHOLE_ARCHIVE: Force loading all members of an archive + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,") + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) +endmacro() diff --git a/Modules/Platform/Linker/Apple-C.cmake b/Modules/Platform/Linker/Apple-C.cmake new file mode 100644 index 000000000..02a1da90e --- /dev/null +++ b/Modules/Platform/Linker/Apple-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-C) diff --git a/Modules/Platform/Linker/Apple-CUDA.cmake b/Modules/Platform/Linker/Apple-CUDA.cmake new file mode 100644 index 000000000..27b466d98 --- /dev/null +++ b/Modules/Platform/Linker/Apple-CUDA.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-CUDA) diff --git a/Modules/Platform/Linker/Apple-CXX.cmake b/Modules/Platform/Linker/Apple-CXX.cmake new file mode 100644 index 000000000..b2bc90b3d --- /dev/null +++ b/Modules/Platform/Linker/Apple-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-CXX) diff --git a/Modules/Platform/Linker/Apple-Fortran.cmake b/Modules/Platform/Linker/Apple-Fortran.cmake new file mode 100644 index 000000000..a39e7aee3 --- /dev/null +++ b/Modules/Platform/Linker/Apple-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-Fortran) diff --git a/Modules/Platform/Linker/Apple-LLD-C.cmake b/Modules/Platform/Linker/Apple-LLD-C.cmake new file mode 100644 index 000000000..e38b1b7e8 --- /dev/null +++ b/Modules/Platform/Linker/Apple-LLD-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-C) diff --git a/Modules/Platform/Linker/Apple-LLD-CXX.cmake b/Modules/Platform/Linker/Apple-LLD-CXX.cmake new file mode 100644 index 000000000..4e4a9cad1 --- /dev/null +++ b/Modules/Platform/Linker/Apple-LLD-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-CXX) diff --git a/Modules/Platform/Linker/Apple-LLD-OBJC.cmake b/Modules/Platform/Linker/Apple-LLD-OBJC.cmake new file mode 100644 index 000000000..ff18785a1 --- /dev/null +++ b/Modules/Platform/Linker/Apple-LLD-OBJC.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-OBJC) diff --git a/Modules/Platform/Linker/Apple-LLD-OBJCXX.cmake b/Modules/Platform/Linker/Apple-LLD-OBJCXX.cmake new file mode 100644 index 000000000..ba9289025 --- /dev/null +++ b/Modules/Platform/Linker/Apple-LLD-OBJCXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-OBJCXX) diff --git a/Modules/Platform/Linker/Apple-MOLD-C.cmake b/Modules/Platform/Linker/Apple-MOLD-C.cmake new file mode 100644 index 000000000..e38b1b7e8 --- /dev/null +++ b/Modules/Platform/Linker/Apple-MOLD-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-C) diff --git a/Modules/Platform/Linker/Apple-MOLD-CXX.cmake b/Modules/Platform/Linker/Apple-MOLD-CXX.cmake new file mode 100644 index 000000000..4e4a9cad1 --- /dev/null +++ b/Modules/Platform/Linker/Apple-MOLD-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-CXX) diff --git a/Modules/Platform/Linker/Apple-MOLD-OBJC.cmake b/Modules/Platform/Linker/Apple-MOLD-OBJC.cmake new file mode 100644 index 000000000..ff18785a1 --- /dev/null +++ b/Modules/Platform/Linker/Apple-MOLD-OBJC.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-OBJC) diff --git a/Modules/Platform/Linker/Apple-MOLD-OBJCXX.cmake b/Modules/Platform/Linker/Apple-MOLD-OBJCXX.cmake new file mode 100644 index 000000000..ba9289025 --- /dev/null +++ b/Modules/Platform/Linker/Apple-MOLD-OBJCXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Apple-AppleClang-OBJCXX) diff --git a/Modules/Platform/Linker/Apple-OBJC.cmake b/Modules/Platform/Linker/Apple-OBJC.cmake new file mode 100644 index 000000000..316d25313 --- /dev/null +++ b/Modules/Platform/Linker/Apple-OBJC.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-OBJC) diff --git a/Modules/Platform/Linker/Apple-OBJCXX.cmake b/Modules/Platform/Linker/Apple-OBJCXX.cmake new file mode 100644 index 000000000..23db50fee --- /dev/null +++ b/Modules/Platform/Linker/Apple-OBJCXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# AppleClang is the default linker +include(Platform/Linker/Apple-AppleClang-OBJCXX) diff --git a/Modules/Platform/Linker/BSD-Linker-Initialize.cmake b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake new file mode 100644 index 000000000..3de1dabb6 --- /dev/null +++ b/Modules/Platform/Linker/BSD-Linker-Initialize.cmake @@ -0,0 +1,22 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(NOT _CMAKE_SYSTEM_LINKER_TYPE) + block(SCOPE_FOR VARIABLES) + execute_process(COMMAND "${CMAKE_LINKER}" --version + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE output) + if(result OR NOT output MATCHES "LLD") + # assume GNU as default linker + set(_CMAKE_SYSTEM_LINKER_TYPE GNU CACHE INTERNAL "System linker type") + else() + set(_CMAKE_SYSTEM_LINKER_TYPE LLD CACHE INTERNAL "System linker type") + endif() + endblock() +endif() + +endblock() diff --git a/Modules/Platform/Linker/CYGWIN-ASM.cmake b/Modules/Platform/Linker/CYGWIN-ASM.cmake new file mode 100644 index 000000000..ab95f4ceb --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/CYGWIN-GNU-ASM) diff --git a/Modules/Platform/Linker/CYGWIN-C.cmake b/Modules/Platform/Linker/CYGWIN-C.cmake new file mode 100644 index 000000000..703624df3 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/CYGWIN-GNU-C) diff --git a/Modules/Platform/Linker/CYGWIN-CXX.cmake b/Modules/Platform/Linker/CYGWIN-CXX.cmake new file mode 100644 index 000000000..783718804 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/CYGWIN-GNU-CXX) diff --git a/Modules/Platform/Linker/CYGWIN-Fortran.cmake b/Modules/Platform/Linker/CYGWIN-Fortran.cmake new file mode 100644 index 000000000..18ce085f4 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/CYGWIN-GNU-Fortran) diff --git a/Modules/Platform/Linker/CYGWIN-GNU-ASM.cmake b/Modules/Platform/Linker/CYGWIN-GNU-ASM.cmake new file mode 100644 index 000000000..e65d0a07c --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU) + +__cygwin_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/CYGWIN-GNU-C.cmake b/Modules/Platform/Linker/CYGWIN-GNU-C.cmake new file mode 100644 index 000000000..dc0a678d5 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU) + +__cygwin_linker_gnu(C) diff --git a/Modules/Platform/Linker/CYGWIN-GNU-CXX.cmake b/Modules/Platform/Linker/CYGWIN-GNU-CXX.cmake new file mode 100644 index 000000000..a45b3cd6f --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU) + +__cygwin_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/CYGWIN-GNU-Fortran.cmake b/Modules/Platform/Linker/CYGWIN-GNU-Fortran.cmake new file mode 100644 index 000000000..7eba12a58 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU) + +__cygwin_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/CYGWIN-GNU.cmake b/Modules/Platform/Linker/CYGWIN-GNU.cmake new file mode 100644 index 000000000..8e5b97093 --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__cygwin_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/CYGWIN-LLD-ASM.cmake b/Modules/Platform/Linker/CYGWIN-LLD-ASM.cmake new file mode 100644 index 000000000..1c65875fe --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD) + +__cygwin_linker_lld(ASM) diff --git a/Modules/Platform/Linker/CYGWIN-LLD-C.cmake b/Modules/Platform/Linker/CYGWIN-LLD-C.cmake new file mode 100644 index 000000000..a4f3063bb --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD) + +__cygwin_linker_lld(C) diff --git a/Modules/Platform/Linker/CYGWIN-LLD-CXX.cmake b/Modules/Platform/Linker/CYGWIN-LLD-CXX.cmake new file mode 100644 index 000000000..5bd50632a --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD) + +__cygwin_linker_lld(CXX) diff --git a/Modules/Platform/Linker/CYGWIN-LLD-Fortran.cmake b/Modules/Platform/Linker/CYGWIN-LLD-Fortran.cmake new file mode 100644 index 000000000..3034b186d --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-LLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD) + +__cygwin_linker_lld(Fortran) diff --git a/Modules/Platform/Linker/CYGWIN-LLD.cmake b/Modules/Platform/Linker/CYGWIN-LLD.cmake new file mode 100644 index 000000000..39a3c594a --- /dev/null +++ b/Modules/Platform/Linker/CYGWIN-LLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/CYGWIN-GNU) + + +macro(__cygwin_linker_lld lang) + __cygwin_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/DragonFly-ASM.cmake b/Modules/Platform/Linker/DragonFly-ASM.cmake new file mode 100644 index 000000000..b0464889c --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-ASM) diff --git a/Modules/Platform/Linker/DragonFly-C.cmake b/Modules/Platform/Linker/DragonFly-C.cmake new file mode 100644 index 000000000..6fedac2bf --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-C) diff --git a/Modules/Platform/Linker/DragonFly-CXX.cmake b/Modules/Platform/Linker/DragonFly-CXX.cmake new file mode 100644 index 000000000..8064bf499 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-CXX) diff --git a/Modules/Platform/Linker/DragonFly-Fortran.cmake b/Modules/Platform/Linker/DragonFly-Fortran.cmake new file mode 100644 index 000000000..64ea90478 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-Fortran) diff --git a/Modules/Platform/Linker/DragonFly-GNU-ASM.cmake b/Modules/Platform/Linker/DragonFly-GNU-ASM.cmake new file mode 100644 index 000000000..e643a9715 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-GNU-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU-ASM) diff --git a/Modules/Platform/Linker/DragonFly-GNU-C.cmake b/Modules/Platform/Linker/DragonFly-GNU-C.cmake new file mode 100644 index 000000000..729e68d42 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-GNU-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU-C) diff --git a/Modules/Platform/Linker/DragonFly-GNU-CXX.cmake b/Modules/Platform/Linker/DragonFly-GNU-CXX.cmake new file mode 100644 index 000000000..d9907ae20 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-GNU-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU-CXX) diff --git a/Modules/Platform/Linker/DragonFly-GNU-Fortran.cmake b/Modules/Platform/Linker/DragonFly-GNU-Fortran.cmake new file mode 100644 index 000000000..f696be9ce --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-GNU-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU-Fortran) diff --git a/Modules/Platform/Linker/DragonFly-LLD-ASM.cmake b/Modules/Platform/Linker/DragonFly-LLD-ASM.cmake new file mode 100644 index 000000000..d4774cd2b --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-LLD-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD-ASM) diff --git a/Modules/Platform/Linker/DragonFly-LLD-C.cmake b/Modules/Platform/Linker/DragonFly-LLD-C.cmake new file mode 100644 index 000000000..5ec3b76de --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-LLD-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD-C) diff --git a/Modules/Platform/Linker/DragonFly-LLD-CXX.cmake b/Modules/Platform/Linker/DragonFly-LLD-CXX.cmake new file mode 100644 index 000000000..11ab59e34 --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-LLD-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD-CXX) diff --git a/Modules/Platform/Linker/DragonFly-LLD-Fortran.cmake b/Modules/Platform/Linker/DragonFly-LLD-Fortran.cmake new file mode 100644 index 000000000..a2b51410c --- /dev/null +++ b/Modules/Platform/Linker/DragonFly-LLD-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD-Fortran) diff --git a/Modules/Platform/Linker/FreeBSD-ASM.cmake b/Modules/Platform/Linker/FreeBSD-ASM.cmake new file mode 100644 index 000000000..d8c8c863d --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-ASM.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/FreeBSD-GNU-ASM) +else() + include(Platform/Linker/FreeBSD-LLD-ASM) +endif() + +endblock() diff --git a/Modules/Platform/Linker/FreeBSD-C.cmake b/Modules/Platform/Linker/FreeBSD-C.cmake new file mode 100644 index 000000000..09975ad50 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-C.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/FreeBSD-GNU-C) +else() + include(Platform/Linker/FreeBSD-LLD-C) +endif() + +endblock() diff --git a/Modules/Platform/Linker/FreeBSD-CXX.cmake b/Modules/Platform/Linker/FreeBSD-CXX.cmake new file mode 100644 index 000000000..9dcb17a61 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-CXX.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/FreeBSD-GNU-CXX) +else() + include(Platform/Linker/FreeBSD-LLD-CXX) +endif() + +endblock() diff --git a/Modules/Platform/Linker/FreeBSD-Fortran.cmake b/Modules/Platform/Linker/FreeBSD-Fortran.cmake new file mode 100644 index 000000000..793e0a6a0 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-Fortran.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/FreeBSD-GNU-Fortran) +else() + include(Platform/Linker/FreeBSD-LLD-Fortran) +endif() + +endblock() diff --git a/Modules/Platform/Linker/FreeBSD-GNU-ASM.cmake b/Modules/Platform/Linker/FreeBSD-GNU-ASM.cmake new file mode 100644 index 000000000..5ff8ed8ae --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU) + +__freebsd_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/FreeBSD-GNU-C.cmake b/Modules/Platform/Linker/FreeBSD-GNU-C.cmake new file mode 100644 index 000000000..8b6f0fd46 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU) + +__freebsd_linker_gnu(C) diff --git a/Modules/Platform/Linker/FreeBSD-GNU-CXX.cmake b/Modules/Platform/Linker/FreeBSD-GNU-CXX.cmake new file mode 100644 index 000000000..ecc553364 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU) + +__freebsd_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/FreeBSD-GNU-Fortran.cmake b/Modules/Platform/Linker/FreeBSD-GNU-Fortran.cmake new file mode 100644 index 000000000..0e2043719 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-GNU) + +__freebsd_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/FreeBSD-GNU.cmake b/Modules/Platform/Linker/FreeBSD-GNU.cmake new file mode 100644 index 000000000..f88485702 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__freebsd_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/FreeBSD-LLD-ASM.cmake b/Modules/Platform/Linker/FreeBSD-LLD-ASM.cmake new file mode 100644 index 000000000..0b362988f --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD) + +__freebsd_linker_lld(ASM) diff --git a/Modules/Platform/Linker/FreeBSD-LLD-C.cmake b/Modules/Platform/Linker/FreeBSD-LLD-C.cmake new file mode 100644 index 000000000..0120daca7 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD) + +__freebsd_linker_lld(C) diff --git a/Modules/Platform/Linker/FreeBSD-LLD-CXX.cmake b/Modules/Platform/Linker/FreeBSD-LLD-CXX.cmake new file mode 100644 index 000000000..0d8aea867 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD) + +__freebsd_linker_lld(CXX) diff --git a/Modules/Platform/Linker/FreeBSD-LLD-Fortran.cmake b/Modules/Platform/Linker/FreeBSD-LLD-Fortran.cmake new file mode 100644 index 000000000..fa0991d14 --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-LLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/FreeBSD-LLD) + +__freebsd_linker_lld(Fortran) diff --git a/Modules/Platform/Linker/FreeBSD-LLD.cmake b/Modules/Platform/Linker/FreeBSD-LLD.cmake new file mode 100644 index 000000000..133f80c5d --- /dev/null +++ b/Modules/Platform/Linker/FreeBSD-LLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/FreeBSD-GNU) + + +macro(__freebsd_linker_lld lang) + __freebsd_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/GNU-ASM.cmake b/Modules/Platform/Linker/GNU-ASM.cmake new file mode 100644 index 000000000..e78201cae --- /dev/null +++ b/Modules/Platform/Linker/GNU-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/GNU-GNU-ASM) diff --git a/Modules/Platform/Linker/GNU-C.cmake b/Modules/Platform/Linker/GNU-C.cmake new file mode 100644 index 000000000..76679409b --- /dev/null +++ b/Modules/Platform/Linker/GNU-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/GNU-GNU-C) diff --git a/Modules/Platform/Linker/GNU-CXX.cmake b/Modules/Platform/Linker/GNU-CXX.cmake new file mode 100644 index 000000000..d653f34e0 --- /dev/null +++ b/Modules/Platform/Linker/GNU-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/GNU-GNU-CXX) diff --git a/Modules/Platform/Linker/GNU-Fortran.cmake b/Modules/Platform/Linker/GNU-Fortran.cmake new file mode 100644 index 000000000..99bcb76f1 --- /dev/null +++ b/Modules/Platform/Linker/GNU-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/GNU-GNU-Fortran) diff --git a/Modules/Platform/Linker/GNU-GNU-ASM.cmake b/Modules/Platform/Linker/GNU-GNU-ASM.cmake new file mode 100644 index 000000000..39316b9d9 --- /dev/null +++ b/Modules/Platform/Linker/GNU-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/GNU-GNU) + +__gnu_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/GNU-GNU-C.cmake b/Modules/Platform/Linker/GNU-GNU-C.cmake new file mode 100644 index 000000000..12a9883c8 --- /dev/null +++ b/Modules/Platform/Linker/GNU-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/GNU-GNU) + +__gnu_linker_gnu(C) diff --git a/Modules/Platform/Linker/GNU-GNU-CXX.cmake b/Modules/Platform/Linker/GNU-GNU-CXX.cmake new file mode 100644 index 000000000..776f604d7 --- /dev/null +++ b/Modules/Platform/Linker/GNU-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/GNU-GNU) + +__gnu_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/GNU-GNU-Fortran.cmake b/Modules/Platform/Linker/GNU-GNU-Fortran.cmake new file mode 100644 index 000000000..87b5c9b0c --- /dev/null +++ b/Modules/Platform/Linker/GNU-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/GNU-GNU) + +__gnu_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/GNU-GNU.cmake b/Modules/Platform/Linker/GNU-GNU.cmake new file mode 100644 index 000000000..a1adf14a6 --- /dev/null +++ b/Modules/Platform/Linker/GNU-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__gnu_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/GNU.cmake b/Modules/Platform/Linker/GNU.cmake new file mode 100644 index 000000000..5f1167418 --- /dev/null +++ b/Modules/Platform/Linker/GNU.cmake @@ -0,0 +1,51 @@ +# 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. +include_guard() + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +# WHOLE_ARCHIVE Feature for LINK_LIBRARY generator expression +## check linker capabilities +function(__cmake_set_whole_archive_feature __linker) + unset(__lang) + if(ARGC EQUAL "2") + set(__lang "${ARGV1}_") + endif() + + if(NOT __linker) + set(_CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") + endif() + + if(NOT DEFINED _CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED) + execute_process(COMMAND "${__linker}" --help + OUTPUT_VARIABLE __linker_help + ERROR_VARIABLE __linker_help) + if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") + set(_CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") + else() + set(_CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") + endif() + endif() + ## WHOLE_ARCHIVE: Force loading all members of an archive + if(_CMAKE_${__lang}LINKER_PUSHPOP_STATE_SUPPORTED) + set(CMAKE_${__lang}LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" + "" + "LINKER:--pop-state" PARENT_SCOPE) + else() + set(CMAKE_${__lang}LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" + "" + "LINKER:--no-whole-archive" PARENT_SCOPE) + endif() + set(CMAKE_${__lang}LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE PARENT_SCOPE) + set(CMAKE_${__lang}LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT PARENT_SCOPE) +endfunction() + + +## Configure system linker +__cmake_set_whole_archive_feature("${CMAKE_LINKER}") + +endblock() diff --git a/Modules/Platform/Linker/Linux-ASM.cmake b/Modules/Platform/Linker/Linux-ASM.cmake new file mode 100644 index 000000000..67e8e79de --- /dev/null +++ b/Modules/Platform/Linker/Linux-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-ASM) diff --git a/Modules/Platform/Linker/Linux-C.cmake b/Modules/Platform/Linker/Linux-C.cmake new file mode 100644 index 000000000..f40c631db --- /dev/null +++ b/Modules/Platform/Linker/Linux-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-C) diff --git a/Modules/Platform/Linker/Linux-CUDA.cmake b/Modules/Platform/Linker/Linux-CUDA.cmake new file mode 100644 index 000000000..191c347ad --- /dev/null +++ b/Modules/Platform/Linker/Linux-CUDA.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-CUDA) diff --git a/Modules/Platform/Linker/Linux-CXX.cmake b/Modules/Platform/Linker/Linux-CXX.cmake new file mode 100644 index 000000000..638f3c705 --- /dev/null +++ b/Modules/Platform/Linker/Linux-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-CXX) diff --git a/Modules/Platform/Linker/Linux-Fortran.cmake b/Modules/Platform/Linker/Linux-Fortran.cmake new file mode 100644 index 000000000..6bfcc690a --- /dev/null +++ b/Modules/Platform/Linker/Linux-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-Fortran) diff --git a/Modules/Platform/Linker/Linux-GNU-ASM.cmake b/Modules/Platform/Linker/Linux-GNU-ASM.cmake new file mode 100644 index 000000000..5812c0832 --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/Linux-GNU-C.cmake b/Modules/Platform/Linker/Linux-GNU-C.cmake new file mode 100644 index 000000000..5bb322466 --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(C) diff --git a/Modules/Platform/Linker/Linux-GNU-CUDA.cmake b/Modules/Platform/Linker/Linux-GNU-CUDA.cmake new file mode 100644 index 000000000..5a3faa4b3 --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(CUDA) diff --git a/Modules/Platform/Linker/Linux-GNU-CXX.cmake b/Modules/Platform/Linker/Linux-GNU-CXX.cmake new file mode 100644 index 000000000..8b0afa70e --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/Linux-GNU-Fortran.cmake b/Modules/Platform/Linker/Linux-GNU-Fortran.cmake new file mode 100644 index 000000000..b7f8a16a7 --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/Linux-GNU-HIP.cmake b/Modules/Platform/Linker/Linux-GNU-HIP.cmake new file mode 100644 index 000000000..6fb9a06ac --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU) + +__linux_linker_gnu(HIP) diff --git a/Modules/Platform/Linker/Linux-GNU.cmake b/Modules/Platform/Linker/Linux-GNU.cmake new file mode 100644 index 000000000..6668141ae --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__linux_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/Linux-GNUgold-ASM.cmake b/Modules/Platform/Linker/Linux-GNUgold-ASM.cmake new file mode 100644 index 000000000..bfef8b9f7 --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-ASM) diff --git a/Modules/Platform/Linker/Linux-GNUgold-C.cmake b/Modules/Platform/Linker/Linux-GNUgold-C.cmake new file mode 100644 index 000000000..2d0efda7e --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-C) diff --git a/Modules/Platform/Linker/Linux-GNUgold-CUDA.cmake b/Modules/Platform/Linker/Linux-GNUgold-CUDA.cmake new file mode 100644 index 000000000..d67dc943c --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-CUDA.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-CUDA) diff --git a/Modules/Platform/Linker/Linux-GNUgold-CXX.cmake b/Modules/Platform/Linker/Linux-GNUgold-CXX.cmake new file mode 100644 index 000000000..33c46d53a --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-CXX) diff --git a/Modules/Platform/Linker/Linux-GNUgold-Fortran.cmake b/Modules/Platform/Linker/Linux-GNUgold-Fortran.cmake new file mode 100644 index 000000000..a01acda3a --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-Fortran) diff --git a/Modules/Platform/Linker/Linux-GNUgold-HIP.cmake b/Modules/Platform/Linker/Linux-GNUgold-HIP.cmake new file mode 100644 index 000000000..6d1e4923f --- /dev/null +++ b/Modules/Platform/Linker/Linux-GNUgold-HIP.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-GNU-HIP) diff --git a/Modules/Platform/Linker/Linux-HIP.cmake b/Modules/Platform/Linker/Linux-HIP.cmake new file mode 100644 index 000000000..4c94abc39 --- /dev/null +++ b/Modules/Platform/Linker/Linux-HIP.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/Linux-GNU-HIP) diff --git a/Modules/Platform/Linker/Linux-LLD-ASM.cmake b/Modules/Platform/Linker/Linux-LLD-ASM.cmake new file mode 100644 index 000000000..7196133c2 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(ASM) diff --git a/Modules/Platform/Linker/Linux-LLD-C.cmake b/Modules/Platform/Linker/Linux-LLD-C.cmake new file mode 100644 index 000000000..f8a409ee3 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(C) diff --git a/Modules/Platform/Linker/Linux-LLD-CUDA.cmake b/Modules/Platform/Linker/Linux-LLD-CUDA.cmake new file mode 100644 index 000000000..161c7bfba --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(CUDA) diff --git a/Modules/Platform/Linker/Linux-LLD-CXX.cmake b/Modules/Platform/Linker/Linux-LLD-CXX.cmake new file mode 100644 index 000000000..8530a3898 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(CXX) diff --git a/Modules/Platform/Linker/Linux-LLD-Fortran.cmake b/Modules/Platform/Linker/Linux-LLD-Fortran.cmake new file mode 100644 index 000000000..70cd3b474 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(Fortran) diff --git a/Modules/Platform/Linker/Linux-LLD-HIP.cmake b/Modules/Platform/Linker/Linux-LLD-HIP.cmake new file mode 100644 index 000000000..ee0849593 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-LLD) + +__linux_linker_lld(HIP) diff --git a/Modules/Platform/Linker/Linux-LLD.cmake b/Modules/Platform/Linker/Linux-LLD.cmake new file mode 100644 index 000000000..afe5d1c77 --- /dev/null +++ b/Modules/Platform/Linker/Linux-LLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/Linux-GNU) + + +macro(__linux_linker_lld lang) + __linux_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/Linux-MOLD-ASM.cmake b/Modules/Platform/Linker/Linux-MOLD-ASM.cmake new file mode 100644 index 000000000..5cc7a7163 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(ASM) diff --git a/Modules/Platform/Linker/Linux-MOLD-C.cmake b/Modules/Platform/Linker/Linux-MOLD-C.cmake new file mode 100644 index 000000000..a62c2c9fe --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(C) diff --git a/Modules/Platform/Linker/Linux-MOLD-CUDA.cmake b/Modules/Platform/Linker/Linux-MOLD-CUDA.cmake new file mode 100644 index 000000000..524803618 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(CUDA) diff --git a/Modules/Platform/Linker/Linux-MOLD-CXX.cmake b/Modules/Platform/Linker/Linux-MOLD-CXX.cmake new file mode 100644 index 000000000..4c4362484 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(CXX) diff --git a/Modules/Platform/Linker/Linux-MOLD-Fortran.cmake b/Modules/Platform/Linker/Linux-MOLD-Fortran.cmake new file mode 100644 index 000000000..e26bd5db1 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(Fortran) diff --git a/Modules/Platform/Linker/Linux-MOLD-HIP.cmake b/Modules/Platform/Linker/Linux-MOLD-HIP.cmake new file mode 100644 index 000000000..5dd6c1615 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Linux-MOLD) + +__linux_linker_mold(HIP) diff --git a/Modules/Platform/Linker/Linux-MOLD.cmake b/Modules/Platform/Linker/Linux-MOLD.cmake new file mode 100644 index 000000000..672e699d0 --- /dev/null +++ b/Modules/Platform/Linker/Linux-MOLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/Linux-GNU) + + +macro(__linux_linker_mold lang) + __linux_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/MSYS-ASM.cmake b/Modules/Platform/Linker/MSYS-ASM.cmake new file mode 100644 index 000000000..9f72f5f35 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-ASM) diff --git a/Modules/Platform/Linker/MSYS-C.cmake b/Modules/Platform/Linker/MSYS-C.cmake new file mode 100644 index 000000000..5ea9f31c7 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-C) diff --git a/Modules/Platform/Linker/MSYS-CXX.cmake b/Modules/Platform/Linker/MSYS-CXX.cmake new file mode 100644 index 000000000..a64b5385f --- /dev/null +++ b/Modules/Platform/Linker/MSYS-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-CXX) diff --git a/Modules/Platform/Linker/MSYS-Fortran.cmake b/Modules/Platform/Linker/MSYS-Fortran.cmake new file mode 100644 index 000000000..390063d4c --- /dev/null +++ b/Modules/Platform/Linker/MSYS-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-Fortran) diff --git a/Modules/Platform/Linker/MSYS-GNU-ASM.cmake b/Modules/Platform/Linker/MSYS-GNU-ASM.cmake new file mode 100644 index 000000000..f85298e39 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-GNU-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU-ASM) diff --git a/Modules/Platform/Linker/MSYS-GNU-C.cmake b/Modules/Platform/Linker/MSYS-GNU-C.cmake new file mode 100644 index 000000000..4e6415fe7 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-GNU-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU-C) diff --git a/Modules/Platform/Linker/MSYS-GNU-CXX.cmake b/Modules/Platform/Linker/MSYS-GNU-CXX.cmake new file mode 100644 index 000000000..2a48ddca8 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-GNU-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU-CXX) diff --git a/Modules/Platform/Linker/MSYS-GNU-Fortran.cmake b/Modules/Platform/Linker/MSYS-GNU-Fortran.cmake new file mode 100644 index 000000000..5055d22df --- /dev/null +++ b/Modules/Platform/Linker/MSYS-GNU-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-GNU-Fortran) diff --git a/Modules/Platform/Linker/MSYS-LLD-ASM.cmake b/Modules/Platform/Linker/MSYS-LLD-ASM.cmake new file mode 100644 index 000000000..907d08404 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-LLD-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD-ASM) diff --git a/Modules/Platform/Linker/MSYS-LLD-C.cmake b/Modules/Platform/Linker/MSYS-LLD-C.cmake new file mode 100644 index 000000000..bef158d05 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-LLD-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD-C) diff --git a/Modules/Platform/Linker/MSYS-LLD-CXX.cmake b/Modules/Platform/Linker/MSYS-LLD-CXX.cmake new file mode 100644 index 000000000..59d9abc38 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-LLD-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD-CXX) diff --git a/Modules/Platform/Linker/MSYS-LLD-Fortran.cmake b/Modules/Platform/Linker/MSYS-LLD-Fortran.cmake new file mode 100644 index 000000000..f705b91d7 --- /dev/null +++ b/Modules/Platform/Linker/MSYS-LLD-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/CYGWIN-LLD-Fortran) diff --git a/Modules/Platform/Linker/MirBSD-ASM.cmake b/Modules/Platform/Linker/MirBSD-ASM.cmake new file mode 100644 index 000000000..d2c419e5c --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-ASM) diff --git a/Modules/Platform/Linker/MirBSD-C.cmake b/Modules/Platform/Linker/MirBSD-C.cmake new file mode 100644 index 000000000..c59f2511a --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-C) diff --git a/Modules/Platform/Linker/MirBSD-CXX.cmake b/Modules/Platform/Linker/MirBSD-CXX.cmake new file mode 100644 index 000000000..9a76d37f2 --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-CXX) diff --git a/Modules/Platform/Linker/MirBSD-Fortran.cmake b/Modules/Platform/Linker/MirBSD-Fortran.cmake new file mode 100644 index 000000000..9ab988635 --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-Fortran) diff --git a/Modules/Platform/Linker/MirBSD-GNU-ASM.cmake b/Modules/Platform/Linker/MirBSD-GNU-ASM.cmake new file mode 100644 index 000000000..f552a9409 --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-GNU-ASM.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU-ASM) diff --git a/Modules/Platform/Linker/MirBSD-GNU-C.cmake b/Modules/Platform/Linker/MirBSD-GNU-C.cmake new file mode 100644 index 000000000..ad337d073 --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-GNU-C.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU-C) diff --git a/Modules/Platform/Linker/MirBSD-GNU-CXX.cmake b/Modules/Platform/Linker/MirBSD-GNU-CXX.cmake new file mode 100644 index 000000000..1edb8e8cf --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-GNU-CXX.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU-CXX) diff --git a/Modules/Platform/Linker/MirBSD-GNU-Fortran.cmake b/Modules/Platform/Linker/MirBSD-GNU-Fortran.cmake new file mode 100644 index 000000000..70890a7bd --- /dev/null +++ b/Modules/Platform/Linker/MirBSD-GNU-Fortran.cmake @@ -0,0 +1,4 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU-Fortran) diff --git a/Modules/Platform/Linker/NetBSD-ASM.cmake b/Modules/Platform/Linker/NetBSD-ASM.cmake new file mode 100644 index 000000000..72a9119f9 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/NetBSD-GNU-ASM) diff --git a/Modules/Platform/Linker/NetBSD-C.cmake b/Modules/Platform/Linker/NetBSD-C.cmake new file mode 100644 index 000000000..6e08344c3 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/NetBSD-GNU-C) diff --git a/Modules/Platform/Linker/NetBSD-CXX.cmake b/Modules/Platform/Linker/NetBSD-CXX.cmake new file mode 100644 index 000000000..23d6cac92 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/NetBSD-GNU-CXX) diff --git a/Modules/Platform/Linker/NetBSD-Fortran.cmake b/Modules/Platform/Linker/NetBSD-Fortran.cmake new file mode 100644 index 000000000..668412777 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/NetBSD-GNU-Fortran) diff --git a/Modules/Platform/Linker/NetBSD-GNU-ASM.cmake b/Modules/Platform/Linker/NetBSD-GNU-ASM.cmake new file mode 100644 index 000000000..533589e52 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/NetBSD-GNU) + +__netbsd_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/NetBSD-GNU-C.cmake b/Modules/Platform/Linker/NetBSD-GNU-C.cmake new file mode 100644 index 000000000..2f0085646 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/NetBSD-GNU) + +__netbsd_linker_gnu(C) diff --git a/Modules/Platform/Linker/NetBSD-GNU-CXX.cmake b/Modules/Platform/Linker/NetBSD-GNU-CXX.cmake new file mode 100644 index 000000000..6e5da900a --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/NetBSD-GNU) + +__netbsd_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/NetBSD-GNU-Fortran.cmake b/Modules/Platform/Linker/NetBSD-GNU-Fortran.cmake new file mode 100644 index 000000000..83d778494 --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/NetBSD-GNU) + +__netbsd_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/NetBSD-GNU.cmake b/Modules/Platform/Linker/NetBSD-GNU.cmake new file mode 100644 index 000000000..f06e491ba --- /dev/null +++ b/Modules/Platform/Linker/NetBSD-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__netbsd_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/OpenBSD-ASM.cmake b/Modules/Platform/Linker/OpenBSD-ASM.cmake new file mode 100644 index 000000000..cd0b25e8c --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-ASM.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/OpenBSD-GNU-ASM) +else() + include(Platform/Linker/OpenBSD-LLD-ASM) +endif() + +endblock() diff --git a/Modules/Platform/Linker/OpenBSD-C.cmake b/Modules/Platform/Linker/OpenBSD-C.cmake new file mode 100644 index 000000000..6685807d2 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-C.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/OpenBSD-GNU-C) +else() + include(Platform/Linker/OpenBSD-LLD-C) +endif() + +endblock() diff --git a/Modules/Platform/Linker/OpenBSD-CXX.cmake b/Modules/Platform/Linker/OpenBSD-CXX.cmake new file mode 100644 index 000000000..4a3d6f801 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-CXX.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/OpenBSD-GNU-CXX) +else() + include(Platform/Linker/OpenBSD-LLD-CXX) +endif() + +endblock() diff --git a/Modules/Platform/Linker/OpenBSD-Fortran.cmake b/Modules/Platform/Linker/OpenBSD-Fortran.cmake new file mode 100644 index 000000000..4ec2fe5a1 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-Fortran.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/BSD-Linker-Initialize) + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(_CMAKE_SYSTEM_LINKER_TYPE STREQUAL "GNU") + include(Platform/Linker/OpenBSD-GNU-Fortran) +else() + include(Platform/Linker/OpenBSD-LLD-Fortran) +endif() + +endblock() diff --git a/Modules/Platform/Linker/OpenBSD-GNU-ASM.cmake b/Modules/Platform/Linker/OpenBSD-GNU-ASM.cmake new file mode 100644 index 000000000..6ad992d8a --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU) + +__openbsd_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/OpenBSD-GNU-C.cmake b/Modules/Platform/Linker/OpenBSD-GNU-C.cmake new file mode 100644 index 000000000..64c8adf2d --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU) + +__openbsd_linker_gnu(C) diff --git a/Modules/Platform/Linker/OpenBSD-GNU-CXX.cmake b/Modules/Platform/Linker/OpenBSD-GNU-CXX.cmake new file mode 100644 index 000000000..98668e565 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU) + +__openbsd_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/OpenBSD-GNU-Fortran.cmake b/Modules/Platform/Linker/OpenBSD-GNU-Fortran.cmake new file mode 100644 index 000000000..0f2b11f9b --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-GNU) + +__openbsd_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/OpenBSD-GNU.cmake b/Modules/Platform/Linker/OpenBSD-GNU.cmake new file mode 100644 index 000000000..b30e4679f --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__openbsd_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/OpenBSD-LLD-ASM.cmake b/Modules/Platform/Linker/OpenBSD-LLD-ASM.cmake new file mode 100644 index 000000000..008e79a99 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-LLD) + +__openbsd_linker_lld(ASM) diff --git a/Modules/Platform/Linker/OpenBSD-LLD-C.cmake b/Modules/Platform/Linker/OpenBSD-LLD-C.cmake new file mode 100644 index 000000000..20f8d12a3 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-LLD) + +__openbsd_linker_lld(C) diff --git a/Modules/Platform/Linker/OpenBSD-LLD-CXX.cmake b/Modules/Platform/Linker/OpenBSD-LLD-CXX.cmake new file mode 100644 index 000000000..d0fd59f88 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-LLD) + +__openbsd_linker_lld(CXX) diff --git a/Modules/Platform/Linker/OpenBSD-LLD-Fortran.cmake b/Modules/Platform/Linker/OpenBSD-LLD-Fortran.cmake new file mode 100644 index 000000000..55efce3b5 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-LLD-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/OpenBSD-LLD) + +__openbsd_linker_lld(Fortran) diff --git a/Modules/Platform/Linker/OpenBSD-LLD.cmake b/Modules/Platform/Linker/OpenBSD-LLD.cmake new file mode 100644 index 000000000..522a232f7 --- /dev/null +++ b/Modules/Platform/Linker/OpenBSD-LLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/OpenBSD-GNU) + + +macro(__openbsd_linker_lld lang) + __openbsd_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/SerenityOS-ASM.cmake b/Modules/Platform/Linker/SerenityOS-ASM.cmake new file mode 100644 index 000000000..98fe46f47 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/SerenityOS-GNU-ASM) diff --git a/Modules/Platform/Linker/SerenityOS-C.cmake b/Modules/Platform/Linker/SerenityOS-C.cmake new file mode 100644 index 000000000..d4aa95311 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/SerenityOS-GNU-C) diff --git a/Modules/Platform/Linker/SerenityOS-CXX.cmake b/Modules/Platform/Linker/SerenityOS-CXX.cmake new file mode 100644 index 000000000..34e9311ac --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# GNU is the default linker +include(Platform/Linker/SerenityOS-GNU-CXX) diff --git a/Modules/Platform/Linker/SerenityOS-GNU-ASM.cmake b/Modules/Platform/Linker/SerenityOS-GNU-ASM.cmake new file mode 100644 index 000000000..3ea724260 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-GNU) + +__serenityos_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/SerenityOS-GNU-C.cmake b/Modules/Platform/Linker/SerenityOS-GNU-C.cmake new file mode 100644 index 000000000..c66d7979f --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-GNU) + +__serenityos_linker_gnu(C) diff --git a/Modules/Platform/Linker/SerenityOS-GNU-CXX.cmake b/Modules/Platform/Linker/SerenityOS-GNU-CXX.cmake new file mode 100644 index 000000000..ff1f6777d --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-GNU) + +__serenityos_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/SerenityOS-GNU.cmake b/Modules/Platform/Linker/SerenityOS-GNU.cmake new file mode 100644 index 000000000..9b06bfb67 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__serenityos_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/SerenityOS-LLD-ASM.cmake b/Modules/Platform/Linker/SerenityOS-LLD-ASM.cmake new file mode 100644 index 000000000..23be6bd83 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-LLD) + +__serenityos_linker_lld(ASM) diff --git a/Modules/Platform/Linker/SerenityOS-LLD-C.cmake b/Modules/Platform/Linker/SerenityOS-LLD-C.cmake new file mode 100644 index 000000000..43b077c06 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-LLD) + +__serenityos_linker_lld(C) diff --git a/Modules/Platform/Linker/SerenityOS-LLD-CXX.cmake b/Modules/Platform/Linker/SerenityOS-LLD-CXX.cmake new file mode 100644 index 000000000..95851ca4d --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SerenityOS-LLD) + +__serenityos_linker_lld(CXX) diff --git a/Modules/Platform/Linker/SerenityOS-LLD.cmake b/Modules/Platform/Linker/SerenityOS-LLD.cmake new file mode 100644 index 000000000..9f34e79a3 --- /dev/null +++ b/Modules/Platform/Linker/SerenityOS-LLD.cmake @@ -0,0 +1,16 @@ +# 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. +include_guard() + + +include(Platform/Linker/SerenityOS-GNU) + + +macro(__serenityos_linker_lld lang) + __serenityos_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE DEDUPLICATION=ALL) +endmacro() diff --git a/Modules/Platform/Linker/SunOS-ASM.cmake b/Modules/Platform/Linker/SunOS-ASM.cmake new file mode 100644 index 000000000..0013e0b04 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-ASM.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Solaris is the default linker +include(Platform/Linker/SunOS-Solaris-ASM) diff --git a/Modules/Platform/Linker/SunOS-C.cmake b/Modules/Platform/Linker/SunOS-C.cmake new file mode 100644 index 000000000..fe90f6a20 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-C.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Solaris is the default linker +include(Platform/Linker/SunOS-Solaris-C) diff --git a/Modules/Platform/Linker/SunOS-CXX.cmake b/Modules/Platform/Linker/SunOS-CXX.cmake new file mode 100644 index 000000000..0111500d6 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-CXX.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Solaris is the default linker +include(Platform/Linker/SunOS-Solaris-CXX) diff --git a/Modules/Platform/Linker/SunOS-Fortran.cmake b/Modules/Platform/Linker/SunOS-Fortran.cmake new file mode 100644 index 000000000..6252846ad --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Fortran.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# Solaris is the default linker +include(Platform/Linker/SunOS-Solaris-Fortran) diff --git a/Modules/Platform/Linker/SunOS-GNU-ASM.cmake b/Modules/Platform/Linker/SunOS-GNU-ASM.cmake new file mode 100644 index 000000000..d69b617c2 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-GNU) + +__sunos_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/SunOS-GNU-C.cmake b/Modules/Platform/Linker/SunOS-GNU-C.cmake new file mode 100644 index 000000000..76554ced0 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-GNU) + +__sunos_linker_gnu(C) diff --git a/Modules/Platform/Linker/SunOS-GNU-CXX.cmake b/Modules/Platform/Linker/SunOS-GNU-CXX.cmake new file mode 100644 index 000000000..68b2d8cf1 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-GNU) + +__sunos_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/SunOS-GNU-Fortran.cmake b/Modules/Platform/Linker/SunOS-GNU-Fortran.cmake new file mode 100644 index 000000000..1a7565de1 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-GNU) + +__sunos_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/SunOS-GNU.cmake b/Modules/Platform/Linker/SunOS-GNU.cmake new file mode 100644 index 000000000..693e31a46 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__sunos_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/SunOS-Solaris-ASM.cmake b/Modules/Platform/Linker/SunOS-Solaris-ASM.cmake new file mode 100644 index 000000000..35ad14936 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Solaris-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-Solaris) + +__sunos_linker_solaris(ASM) diff --git a/Modules/Platform/Linker/SunOS-Solaris-C.cmake b/Modules/Platform/Linker/SunOS-Solaris-C.cmake new file mode 100644 index 000000000..6251dcc6e --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Solaris-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-Solaris) + +__sunos_linker_solaris(C) diff --git a/Modules/Platform/Linker/SunOS-Solaris-CXX.cmake b/Modules/Platform/Linker/SunOS-Solaris-CXX.cmake new file mode 100644 index 000000000..f6e26451a --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Solaris-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-Solaris) + +__sunos_linker_solaris(CXX) diff --git a/Modules/Platform/Linker/SunOS-Solaris-Fortran.cmake b/Modules/Platform/Linker/SunOS-Solaris-Fortran.cmake new file mode 100644 index 000000000..f55b9f25b --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Solaris-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/SunOS-Solaris) + +__sunos_linker_solaris(Fortran) diff --git a/Modules/Platform/Linker/SunOS-Solaris.cmake b/Modules/Platform/Linker/SunOS-Solaris.cmake new file mode 100644 index 000000000..cf82bf8d5 --- /dev/null +++ b/Modules/Platform/Linker/SunOS-Solaris.cmake @@ -0,0 +1,27 @@ +# 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. +include_guard() + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +macro(__sunos_linker_solaris lang) + # Features for LINK_LIBRARY generator expression + ## WHOLE_ARCHIVE: Force loading all members of an archive + if (CMAKE_SYSTEM_VERSION VERSION_GREATER "5.10") + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" + "" + "LINKER:--no-whole-archive") + else() + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-z,allextract" + "" + "LINKER:-z,defaultextract") + endif() + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) +endmacro() + +endblock() diff --git a/Modules/Platform/Linker/Windows-ASM.cmake b/Modules/Platform/Linker/Windows-ASM.cmake new file mode 100644 index 000000000..bddaed172 --- /dev/null +++ b/Modules/Platform/Linker/Windows-ASM.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(CMAKE_ASM_SIMULATE_ID STREQUAL "MSVC") + # MSVC is the default linker + include(Platform/Linker/Windows-MSVC-ASM) +else() + # GNU is the default linker + include(Platform/Linker/Windows-GNU-ASM) +endif() + +endblock() diff --git a/Modules/Platform/Linker/Windows-C.cmake b/Modules/Platform/Linker/Windows-C.cmake new file mode 100644 index 000000000..4a584076e --- /dev/null +++ b/Modules/Platform/Linker/Windows-C.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(CMAKE_C_SIMULATE_ID STREQUAL "MSVC") + # MSVC is the default linker + include(Platform/Linker/Windows-MSVC-C) +else() + # GNU is the default linker + include(Platform/Linker/Windows-GNU-C) +endif() + +endblock() diff --git a/Modules/Platform/Linker/Windows-CUDA.cmake b/Modules/Platform/Linker/Windows-CUDA.cmake new file mode 100644 index 000000000..7e89791fd --- /dev/null +++ b/Modules/Platform/Linker/Windows-CUDA.cmake @@ -0,0 +1,5 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# MSVC is the default linker +include(Platform/Linker/Windows-MSVC-CUDA) diff --git a/Modules/Platform/Linker/Windows-CXX.cmake b/Modules/Platform/Linker/Windows-CXX.cmake new file mode 100644 index 000000000..29768511d --- /dev/null +++ b/Modules/Platform/Linker/Windows-CXX.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + # MSVC is the default linker + include(Platform/Linker/Windows-MSVC-CXX) +else() + # GNU is the default linker + include(Platform/Linker/Windows-GNU-CXX) +endif() + +endblock() diff --git a/Modules/Platform/Linker/Windows-Fortran.cmake b/Modules/Platform/Linker/Windows-Fortran.cmake new file mode 100644 index 000000000..928f6d595 --- /dev/null +++ b/Modules/Platform/Linker/Windows-Fortran.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(CMAKE_Fortran_SIMULATE_ID STREQUAL "MSVC") + # MSVC is the default linker + include(Platform/Linker/Windows-MSVC-Fortran) +else() + # GNU is the default linker + include(Platform/Linker/Windows-GNU-Fortran) +endif() + +endblock() diff --git a/Modules/Platform/Linker/Windows-GNU-ASM.cmake b/Modules/Platform/Linker/Windows-GNU-ASM.cmake new file mode 100644 index 000000000..7fac7b188 --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-GNU) + +__windows_linker_gnu(ASM) diff --git a/Modules/Platform/Linker/Windows-GNU-C.cmake b/Modules/Platform/Linker/Windows-GNU-C.cmake new file mode 100644 index 000000000..833a64dd7 --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-GNU) + +__windows_linker_gnu(C) diff --git a/Modules/Platform/Linker/Windows-GNU-CXX.cmake b/Modules/Platform/Linker/Windows-GNU-CXX.cmake new file mode 100644 index 000000000..6bc1af0af --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-GNU) + +__windows_linker_gnu(CXX) diff --git a/Modules/Platform/Linker/Windows-GNU-Fortran.cmake b/Modules/Platform/Linker/Windows-GNU-Fortran.cmake new file mode 100644 index 000000000..4f0c35fe5 --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-GNU) + +__windows_linker_gnu(Fortran) diff --git a/Modules/Platform/Linker/Windows-GNU-HIP.cmake b/Modules/Platform/Linker/Windows-GNU-HIP.cmake new file mode 100644 index 000000000..4814888ce --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-GNU) + +__windows_linker_gnu(HIP) diff --git a/Modules/Platform/Linker/Windows-GNU.cmake b/Modules/Platform/Linker/Windows-GNU.cmake new file mode 100644 index 000000000..a5c4417aa --- /dev/null +++ b/Modules/Platform/Linker/Windows-GNU.cmake @@ -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. + + +# This module is shared by multiple languages; use include blocker. +include_guard() + +include(Platform/Linker/GNU) + +macro(__windows_linker_gnu lang) + if(CMAKE_${lang}_COMPILER_LINKER) + __cmake_set_whole_archive_feature("${CMAKE_${lang}_COMPILER_LINKER}" "${lang}") + endif() +endmacro() diff --git a/Modules/Platform/Linker/Windows-HIP.cmake b/Modules/Platform/Linker/Windows-HIP.cmake new file mode 100644 index 000000000..67b37d13e --- /dev/null +++ b/Modules/Platform/Linker/Windows-HIP.cmake @@ -0,0 +1,15 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +if(CMAKE_HIP_SIMULATE_ID STREQUAL "MSVC") + # MSVC is the default linker + include(Platform/Linker/Windows-MSVC-HIP) +else() + # GNU is the default linker + include(Platform/Linker/Windows-GNU-HIP) +endif() + +endblock() diff --git a/Modules/Platform/Linker/Windows-LLD-ASM.cmake b/Modules/Platform/Linker/Windows-LLD-ASM.cmake new file mode 100644 index 000000000..fbcc55d1e --- /dev/null +++ b/Modules/Platform/Linker/Windows-LLD-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-LLD) + +__windows_linker_lld(ASM) diff --git a/Modules/Platform/Linker/Windows-LLD-C.cmake b/Modules/Platform/Linker/Windows-LLD-C.cmake new file mode 100644 index 000000000..8efd87e9a --- /dev/null +++ b/Modules/Platform/Linker/Windows-LLD-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-LLD) + +__windows_linker_lld(C) diff --git a/Modules/Platform/Linker/Windows-LLD-CXX.cmake b/Modules/Platform/Linker/Windows-LLD-CXX.cmake new file mode 100644 index 000000000..7bd3131b9 --- /dev/null +++ b/Modules/Platform/Linker/Windows-LLD-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-LLD) + +__windows_linker_lld(CXX) diff --git a/Modules/Platform/Linker/Windows-LLD-HIP.cmake b/Modules/Platform/Linker/Windows-LLD-HIP.cmake new file mode 100644 index 000000000..4b04a79cd --- /dev/null +++ b/Modules/Platform/Linker/Windows-LLD-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-LLD) + +__windows_linker_lld(HIP) diff --git a/Modules/Platform/Linker/Windows-LLD.cmake b/Modules/Platform/Linker/Windows-LLD.cmake new file mode 100644 index 000000000..f2dde1ad3 --- /dev/null +++ b/Modules/Platform/Linker/Windows-LLD.cmake @@ -0,0 +1,24 @@ +# 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. +include_guard() + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +macro(__windows_linker_lld lang) + # Features for LINK_LIBRARY generator expression + if(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT STREQUAL "GNU") + include(Platform/Linker/Windows-GNU) + __windows_linker_gnu(${lang}) + + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) + else() + include(Platform/Linker/Windows-MSVC) + __windows_linker_msvc(${lang}) + endif() +endmacro() + +endblock() diff --git a/Modules/Platform/Linker/Windows-MSVC-ASM.cmake b/Modules/Platform/Linker/Windows-MSVC-ASM.cmake new file mode 100644 index 000000000..df3a64bed --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-ASM.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(ASM) diff --git a/Modules/Platform/Linker/Windows-MSVC-C.cmake b/Modules/Platform/Linker/Windows-MSVC-C.cmake new file mode 100644 index 000000000..467c0f34f --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-C.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(C) diff --git a/Modules/Platform/Linker/Windows-MSVC-CUDA.cmake b/Modules/Platform/Linker/Windows-MSVC-CUDA.cmake new file mode 100644 index 000000000..dc3d5e9f1 --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-CUDA.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(CUDA) diff --git a/Modules/Platform/Linker/Windows-MSVC-CXX.cmake b/Modules/Platform/Linker/Windows-MSVC-CXX.cmake new file mode 100644 index 000000000..f5d3294f4 --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-CXX.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(CXX) diff --git a/Modules/Platform/Linker/Windows-MSVC-Fortran.cmake b/Modules/Platform/Linker/Windows-MSVC-Fortran.cmake new file mode 100644 index 000000000..b5f99f3af --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-Fortran.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(Fortran) diff --git a/Modules/Platform/Linker/Windows-MSVC-HIP.cmake b/Modules/Platform/Linker/Windows-MSVC-HIP.cmake new file mode 100644 index 000000000..cc2a7daea --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC-HIP.cmake @@ -0,0 +1,6 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include(Platform/Linker/Windows-MSVC) + +__windows_linker_msvc(HIP) diff --git a/Modules/Platform/Linker/Windows-MSVC.cmake b/Modules/Platform/Linker/Windows-MSVC.cmake new file mode 100644 index 000000000..2eb571392 --- /dev/null +++ b/Modules/Platform/Linker/Windows-MSVC.cmake @@ -0,0 +1,35 @@ +# 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. +include_guard() + +block(SCOPE_FOR POLICIES) +cmake_policy(SET CMP0054 NEW) + +# 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:") + set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) +endif() + +macro(__windows_linker_msvc lang) + set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) + + # Features for LINK_LIBRARY generator expression + if(DEFINED CMAKE_${lang}_COMPILER_LINKER_VERSION) + if (CMAKE_${lang}_COMPILER_LINKER_VERSION GREATER_EQUAL "14.10") + ## WHOLE_ARCHIVE: Force loading all members of an archive + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:") + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) + set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) + else() + set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED FALSE) + endif() + endif() +endmacro() + +endblock() diff --git a/Modules/Platform/Linux-Intel.cmake b/Modules/Platform/Linux-Intel.cmake index 3b5ca59ca..95debf96d 100644 --- a/Modules/Platform/Linux-Intel.cmake +++ b/Modules/Platform/Linux-Intel.cmake @@ -39,6 +39,11 @@ macro(__linux_compiler_intel lang) set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,") set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",") + # FIXME(#26157): compute CMAKE__COMPILER_LINKER* variables + # in the meantime, enforce deactivation of push/pop state linker options + # because xild front-end linker do not support these options even if the platform linker does... + set(_CMAKE_${lang}_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") + set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES) if(XIAR) diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake index b9801ee81..5b61dd6b6 100644 --- a/Modules/Platform/Linux.cmake +++ b/Modules/Platform/Linux.cmake @@ -20,32 +20,6 @@ foreach(type SHARED_LIBRARY SHARED_MODULE EXE) endforeach() -# Features for LINK_LIBRARY generator expression -## check linker capabilities -if(NOT DEFINED _CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - execute_process(COMMAND "${CMAKE_LINKER}" --help - OUTPUT_VARIABLE __linker_help - ERROR_VARIABLE __linker_help) - if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") - else() - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") - endif() - unset(__linker_help) -endif() -## WHOLE_ARCHIVE: Force loading all members of an archive -if(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" - "" - "LINKER:--pop-state") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is ## no pending undefined symbols @@ -84,7 +58,7 @@ include(Platform/UnixPaths) # Debian has lib32 and lib64 paths only for compatibility so they should not be # searched. -if(NOT CMAKE_CROSSCOMPILING) +if(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_COMPILER_SYSROOT) 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) diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake index 892d55ba1..110212b4a 100644 --- a/Modules/Platform/NetBSD.cmake +++ b/Modules/Platform/NetBSD.cmake @@ -13,33 +13,6 @@ set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") set(CMAKE_EXE_EXPORTS_C_FLAG "-Wl,--export-dynamic") -# Features for LINK_LIBRARY generator expression -## check linker capabilities -if(NOT DEFINED _CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - execute_process(COMMAND "${CMAKE_LINKER}" --help - OUTPUT_VARIABLE __linker_help - ERROR_VARIABLE __linker_help) - if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") - else() - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") - endif() - unset(__linker_help) -endif() -## WHOLE_ARCHIVE: Force loading all members of an archive -if(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" - "" - "LINKER:--pop-state") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - - # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is ## no pending undefined symbols diff --git a/Modules/Platform/SunOS.cmake b/Modules/Platform/SunOS.cmake index 25e95e6bc..e01e89228 100644 --- a/Modules/Platform/SunOS.cmake +++ b/Modules/Platform/SunOS.cmake @@ -8,21 +8,6 @@ if(CMAKE_SYSTEM MATCHES "SunOS-4") endif() -# Features for LINK_LIBRARY generator expression -## WHOLE_ARCHIVE: Force loading all members of an archive -if (CMAKE_SYSTEM_VERSION VERSION_GREATER "5.10") - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-z,allextract" - "" - "LINKER:-z,defaultextract") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - - # Features for LINK_GROUP generator expression if (CMAKE_SYSTEM_VERSION VERSION_GREATER "5.9") ## RESCAN: request the linker to rescan static libraries until there is diff --git a/Modules/Platform/WASI-Initialize.cmake b/Modules/Platform/WASI-Initialize.cmake new file mode 100644 index 000000000..b49713f1b --- /dev/null +++ b/Modules/Platform/WASI-Initialize.cmake @@ -0,0 +1 @@ +set(WASI 1) diff --git a/Modules/Platform/WASI.cmake b/Modules/Platform/WASI.cmake new file mode 100644 index 000000000..8e4a05bac --- /dev/null +++ b/Modules/Platform/WASI.cmake @@ -0,0 +1 @@ +# WASI Platform diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake index e3725cb37..7a054928e 100644 --- a/Modules/Platform/Windows-Clang.cmake +++ b/Modules/Platform/Windows-Clang.cmake @@ -55,8 +55,6 @@ macro(__windows_compiler_clang_gnu lang) set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "") set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared") - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) - # linker selection set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link") set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=link") @@ -135,14 +133,6 @@ macro(__windows_compiler_clang_gnu lang) 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") - # Features for LINK_LIBRARY generator expression - if(MSVC_VERSION GREATER "1900") - ## WHOLE_ARCHIVE: Force loading all members of an archive - set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:") - set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) - set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - endif() - enable_language(RC) endmacro() diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index 1a4159bcd..7dc92cd91 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake @@ -64,32 +64,6 @@ if("${_help}" MATCHES "GNU ld .* 2\\.1[1-6]") endif() -# Features for LINK_LIBRARY generator expression -## check linker capabilities -if(NOT DEFINED _CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - execute_process(COMMAND "${CMAKE_LINKER}" --help - OUTPUT_VARIABLE __linker_help - ERROR_VARIABLE __linker_help) - if(__linker_help MATCHES "--push-state" AND __linker_help MATCHES "--pop-state") - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE CACHE INTERNAL "linker supports push/pop state") - else() - set(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED FALSE CACHE INTERNAL "linker supports push/pop state") - endif() - unset(__linker_help) -endif() -## WHOLE_ARCHIVE: Force loading all members of an archive -if(_CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED) - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--push-state,--whole-archive" - "" - "LINKER:--pop-state") -else() - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:--whole-archive" - "" - "LINKER:--no-whole-archive") -endif() -set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) -set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - # Features for LINK_GROUP generator expression ## RESCAN: request the linker to rescan static libraries until there is ## no pending undefined symbols diff --git a/Modules/Platform/Windows-IntelLLVM.cmake b/Modules/Platform/Windows-IntelLLVM.cmake index 517d95959..ba2cc9c09 100644 --- a/Modules/Platform/Windows-IntelLLVM.cmake +++ b/Modules/Platform/Windows-IntelLLVM.cmake @@ -32,14 +32,6 @@ macro(__windows_compiler_intel lang) 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:") - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) - set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) - endif() - set(CMAKE_${lang}_LINK_EXECUTABLE "${_CMAKE_VS_LINK_EXE} ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /link /out: /implib: /pdb: /version:.${_PLATFORM_LINK_FLAGS} ${CMAKE_END_TEMP_FILE}") set(CMAKE_${lang}_CREATE_SHARED_LIBRARY diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake index 5809379f0..0530441d9 100644 --- a/Modules/Platform/Windows-MSVC.cmake +++ b/Modules/Platform/Windows-MSVC.cmake @@ -240,7 +240,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver") else() set(_PLATFORM_DEFINES "/DWIN32") if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64EC") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64EC")) - set(_PLATFORM_DEFINES "${_PLATFORM_DEFINES} /D_AMD64_ /DAMD64 /D_ARM64EC_ /DARM64EC") + set(_PLATFORM_DEFINES "${_PLATFORM_DEFINES} /D_AMD64_ /D_ARM64EC_") endif() if(_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM" OR _MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM") set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib") @@ -353,20 +353,12 @@ else() endif() unset(__WINDOWS_MSVC_CMP0141) -# 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 "/WHOLEARCHIVE:") - set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) - set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_ATTRIBUTES LIBRARY_TYPE=STATIC DEDUPLICATION=YES OVERRIDE=DEFAULT) -endif() - macro(__windows_compiler_msvc lang) if(NOT MSVC_VERSION LESS 1400) # for 2005 make sure the manifest is put in the dll with mt - set(_CMAKE_VS_LINK_DLL " -E vs_link_dll --intdir= --rc= --mt= --manifests -- ") - set(_CMAKE_VS_LINK_EXE " -E vs_link_exe --intdir= --rc= --mt= --manifests -- ") + set(_CMAKE_VS_LINK_DLL " -E vs_link_dll --msvc-ver=${MSVC_VERSION} --intdir= --rc= --mt= --manifests -- ") + set(_CMAKE_VS_LINK_EXE " -E vs_link_exe --msvc-ver=${MSVC_VERSION} --intdir= --rc= --mt= --manifests -- ") endif() if(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver") set(_DLL_DRIVER "-driver") @@ -514,8 +506,6 @@ macro(__windows_compiler_msvc lang) set(CMAKE_${lang}_DEPFILE_FORMAT msvc) endif() - set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD DEDUPLICATION=ALL) - # linker selection set(CMAKE_${lang}_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}") set(CMAKE_${lang}_USING_LINKER_LLD "${CMAKE_LINKER_LLD}") diff --git a/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake b/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake index ce8060bed..e3858b33b 100644 --- a/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake +++ b/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake @@ -1 +1,2 @@ include(Platform/Windows-MSVC-C) +__windows_kernel_mode(C) diff --git a/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake b/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake index 281eadc10..e0b46cae4 100644 --- a/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake +++ b/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake @@ -1 +1,2 @@ include(Platform/Windows-MSVC-CXX) +__windows_kernel_mode(CXX) diff --git a/Modules/Platform/WindowsKernelModeDriver.cmake b/Modules/Platform/WindowsKernelModeDriver.cmake index 65b2eaed4..78bf2d4f9 100644 --- a/Modules/Platform/WindowsKernelModeDriver.cmake +++ b/Modules/Platform/WindowsKernelModeDriver.cmake @@ -1 +1,65 @@ include(Platform/Windows) +macro(__windows_kernel_mode lang) + if(CMAKE_CROSSCOMPILING) + set(_KMDF_ERROR_EPILOGUE + "Please set a valid CMAKE_WINDOWS_KMDF_VERSION in the toolchain file. " + "For more information, see\n" + " https://learn.microsoft.com/en-us/windows-hardware/drivers/wdf/kmdf-version-history" + ) + if(NOT DEFINED CMAKE_WINDOWS_KMDF_VERSION) + message(FATAL_ERROR + "The Kernel-Mode Driver Framework (KMDF) version has not been set. " + ${_KMDF_ERROR_EPILOGUE} + ) + endif() + if(NOT CMAKE_WINDOWS_KMDF_VERSION MATCHES "^[0-9]\.[0-9]+$") + message(FATAL_ERROR + "The Kernel-Mode Driver Framework (KMDF) version is set to an invalid value. " + "The expected format is [0-9].[0-9]+. For example, 1.15 or 1.9. " + ${_KMDF_ERROR_EPILOGUE} + ) + endif() + + set(_KMDF_ENV_VARS + Platform + WindowsSdkDir + VCToolsInstallDir + ) + if(DEFINED ENV{EnterpriseWDK}) + set(_WINDOWS_SDK_VERSION "$ENV{Version_Number}") + list(APPEND _KMDF_ENV_VARS Version_Number) + else() + set(_WINDOWS_SDK_VERSION "$ENV{WindowsSDKLibVersion}") + list(APPEND _KMDF_ENV_VARS WindowsSDKLibVersion) + endif() + foreach(var IN LISTS _KMDF_ENV_VARS) + if(NOT DEFINED ENV{${var}}) + message(FATAL_ERROR "Required environment variable '${var}' is not defined.") + endif() + endforeach() + unset(_KMDF_ENV_VARS) + + set(_KMDF_PLATFORM "$ENV{Platform}") + + list(APPEND CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES + $ENV{WindowsSdkDir}/Include/${_WINDOWS_SDK_VERSION}/km + $ENV{WindowsSdkDir}/Include/${_WINDOWS_SDK_VERSION}/km/crt + $ENV{WindowsSdkDir}/Include/${_WINDOWS_SDK_VERSION}/shared + $ENV{WindowsSdkDir}/Include/wdf/kmdf/${CMAKE_WINDOWS_KMDF_VERSION} + $ENV{VCToolsInstallDir}/include + ) + + list(APPEND CMAKE_RC_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_${lang}_STANDARD_INCLUDE_DIRECTORIES} + ) + list(REMOVE_DUPLICATES CMAKE_RC_STANDARD_INCLUDE_DIRECTORIES) + + list(APPEND CMAKE_${lang}_STANDARD_LINK_DIRECTORIES + $ENV{WindowsSdkDir}/Lib/${_WINDOWS_SDK_VERSION}/km/${_KMDF_PLATFORM} + ) + + unset(_KMDF_ERROR_EPILOGUE) + unset(_KMDF_PLATFORM) + unset(_WINDOWS_SDK_VERSION) + endif() +endmacro() diff --git a/Modules/Platform/iOS-Initialize.cmake b/Modules/Platform/iOS-Initialize.cmake index 301ca4cbd..91e3898a4 100644 --- a/Modules/Platform/iOS-Initialize.cmake +++ b/Modules/Platform/iOS-Initialize.cmake @@ -1,6 +1,6 @@ include(Platform/Darwin-Initialize) -if(NOT _CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhone(OS|Simulator)") +if(NOT _CMAKE_OSX_SYSROOT_PATH MATCHES "/(iPhoneOS|iPhoneSimulator|MacOSX)") message(FATAL_ERROR "${CMAKE_OSX_SYSROOT} is not an iOS SDK") endif() diff --git a/Modules/SystemInformation.in b/Modules/SystemInformation.in index f2aef5055..50b5720e2 100644 --- a/Modules/SystemInformation.in +++ b/Modules/SystemInformation.in @@ -6,6 +6,7 @@ CMAKE_STATIC_LIBRARY_PREFIX == "${CMAKE_STATIC_LIBRARY_PREFIX}" CMAKE_STATIC_LIBRARY_SUFFIX == "${CMAKE_STATIC_LIBRARY_SUFFIX}" CMAKE_SHARED_LIBRARY_PREFIX == "${CMAKE_SHARED_LIBRARY_PREFIX}" CMAKE_SHARED_LIBRARY_SUFFIX == "${CMAKE_SHARED_LIBRARY_SUFFIX}" +CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX == "${CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX}" CMAKE_SHARED_MODULE_PREFIX == "${CMAKE_SHARED_MODULE_PREFIX}" CMAKE_SHARED_MODULE_SUFFIX == "${CMAKE_SHARED_MODULE_SUFFIX}" diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake index 1724c3ace..1fcc3af61 100644 --- a/Modules/UseJava.cmake +++ b/Modules/UseJava.cmake @@ -873,6 +873,7 @@ function(add_jar _TARGET_NAME) endforeach() endif() + cmake_language(GET_MESSAGE_LOG_LEVEL _LOG_LEVEL) # Compile the java files and create a list of class files add_custom_command( # NOTE: this command generates an artificial dependency file @@ -881,6 +882,7 @@ function(add_jar _TARGET_NAME) -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH} -DCMAKE_JAR_CLASSES_PREFIX=${CMAKE_JAR_CLASSES_PREFIX} -P ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/UseJava/ClearClassFiles.cmake + --log-level ${_LOG_LEVEL} COMMAND ${Java_JAVAC_EXECUTABLE} ${CMAKE_JAVA_COMPILE_FLAGS} -classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}" diff --git a/Modules/UseJava/ClearClassFiles.cmake b/Modules/UseJava/ClearClassFiles.cmake index 2c41665f4..42a7666d5 100644 --- a/Modules/UseJava/ClearClassFiles.cmake +++ b/Modules/UseJava/ClearClassFiles.cmake @@ -12,7 +12,7 @@ if(CMAKE_JAVA_CLASS_OUTPUT_PATH) list(TRANSFORM classes PREPEND "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/") if(classes) file(REMOVE ${classes}) - message(STATUS "Clean class files from previous build") + message(VERBOSE "Clean class files from previous build") endif() endif() else() diff --git a/Modules/UseJava/javaTargets.cmake.in b/Modules/UseJava/javaTargets.cmake.in index dc20c822b..6002d4eed 100644 --- a/Modules/UseJava/javaTargets.cmake.in +++ b/Modules/UseJava/javaTargets.cmake.in @@ -1,5 +1,5 @@ cmake_policy(PUSH) -cmake_policy(VERSION 2.8.12...3.28) +cmake_policy(VERSION 2.8.12...3.29) #---------------------------------------------------------------- # Generated CMake Java target import file. diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake index c58511ae5..bc4de91fe 100644 --- a/Modules/UseSWIG.cmake +++ b/Modules/UseSWIG.cmake @@ -188,9 +188,8 @@ ensure generated files will receive the required settings. itself. This property is only meaningful for :ref:`Makefile `, :ref:`Ninja `, :generator:`Xcode`, and - :ref:`Visual Studio ` - (:generator:`Visual Studio 12 2013` and above) generators. Default value is - ``FALSE``. + :ref:`Visual Studio ` generators. + Default value is ``FALSE``. .. versionadded:: 3.21 Added the support of :generator:`Xcode` generator. @@ -352,9 +351,8 @@ as well as ``SWIG``: itself. This variable is only meaningful for :ref:`Makefile `, :ref:`Ninja `, :generator:`Xcode`, and - :ref:`Visual Studio ` - (:generator:`Visual Studio 12 2013` and above) generators. Default value is - ``FALSE``. + :ref:`Visual Studio ` generators. + Default value is ``FALSE``. Source file property ``USE_SWIG_DEPENDENCIES``, if not defined, will be initialized with the value of this variable. @@ -565,7 +563,7 @@ function(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile) endif() set (use_swig_dependencies ${SWIG_USE_SWIG_DEPENDENCIES}) - if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])") + if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio") get_property(use_swig_dependencies_set SOURCE "${infile}" PROPERTY USE_SWIG_DEPENDENCIES SET) if (use_swig_dependencies_set) get_property(use_swig_dependencies SOURCE "${infile}" PROPERTY USE_SWIG_DEPENDENCIES) @@ -890,7 +888,7 @@ function(SWIG_ADD_LIBRARY name) set(SWIG_SOURCE_FILE_EXTENSIONS ".i") endif() - if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])") + if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio") # For Makefiles, Ninja, Xcode and Visual Studio generators, # use SWIG generated dependencies if requested if (NOT DEFINED SWIG_USE_SWIG_DEPENDENCIES) diff --git a/README.rst b/README.rst index e793d274a..8a471db0e 100644 --- a/README.rst +++ b/README.rst @@ -51,18 +51,19 @@ Building CMake with CMake ------------------------- You can build CMake as any other project with a CMake-based build system: -run the installed CMake on the sources of this CMake with your preferred -options and generators. Then build it and install it. -For instructions how to do this, see documentation on `Running CMake`_. - -.. _`Running CMake`: https://cmake.org/runningcmake +run an already-installed CMake on this source tree with your preferred +generator and options. Then build it and install it. To build the documentation, install `Sphinx`_ and configure CMake with ``-DSPHINX_HTML=ON`` and/or ``-DSPHINX_MAN=ON`` to enable the "html" or "man" builder. Add ``-DSPHINX_EXECUTABLE=/path/to/sphinx-build`` if the tool is not found automatically. +To run the test suite, run ``ctest`` in the CMake build directory after +building. See the `CMake Testing Guide`_ for details. + .. _`Sphinx`: https://sphinx-doc.org +.. _`CMake Testing Guide`: Help/dev/testing.rst Building CMake from Scratch --------------------------- diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 185ebfea7..c4cd101eb 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -121,6 +121,8 @@ add_library( cmBinUtilsWindowsPELinker.h cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h + cmBuildDatabase.cxx + cmBuildDatabase.h cmBuildOptions.h cmCacheManager.cxx cmCacheManager.h @@ -140,6 +142,7 @@ add_library( cmCMakePresetsGraphReadJSONTestPresets.cxx cmCMakePresetsGraphReadJSONWorkflowPresets.cxx cmCommandArgumentParserHelper.cxx + cmCommandLineArgument.h cmCommonTargetGenerator.cxx cmCommonTargetGenerator.h cmComputeComponentGraph.cxx @@ -199,16 +202,28 @@ add_library( cmEvaluatedTargetProperty.cxx cmEvaluatedTargetProperty.h cmExprParserHelper.cxx + cmExportAndroidMKGenerator.h + cmExportAndroidMKGenerator.cxx cmExportBuildAndroidMKGenerator.h cmExportBuildAndroidMKGenerator.cxx + cmExportBuildCMakeConfigGenerator.h + cmExportBuildCMakeConfigGenerator.cxx cmExportBuildFileGenerator.h cmExportBuildFileGenerator.cxx + cmExportCMakeConfigGenerator.h + cmExportCMakeConfigGenerator.cxx cmExportFileGenerator.h cmExportFileGenerator.cxx cmExportInstallAndroidMKGenerator.h cmExportInstallAndroidMKGenerator.cxx + cmExportInstallCMakeConfigGenerator.h + cmExportInstallCMakeConfigGenerator.cxx cmExportInstallFileGenerator.h cmExportInstallFileGenerator.cxx + cmExportInstallPackageInfoGenerator.h + cmExportInstallPackageInfoGenerator.cxx + cmExportPackageInfoGenerator.h + cmExportPackageInfoGenerator.cxx cmExportTryCompileFileGenerator.h cmExportTryCompileFileGenerator.cxx cmExportSet.h @@ -311,6 +326,10 @@ add_library( cmGraphVizWriter.h cmImportedCxxModuleInfo.cxx cmImportedCxxModuleInfo.h + cmInstallAndroidMKExportGenerator.cxx + cmInstallAndroidMKExportGenerator.h + cmInstallCMakeConfigExportGenerator.cxx + cmInstallCMakeConfigExportGenerator.h cmInstallGenerator.h cmInstallGenerator.cxx cmInstallGetRuntimeDependenciesGenerator.h @@ -324,6 +343,8 @@ add_library( cmInstallFilesGenerator.cxx cmInstallImportedRuntimeArtifactsGenerator.h cmInstallImportedRuntimeArtifactsGenerator.cxx + cmInstallPackageInfoExportGenerator.h + cmInstallPackageInfoExportGenerator.cxx cmInstallRuntimeDependencySet.h cmInstallRuntimeDependencySet.cxx cmInstallRuntimeDependencySetGenerator.h @@ -336,6 +357,8 @@ add_library( cmInstallTargetGenerator.cxx cmInstallDirectoryGenerator.h cmInstallDirectoryGenerator.cxx + cmInstallScriptHandler.h + cmInstallScriptHandler.cxx cmJSONHelpers.cxx cmJSONHelpers.h cmJSONState.cxx @@ -359,6 +382,10 @@ add_library( cmLocalCommonGenerator.h cmLocalGenerator.cxx cmLocalGenerator.h + cmPkgConfigParser.cxx + cmPkgConfigParser.h + cmPkgConfigResolver.cxx + cmPkgConfigResolver.h cmPlaceholderExpander.cxx cmPlaceholderExpander.h cmRulePlaceholderExpander.cxx @@ -523,6 +550,8 @@ add_library( cmCMakeMinimumRequired.h cmCMakePathCommand.h cmCMakePathCommand.cxx + cmCMakePkgConfigCommand.h + cmCMakePkgConfigCommand.cxx cmCMakePolicyCommand.cxx cmCMakePolicyCommand.h cmConditionEvaluator.cxx @@ -790,6 +819,7 @@ target_link_libraries( LibUV::LibUV Threads::Threads ZLIB::ZLIB + llpkgc::llpkgc ) if(CMake_ENABLE_DEBUGGER) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index a09ed7b2c..9b5b3dc62 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) -set(CMake_VERSION_MINOR 30) -set(CMake_VERSION_PATCH 5) +set(CMake_VERSION_MINOR 31) +set(CMake_VERSION_PATCH 0) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) @@ -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 [==[9c4a0a9ff0 CMake 3.30.5]==]) + set(git_info [==[e22c8383b9 CMake 3.31.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]* " diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx index a77c22fa8..f2d7f1c47 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.cxx +++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCPackIFWInstaller.h" +#include #include #include #include @@ -306,12 +307,37 @@ void cmCPackIFWInstaller::ConfigureFromOptions() this->GetOption("CPACK_IFW_PACKAGE_PRODUCT_IMAGES")) { this->ProductImages.clear(); cmExpandList(productImages, this->ProductImages); - for (const auto& file : this->ProductImages) { + + auto erase_missing_file_pred = [this](const std::string& file) -> bool { if (!cmSystemTools::FileExists(file)) { - // The warning will say skipped, but there will later be a hard error - // when the binarycreator tool tries to read the missing file. this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_PRODUCT_IMAGES", file); + return true; + } + return false; + }; + + this->ProductImages.erase(std::remove_if(this->ProductImages.begin(), + this->ProductImages.end(), + erase_missing_file_pred), + this->ProductImages.end()); + } + + if (!this->ProductImages.empty()) { + if (cmValue productUrls = + this->GetOption("CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS")) { + this->ProductImageUrls.clear(); + cmExpandList(productUrls, this->ProductImageUrls); + if (this->ProductImageUrls.size() != this->ProductImages.size()) { + cmCPackIFWLogger( + WARNING, + "Option \"CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS\" will be skipped " + "because it contains " + << this->ProductImageUrls.size() + << " elements while \"CPACK_IFW_PACKAGE_PRODUCT_IMAGES\" " + "contains " + << this->ProductImages.size() << " elements." << std::endl); + this->ProductImageUrls.clear(); } } } @@ -614,13 +640,20 @@ void cmCPackIFWInstaller::GenerateInstallerFile() // Product images (copy to config dir) if (!this->IsVersionLess("4.0") && !this->ProductImages.empty()) { xout.StartElement("ProductImages"); - for (auto const& srcImg : this->ProductImages) { + const bool hasProductImageUrl = !this->ProductImageUrls.empty(); + for (size_t i = 0; i < this->ProductImages.size(); ++i) { + xout.StartElement("ProductImage"); + auto const& srcImg = this->ProductImages[i]; std::string name = cmSystemTools::GetFilenameName(srcImg); std::string dstImg = this->Directory + "/config/" + name; cmsys::SystemTools::CopyFileIfDifferent(srcImg, dstImg); xout.Element("Image", name); + if (hasProductImageUrl) { + xout.Element("Url", this->ProductImageUrls.at(i)); + } + xout.EndElement(); // } - xout.EndElement(); + xout.EndElement(); // } // Resources (copy to resources dir) diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.h b/Source/CPack/IFW/cmCPackIFWInstaller.h index 205835b0f..fb980d7c7 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.h +++ b/Source/CPack/IFW/cmCPackIFWInstaller.h @@ -124,6 +124,10 @@ public: /// A list of images to be shown on PerformInstallationPage. std::vector ProductImages; + /// A list of associated URLs linked to images to be shown on + /// PerformInstallationPage. + std::vector ProductImageUrls; + /// Command executed after the installer is done if the user accepts the /// action std::string RunProgram; diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 8532b3ede..1e2d202ae 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -754,7 +754,7 @@ bool cmCPackWIXGenerator::CreateFeatureHierarchy( { for (auto const& i : ComponentGroups) { cmCPackComponentGroup const& group = i.second; - if (group.ParentGroup == nullptr) { + if (!group.ParentGroup) { featureDefinitions.EmitFeatureForComponentGroup(group, *this->Patch); } } diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index 8bd3aa05f..c70a2f197 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -259,11 +259,11 @@ int cmCPackArchiveGenerator::addOneComponentToArchive( std::string rp = filePrefix + file; DeduplicateStatus status = DeduplicateStatus::Add; - if (deduplicator != nullptr) { + if (deduplicator) { status = deduplicator->IsDeduplicate(rp, localToplevel); } - if (deduplicator == nullptr || status == DeduplicateStatus::Add) { + if (!deduplicator || status == DeduplicateStatus::Add) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl); archive.Add(rp, 0, nullptr, false); } else if (status == DeduplicateStatus::Error) { @@ -350,7 +350,7 @@ int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) // Handle Orphan components (components not belonging to any groups) for (auto& comp : this->Components) { // Does the component belong to a group? - if (comp.second.Group == nullptr) { + if (!comp.second.Group) { cmCPackLogger( cmCPackLog::LOG_VERBOSE, "Component <" diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 31191a9ae..14346b472 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -598,7 +598,7 @@ int cmCPackDebGenerator::PackageComponents(bool ignoreGroup) // Handle Orphan components (components not belonging to any groups) for (auto const& comp : this->Components) { // Does the component belong to a group? - if (comp.second.Group == nullptr) { + if (!comp.second.Group) { cmCPackLogger( cmCPackLog::LOG_VERBOSE, "Component <" @@ -789,6 +789,21 @@ bool cmCPackDebGenerator::createDeb() if (cmNonempty(debian_pkg_replaces)) { controlValues["Replaces"] = *debian_pkg_replaces; } + cmValue debian_pkg_multiarch = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MULTIARCH"); + if (cmNonempty(debian_pkg_multiarch)) { + // check for valid values: same, foreign, allowed + if (*debian_pkg_multiarch != "same" && + *debian_pkg_multiarch != "foreign" && + *debian_pkg_multiarch != "allowed") { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error: invalid value for Multi-Arch: " + << *debian_pkg_multiarch + << ". Valid values are: same, foreign, allowed\n"); + return false; + } + controlValues["Multi-Arch"] = *debian_pkg_multiarch; + } const std::string strGenWDIR(this->GetOption("GEN_WDIR")); const std::string shlibsfilename = strGenWDIR + "/shlibs"; @@ -913,7 +928,7 @@ std::string cmCPackDebGenerator::GetComponentInstallSuffix( // the current COMPONENT belongs to. std::string groupVar = "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; - if (nullptr != this->GetOption(groupVar)) { + if (this->GetOption(groupVar)) { return *this->GetOption(groupVar); } return componentName; diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index b02557050..44d0ece5d 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -967,7 +967,7 @@ int cmCPackGenerator::InstallCMakeProject( std::string absoluteDestFileComponent = std::string("CPACK_ABSOLUTE_DESTINATION_FILES") + "_" + this->GetComponentInstallSuffix(component); - if (nullptr != this->GetOption(absoluteDestFileComponent)) { + if (this->GetOption(absoluteDestFileComponent)) { std::string absoluteDestFilesListComponent = cmStrCat(this->GetOption(absoluteDestFileComponent), ';', *d); this->SetOption(absoluteDestFileComponent, @@ -1266,7 +1266,7 @@ int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf) if (val1 != val2) { // One variable is set but not the other? // Then set the other variable to the same value (even if it is invalid). - if (val1.Get() != nullptr && val2.Get() == nullptr) { + if (val1.Get() && !val2.Get()) { cmCPackLogger(cmCPackLog::LOG_WARNING, "Variable CPACK_TEMPORARY_INSTALL_DIRECTORY is set, which " "is not recommended. For backwards-compatibility we will " @@ -1274,7 +1274,7 @@ int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf) "proceed. However, better set neither of them!" << std::endl); this->MakefileMap->AddDefinition("CPACK_TEMPORARY_DIRECTORY", val1); - } else if (val1.Get() == nullptr && val2.Get() != nullptr) { + } else if (!val1.Get() && val2.Get()) { cmCPackLogger( cmCPackLog::LOG_WARNING, "Variable CPACK_TEMPORARY_DIRECTORY is set, which is not recommended." @@ -1301,7 +1301,7 @@ int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf) << std::endl); return 0; } - } else if (val1.Get() != nullptr && val2.Get() != nullptr) { + } else if (val1.Get() && val2.Get()) { cmCPackLogger(cmCPackLog::LOG_WARNING, "Variables CPACK_TEMPORARY_DIRECTORY and " "CPACK_TEMPORARY_INSTALL_DIRECTORY are both set. Because " diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx index 17ea89fa1..35d126f33 100644 --- a/Source/CPack/cmCPackInnoSetupGenerator.cxx +++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx @@ -423,7 +423,7 @@ bool cmCPackInnoSetupGenerator::ProcessFiles() params["DestDir"] = QuotePath(destDir); - if (component != nullptr && component->IsDownloaded) { + if (component && component->IsDownloaded) { const std::string& archiveName = cmSystemTools::GetFilenameWithoutLastExtension( component->ArchiveFile); @@ -968,7 +968,7 @@ bool cmCPackInnoSetupGenerator::BuildDownloadedComponentArchive( } // Try to get the SHA256 hash of the archive file - if (hash == nullptr) { + if (!hash) { return true; } @@ -1088,7 +1088,7 @@ std::string cmCPackInnoSetupGenerator::ISKeyValueLine( std::string cmCPackInnoSetupGenerator::CreateRecursiveComponentPath( cmCPackComponentGroup* group, const std::string& path) { - if (group == nullptr) { + if (!group) { return path; } @@ -1100,7 +1100,7 @@ std::string cmCPackInnoSetupGenerator::CreateRecursiveComponentPath( void cmCPackInnoSetupGenerator::CreateRecursiveComponentGroups( cmCPackComponentGroup* group) { - if (group == nullptr) { + if (!group) { return; } diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 155bc9e95..53871eec9 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -304,7 +304,7 @@ int cmCPackNSISGenerator::PackageFiles() // Create installation groups first for (auto& group : this->ComponentGroups) { - if (group.second.ParentGroup == nullptr) { + if (!group.second.ParentGroup) { componentCode += this->CreateComponentGroupDescription(&group.second, macrosOut); } diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx index aa99fb648..196aaf161 100644 --- a/Source/CPack/cmCPackNuGetGenerator.cxx +++ b/Source/CPack/cmCPackNuGetGenerator.cxx @@ -92,7 +92,7 @@ void cmCPackNuGetGenerator::SetupGroupComponentVariables(bool ignoreGroup) std::vector components; for (auto const& comp : this->Components) { // Does the component belong to a group? - if (comp.second.Group == nullptr) { + if (!comp.second.Group) { cmCPackLogger( cmCPackLog::LOG_VERBOSE, "Component <" diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx index 4d60c6cee..620819538 100644 --- a/Source/CPack/cmCPackPKGGenerator.cxx +++ b/Source/CPack/cmCPackPKGGenerator.cxx @@ -54,10 +54,10 @@ void cmCPackPKGGenerator::CreateBackground(const char* themeName, cmXMLWriter& xout) { std::string paramSuffix = - (themeName == nullptr) ? "" : cmSystemTools::UpperCase(themeName); - std::string opt = (themeName == nullptr) - ? cmStrCat("CPACK_", genName, "_BACKGROUND") - : cmStrCat("CPACK_", genName, "_BACKGROUND_", paramSuffix); + themeName ? cmSystemTools::UpperCase(themeName) : ""; + std::string opt = themeName + ? cmStrCat("CPACK_", genName, "_BACKGROUND_", paramSuffix) + : cmStrCat("CPACK_", genName, "_BACKGROUND"); cmValue bgFileName = this->GetOption(opt); if (!bgFileName) { return; @@ -73,21 +73,21 @@ void cmCPackPKGGenerator::CreateBackground(const char* themeName, return; } - if (themeName == nullptr) { - xout.StartElement("background"); - } else { + if (themeName) { xout.StartElement(cmStrCat("background-", themeName)); + } else { + xout.StartElement("background"); } xout.Attribute("file", *bgFileName); cmValue param = this->GetOption(cmStrCat(opt, "_ALIGNMENT")); - if (param != nullptr) { + if (param) { xout.Attribute("alignment", *param); } param = this->GetOption(cmStrCat(opt, "_SCALING")); - if (param != nullptr) { + if (param) { xout.Attribute("scaling", *param); } @@ -95,12 +95,12 @@ void cmCPackPKGGenerator::CreateBackground(const char* themeName, // attribute for the background, but I've seen examples that // doesn't have them, so don't make them mandatory. param = this->GetOption(cmStrCat(opt, "_MIME_TYPE")); - if (param != nullptr) { + if (param) { xout.Attribute("mime-type", *param); } param = this->GetOption(cmStrCat(opt, "_UTI")); - if (param != nullptr) { + if (param) { xout.Attribute("uti", *param); } @@ -152,7 +152,7 @@ void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile, // Emit the outline for the groups for (auto const& group : this->ComponentGroups) { - if (group.second.ParentGroup == nullptr) { + if (!group.second.ParentGroup) { CreateChoiceOutline(group.second, xChoiceOut); } } @@ -434,7 +434,7 @@ bool cmCPackPKGGenerator::CopyResourcePlistFile(const std::string& name, } std::string inFName = cmStrCat("CPack.", name, ".in"); - std::string inFileName = this->FindTemplate(inFName.c_str()); + std::string inFileName = this->FindTemplate(inFName); if (inFileName.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: " << inFName << std::endl); diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx index 8dfb1597c..a4f4dacf0 100644 --- a/Source/CPack/cmCPackRPMGenerator.cxx +++ b/Source/CPack/cmCPackRPMGenerator.cxx @@ -171,7 +171,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) for (compIt = this->Components.begin(); compIt != this->Components.end(); ++compIt) { // Does the component belong to a group? - if (compIt->second.Group == nullptr) { + if (!compIt->second.Group) { std::string component(compIt->first); std::transform(component.begin(), component.end(), component.begin(), ::toupper); @@ -237,7 +237,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) for (compIt = this->Components.begin(); compIt != this->Components.end(); ++compIt) { // Does the component belong to a group? - if (compIt->second.Group == nullptr) { + if (!compIt->second.Group) { std::string component(compIt->first); std::transform(component.begin(), component.end(), component.begin(), ::toupper); @@ -325,7 +325,7 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) for (compIt = this->Components.begin(); compIt != this->Components.end(); ++compIt) { // Does the component belong to a group? - if (compIt->second.Group == nullptr) { + if (!compIt->second.Group) { cmCPackLogger( cmCPackLog::LOG_VERBOSE, "Component <" @@ -458,7 +458,7 @@ std::string cmCPackRPMGenerator::GetComponentInstallSuffix( // the current COMPONENT belongs to. std::string groupVar = "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; - if (nullptr != this->GetOption(groupVar)) { + if (this->GetOption(groupVar)) { return *this->GetOption(groupVar); } return componentName; diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 39a8e850c..305ac5629 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -583,7 +583,7 @@ int main(int argc, char const* const* argv) cmValue projVersionPatch = mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH"); std::ostringstream ostr; - ostr << *projVersionMajor << "." << *projVersionMinor << '.' + ostr << *projVersionMajor << '.' << *projVersionMinor << '.' << *projVersionPatch; mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str()); } diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 859798efd..e962cc721 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -1121,10 +1122,10 @@ void cmCTestBuildHandler::ProcessBuffer(const char* data, size_t length, // And if this is verbose output, display the content of the chunk cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(data, length)); + cm::string_view(data, length)); // Always store the chunk to the file - ofs << cmCTestLogWrite(data, length); + ofs << cm::string_view(data, length); } int cmCTestBuildHandler::ProcessSingleLine(const char* data) diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index f9f9addad..cec4a7e59 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -1819,7 +1819,7 @@ int cmCTestCoverageHandler::RunBullseyeCoverageBranch( covLogXML.StartElement("Report"); // write the bullseye header line = 0; - for (int k = 0; bullseyeHelp[k] != nullptr; ++k) { + for (int k = 0; bullseyeHelp[k]; ++k) { covLogXML.StartElement("Line"); covLogXML.Attribute("Number", line); covLogXML.Attribute("Count", -1); diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx index 7137e63aa..b203a51c7 100644 --- a/Source/CTest/cmCTestCurl.cxx +++ b/Source/CTest/cmCTestCurl.cxx @@ -14,6 +14,11 @@ #include "cmSystemTools.h" #include "cmValue.h" +namespace { +const bool TLS_VERIFY_DEFAULT = true; +const int TLS_VERSION_DEFAULT = CURL_SSLVERSION_TLSv1_2; +} + cmCTestCurl::cmCTestCurl(cmCTest* ctest) : CTest(ctest) , CurlOpts(ctest) @@ -61,6 +66,9 @@ cmCTestCurlOpts::cmCTestCurlOpts(cmCTest* ctest) { this->TLSVersionOpt = cmCurlParseTLSVersion(ctest->GetCTestConfiguration("TLSVersion")); + if (!this->TLSVersionOpt.has_value()) { + this->TLSVersionOpt = TLS_VERSION_DEFAULT; + } std::string tlsVerify = ctest->GetCTestConfiguration("TLSVerify"); if (!tlsVerify.empty()) { @@ -76,6 +84,9 @@ cmCTestCurlOpts::cmCTestCurlOpts(cmCTest* ctest) } } } + if (!this->TLSVerifyOpt.has_value()) { + this->TLSVerifyOpt = TLS_VERIFY_DEFAULT; + } } bool cmCTestCurl::InitCurl() @@ -84,11 +95,11 @@ bool cmCTestCurl::InitCurl() return false; } cmCurlSetCAInfo(this->Curl); - if (this->CurlOpts.TLSVersionOpt) { + if (this->CurlOpts.TLSVersionOpt.has_value()) { curl_easy_setopt(this->Curl, CURLOPT_SSLVERSION, *this->CurlOpts.TLSVersionOpt); } - if (this->CurlOpts.TLSVerifyOpt) { + if (this->CurlOpts.TLSVerifyOpt.has_value()) { curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYPEER, *this->CurlOpts.TLSVerifyOpt ? 1 : 0); } diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index b1afdc07b..8596ffaf0 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -68,7 +68,7 @@ public: std::ostringstream ostr; ostr << name << ":\n"; int i = 0; - for (; atts[i] != nullptr; i += 2) { + for (; atts[i]; i += 2) { ostr << " " << atts[i] << " - " << atts[i + 1] << "\n"; } ostr << "\n"; @@ -79,7 +79,7 @@ public: const char* GetAttribute(const char* name, const char** atts) { int i = 0; - for (; atts[i] != nullptr; ++i) { + for (; atts[i]; ++i) { if (strcmp(name, atts[i]) == 0) { return atts[i + 1]; } @@ -282,7 +282,7 @@ void cmCTestMemCheckHandler::InitializeResultsVectors() nullptr }; this->GlobalResults.clear(); - for (int i = 0; cmCTestMemCheckResultStrings[i] != nullptr; ++i) { + for (int i = 0; cmCTestMemCheckResultStrings[i]; ++i) { this->ResultStrings.emplace_back(cmCTestMemCheckResultStrings[i]); this->ResultStringsLong.emplace_back(cmCTestMemCheckResultLongStrings[i]); this->GlobalResults.push_back(0); diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 2003ce668..ed567d448 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -139,10 +139,10 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) { // execute the script passing in the arguments to the script as well as the // arguments from this invocation of cmake - std::vector argv; - argv.push_back(cmSystemTools::GetCTestCommand().c_str()); + std::vector argv; + argv.push_back(cmSystemTools::GetCTestCommand()); argv.push_back("-SR"); - argv.push_back(total_script_arg.c_str()); + argv.push_back(total_script_arg); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Executable for CTest is: " << cmSystemTools::GetCTestCommand() @@ -151,10 +151,14 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) // now pass through all the other arguments std::vector& initArgs = this->CTest->GetInitialCommandLineArguments(); + //*** need to make sure this does not have the current script *** + for (size_t i = 1; i < initArgs.size(); ++i) { + argv.push_back(initArgs[i]); + } // Now create process object cmUVProcessChainBuilder builder; - builder.AddCommand(initArgs) + builder.AddCommand(argv) .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); auto process = builder.Start(); @@ -210,13 +214,10 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) std::ostringstream message; message << "Error running command: ["; message << static_cast(result.first) << "] "; - for (const char* arg : argv) { - if (arg) { - message << arg << " "; - } + for (std::string const& arg : argv) { + message << arg << " "; } - cmCTestLog(this->CTest, ERROR_MESSAGE, - message.str() << argv[0] << std::endl); + cmCTestLog(this->CTest, ERROR_MESSAGE, message.str() << std::endl); return -1; } return retVal; diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h index 8aa07e734..7747750dc 100644 --- a/Source/CTest/cmCTestScriptHandler.h +++ b/Source/CTest/cmCTestScriptHandler.h @@ -67,7 +67,7 @@ public: void AddConfigurationScript(const std::string&, bool pscope); /** - * Run a dashboard using a specified confiuration script + * Run a dashboard using a specified configuration script */ int ProcessHandler() override; diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index fdf3178d3..91dea5507 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -180,7 +181,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( curl = cm_curl_easy_init(); if (curl) { cmCurlSetCAInfo(curl); - if (curlOpts.TLSVersionOpt) { + if (curlOpts.TLSVersionOpt.has_value()) { cm::optional tlsVersionStr = cmCurlPrintTLSVersion(*curlOpts.TLSVersionOpt); cmCTestOptionalLog( @@ -190,7 +191,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( this->Quiet); curl_easy_setopt(curl, CURLOPT_SSLVERSION, *curlOpts.TLSVersionOpt); } - if (curlOpts.TLSVerifyOpt) { + if (curlOpts.TLSVerifyOpt.has_value()) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Set CURLOPT_SSL_VERIFYPEER to " << (*curlOpts.TLSVerifyOpt ? "on" : "off") @@ -360,7 +361,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( if (!chunk.empty()) { cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(chunk.data(), chunk.size()) + << cm::string_view(chunk.data(), chunk.size()) << "]" << std::endl, this->Quiet); this->ParseResponse(chunk); @@ -369,7 +370,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( cmCTestOptionalLog( this->CTest, DEBUG, "CURL debug output: [" - << cmCTestLogWrite(chunkDebug.data(), chunkDebug.size()) << "]" + << cm::string_view(chunkDebug.data(), chunkDebug.size()) << "]" << std::endl, this->Quiet); } @@ -423,7 +424,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( if (!chunk.empty()) { cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(chunk.data(), chunk.size()) + << cm::string_view(chunk.data(), chunk.size()) << "]" << std::endl, this->Quiet); this->ParseResponse(chunk); @@ -451,11 +452,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( // avoid deref of begin for zero size array if (!chunk.empty()) { *this->LogFile << " Curl output was: " - << cmCTestLogWrite(chunk.data(), chunk.size()) + << cm::string_view(chunk.data(), chunk.size()) << std::endl; cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: [" - << cmCTestLogWrite(chunk.data(), chunk.size()) << "]" + << cm::string_view(chunk.data(), chunk.size()) << "]" << std::endl); } ::curl_easy_cleanup(curl); @@ -504,7 +505,7 @@ void cmCTestSubmitHandler::ParseResponse( if (this->HasWarnings || this->HasErrors) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Server Response:\n" - << cmCTestLogWrite(chunk.data(), chunk.size()) << "\n"); + << cm::string_view(chunk.data(), chunk.size()) << "\n"); } } diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 2018b7352..c7875cd99 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -702,13 +702,23 @@ void cmCTestTestHandler::LogFailedTests(const std::vector& failed, if (this->GetTestStatus(ft) == "Not Run") { testColor = cmCTest::Color::YELLOW; } + std::string ft_name_and_status = + cmStrCat(ft.Name, " (", this->GetTestStatus(ft), ")"); + std::string labels; + const cmCTestTestProperties& p = *ft.Properties; + if (!p.Labels.empty()) { + static size_t const maxLen = 50; + size_t const ns = ft_name_and_status.size() >= maxLen + ? 1 + : maxLen - ft_name_and_status.size(); + labels = cmStrCat(std::string(ns, ' '), cmJoin(p.Labels, " ")); + } cmCTestLog( this->CTest, HANDLER_OUTPUT, "\t" << this->CTest->GetColorCode(testColor) << std::setw(3) - << ft.TestCount << " - " << ft.Name << " (" - << this->GetTestStatus(ft) << ")" + << ft.TestCount << " - " << ft_name_and_status << this->CTest->GetColorCode(cmCTest::Color::CLEAR_COLOR) - << std::endl); + << labels << std::endl); } } } diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index dd1bc598b..c35af3fd8 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -65,7 +65,7 @@ public: */ void PopulateCustomVectors(cmMakefile* mf) override; - //! Control the use of the regular expresisons, call these methods to turn + //! Control the use of the regular expressions, call these methods to turn /// them on void UseIncludeRegExp(); void UseExcludeRegExp(); diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt index 5d0e24062..bd7c415fb 100644 --- a/Source/Checks/Curses/CMakeLists.txt +++ b/Source/Checks/Curses/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13...3.29 FATAL_ERROR) project(CheckCurses C) set(CURSES_NEED_NCURSES TRUE) diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 0a03f3af6..0ba0c22b6 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -36,6 +36,8 @@ function(cm_check_cxx_feature name) ) endif() set(check_output "${OUTPUT}") + # Filter out ninja warnings. + string(REGEX REPLACE "[^\n]*ninja: warning: [^\n]*" "" check_output "${check_output}") # Filter out MSBuild output that looks like a warning. string(REGEX REPLACE " +0 Warning\\(s\\)" "" check_output "${check_output}") # Filter out MSBuild output that looks like a warning. diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx index bdc7c6d74..456ec46c8 100644 --- a/Source/Checks/cm_cxx_filesystem.cxx +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -47,7 +47,7 @@ int main() #if defined(__GLIBCXX__) // RH gcc-toolset-10 has a strange bug: it selects, in some circumstances, // the wrong constructor which generate error in template instantiation. - class my_string_view : std::string_view + class my_string_view : public std::string_view { public: my_string_view(const char* p) diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx index 18c1a807f..bc21cd0d1 100644 --- a/Source/CursesDialog/ccmake.cxx +++ b/Source/CursesDialog/ccmake.cxx @@ -115,7 +115,7 @@ int main(int argc, char const* const* argv) cmCursesForm::DebugStart(); } - if (initscr() == nullptr) { + if (!initscr()) { fprintf(stderr, "Error: ncurses initialization failed\n"); exit(1); } diff --git a/Source/CursesDialog/cmCursesForm.cxx b/Source/CursesDialog/cmCursesForm.cxx index ef36b4580..900fbca4d 100644 --- a/Source/CursesDialog/cmCursesForm.cxx +++ b/Source/CursesDialog/cmCursesForm.cxx @@ -52,7 +52,7 @@ void cmCursesForm::LogMessage(const char* msg) void cmCursesForm::HandleResize() { endwin(); - if (initscr() == nullptr) { + if (!initscr()) { static const char errmsg[] = "Error: ncurses initialization failed\n"; #ifdef _WIN32 fprintf(stderr, "%s", errmsg); diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index 72460f3b1..81bf0b39c 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -307,7 +307,7 @@ void cmCursesMainForm::PrintKeys(int process /* = 0 */) } char fmt_s[] = "%s"; - if (cw == nullptr || !cw->PrintKeys()) { + if (!cw || !cw->PrintKeys()) { char firstLine[512] = ""; char secondLine[512] = ""; char thirdLine[512] = ""; @@ -605,7 +605,7 @@ void cmCursesMainForm::RemoveEntry(const char* value) std::find_if(this->Entries.begin(), this->Entries.end(), [value](cmCursesCacheEntryComposite& entry) -> bool { const char* val = entry.GetValue(); - return val != nullptr && !strcmp(value, val); + return val && !strcmp(value, val); }); if (removeIt != this->Entries.end()) { diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake index 4e7f0fe87..09d8250cf 100644 --- a/Source/Modules/CMakeBuildUtilities.cmake +++ b/Source/Modules/CMakeBuildUtilities.cmake @@ -150,7 +150,7 @@ else() 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)") + AND CMAKE_SYSTEM_NAME MATCHES "(Linux|FreeBSD|CYGWIN|MSYS)") set(_CMAKE_USE_OPENSSL_DEFAULT ON) endif() option(CMAKE_USE_OPENSSL "Use OpenSSL." ${_CMAKE_USE_OPENSSL_DEFAULT}) @@ -260,6 +260,7 @@ if(CMAKE_USE_SYSTEM_LIBARCHIVE) INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIRS}") endif () else() + set(DONT_FAIL_ON_CRC_ERROR OFF) set(EXPAT_INCLUDE_DIR ${CMAKE_EXPAT_INCLUDES}) set(EXPAT_LIBRARY ${CMAKE_EXPAT_LIBRARIES}) set(ENABLE_MBEDTLS OFF) @@ -279,6 +280,7 @@ else() set(ENABLE_LIBXML2 OFF) set(ENABLE_EXPAT OFF) set(ENABLE_PCREPOSIX OFF) + set(ENABLE_PCRE2POSIX OFF) set(ENABLE_LIBGCC OFF) set(ENABLE_CNG OFF) set(ENABLE_TAR OFF) @@ -394,3 +396,9 @@ if(CMake_ENABLE_DEBUGGER) CMAKE_SET_TARGET_FOLDER(cppdap "Utilities/3rdParty") endif() endif() + +#--------------------------------------------------------------------- +# Build llpkgc library. +add_subdirectory(Utilities/cmllpkgc) +add_library(llpkgc::llpkgc ALIAS cmllpkgc) +CMAKE_SET_TARGET_FOLDER(cmllpkgc "Utilities/3rdParty") diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index d94301160..885094d3e 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -2,11 +2,15 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmAddCustomCommandCommand.h" +#include +#include +#include #include #include #include #include +#include #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" @@ -53,6 +57,7 @@ bool cmAddCustomCommandCommand(std::vector const& args, bool command_expand_lists = false; bool depends_explicit_only = mf.IsOn("CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY"); + bool codegen = false; std::string implicit_depends_lang; cmImplicitDependsList implicit_depends; @@ -111,6 +116,7 @@ bool cmAddCustomCommandCommand(std::vector const& args, MAKE_STATIC_KEYWORD(VERBATIM); MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY); + MAKE_STATIC_KEYWORD(CODEGEN); #undef MAKE_STATIC_KEYWORD static std::unordered_set const keywords{ keyAPPEND, @@ -135,13 +141,85 @@ bool cmAddCustomCommandCommand(std::vector const& args, keyUSES_TERMINAL, keyVERBATIM, keyWORKING_DIRECTORY, - keyDEPENDS_EXPLICIT_ONLY + keyDEPENDS_EXPLICIT_ONLY, + keyCODEGEN }; + /* clang-format off */ + static std::set const supportedTargetKeywords{ + keyARGS, + keyBYPRODUCTS, + keyCOMMAND, + keyCOMMAND_EXPAND_LISTS, + keyCOMMENT, + keyPOST_BUILD, + keyPRE_BUILD, + keyPRE_LINK, + keyTARGET, + keyVERBATIM, + keyWORKING_DIRECTORY + }; + /* clang-format on */ + static std::set const supportedOutputKeywords{ + keyAPPEND, + keyARGS, + keyBYPRODUCTS, + keyCODEGEN, + keyCOMMAND, + keyCOMMAND_EXPAND_LISTS, + keyCOMMENT, + keyDEPENDS, + keyDEPENDS_EXPLICIT_ONLY, + keyDEPFILE, + keyIMPLICIT_DEPENDS, + keyJOB_POOL, + keyJOB_SERVER_AWARE, + keyMAIN_DEPENDENCY, + keyOUTPUT, + keyUSES_TERMINAL, + keyVERBATIM, + keyWORKING_DIRECTORY + }; + /* clang-format off */ + static std::set const supportedAppendKeywords{ + keyAPPEND, + keyARGS, + keyCOMMAND, + keyCOMMENT, // Allowed but ignored + keyDEPENDS, + keyIMPLICIT_DEPENDS, + keyMAIN_DEPENDENCY, // Allowed but ignored + keyOUTPUT, + keyWORKING_DIRECTORY // Allowed but ignored + }; + /* clang-format on */ + std::set keywordsSeen; + std::string const* keywordExpectingValue = nullptr; + auto const cmp0175 = mf.GetPolicyStatus(cmPolicies::CMP0175); for (std::string const& copy : args) { if (keywords.count(copy)) { + // Check if a preceding keyword expected a value but there wasn't one + if (keywordExpectingValue) { + std::string const msg = + cmStrCat("Keyword ", *keywordExpectingValue, + " requires a value, but none was given."); + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, '\n', + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } + keywordExpectingValue = nullptr; + keywordsSeen.insert(copy); + if (copy == keySOURCE) { doing = doing_source; + keywordExpectingValue = &keySOURCE; } else if (copy == keyCOMMAND) { doing = doing_command; @@ -166,8 +244,11 @@ bool cmAddCustomCommandCommand(std::vector const& args, command_expand_lists = true; } else if (copy == keyDEPENDS_EXPLICIT_ONLY) { depends_explicit_only = true; + } else if (copy == keyCODEGEN) { + codegen = true; } else if (copy == keyTARGET) { doing = doing_target; + keywordExpectingValue = &keyTARGET; } else if (copy == keyARGS) { // Ignore this old keyword. } else if (copy == keyDEPENDS) { @@ -176,16 +257,20 @@ bool cmAddCustomCommandCommand(std::vector const& args, doing = doing_outputs; } else if (copy == keyOUTPUT) { doing = doing_output; + keywordExpectingValue = &keyOUTPUT; } else if (copy == keyBYPRODUCTS) { doing = doing_byproducts; } else if (copy == keyWORKING_DIRECTORY) { doing = doing_working_directory; + keywordExpectingValue = &keyWORKING_DIRECTORY; } else if (copy == keyMAIN_DEPENDENCY) { doing = doing_main_dependency; + keywordExpectingValue = &keyMAIN_DEPENDENCY; } else if (copy == keyIMPLICIT_DEPENDS) { doing = doing_implicit_depends_lang; } else if (copy == keyCOMMENT) { doing = doing_comment; + keywordExpectingValue = &keyCOMMENT; } else if (copy == keyDEPFILE) { doing = doing_depfile; if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) { @@ -193,12 +278,16 @@ bool cmAddCustomCommandCommand(std::vector const& args, mf.GetGlobalGenerator()->GetName())); return false; } + keywordExpectingValue = &keyDEPFILE; } else if (copy == keyJOB_POOL) { doing = doing_job_pool; + keywordExpectingValue = &keyJOB_POOL; } else if (copy == keyJOB_SERVER_AWARE) { doing = doing_job_server_aware; + keywordExpectingValue = &keyJOB_SERVER_AWARE; } } else { + keywordExpectingValue = nullptr; // Value is being processed now std::string filename; switch (doing) { case doing_output: @@ -283,6 +372,21 @@ bool cmAddCustomCommandCommand(std::vector const& args, byproducts.push_back(filename); break; case doing_comment: + if (!comment_buffer.empty()) { + std::string const msg = + "COMMENT requires exactly one argument, but multiple values " + "or COMMENT keywords have been given."; + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, '\n', + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } comment_buffer = copy; comment = comment_buffer.c_str(); break; @@ -322,8 +426,51 @@ bool cmAddCustomCommandCommand(std::vector const& args, return false; } + if (codegen) { + if (output.empty()) { + status.SetError("CODEGEN requires at least 1 OUTPUT."); + return false; + } + + if (append) { + status.SetError("CODEGEN may not be used with APPEND."); + return false; + } + + if (!implicit_depends.empty()) { + status.SetError("CODEGEN is not compatible with IMPLICIT_DEPENDS."); + return false; + } + + if (mf.GetPolicyStatus(cmPolicies::CMP0171) != cmPolicies::NEW) { + status.SetError("CODEGEN option requires policy CMP0171 be set to NEW!"); + return false; + } + } + // Check for an append request. if (append) { + std::vector unsupportedKeywordsUsed; + std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), + supportedAppendKeywords.begin(), + supportedAppendKeywords.end(), + std::back_inserter(unsupportedKeywordsUsed)); + if (!unsupportedKeywordsUsed.empty()) { + std::string const msg = + cmJoin(unsupportedKeywordsUsed, ", "_s, + "The following keywords are not supported when using " + "APPEND with add_custom_command(OUTPUT): "_s); + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, ".\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, commandLines); return true; @@ -349,12 +496,96 @@ bool cmAddCustomCommandCommand(std::vector const& args, cc->SetDependsExplicitOnly(depends_explicit_only); if (source.empty() && output.empty()) { // Source is empty, use the target. + if (commandLines.empty()) { + std::string const msg = "At least one COMMAND must be given."; + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, '\n', + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } + + std::vector unsupportedKeywordsUsed; + std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), + supportedTargetKeywords.begin(), + supportedTargetKeywords.end(), + std::back_inserter(unsupportedKeywordsUsed)); + if (!unsupportedKeywordsUsed.empty()) { + std::string const msg = + cmJoin(unsupportedKeywordsUsed, ", "_s, + "The following keywords are not supported when using " + "add_custom_command(TARGET): "_s); + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, ".\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } + auto const prePostCount = keywordsSeen.count(keyPRE_BUILD) + + keywordsSeen.count(keyPRE_LINK) + keywordsSeen.count(keyPOST_BUILD); + if (prePostCount != 1) { + std::string msg = + "Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given."; + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + msg += " Assuming "; + switch (cctype) { + case cmCustomCommandType::PRE_BUILD: + msg += "PRE_BUILD"; + break; + case cmCustomCommandType::PRE_LINK: + msg += "PRE_LINK"; + break; + case cmCustomCommandType::POST_BUILD: + msg += "POST_BUILD"; + } + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, " to preserve backward compatibility.\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } mf.AddCustomCommandToTarget(target, cctype, std::move(cc)); } else if (target.empty()) { // Target is empty, use the output. + std::vector unsupportedKeywordsUsed; + std::set_difference(keywordsSeen.begin(), keywordsSeen.end(), + supportedOutputKeywords.begin(), + supportedOutputKeywords.end(), + std::back_inserter(unsupportedKeywordsUsed)); + if (!unsupportedKeywordsUsed.empty()) { + std::string const msg = + cmJoin(unsupportedKeywordsUsed, ", "_s, + "The following keywords are not supported when using " + "add_custom_command(OUTPUT): "_s); + if (cmp0175 == cmPolicies::NEW) { + mf.IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + if (cmp0175 == cmPolicies::WARN) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(msg, ".\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0175))); + } + } cc->SetOutputs(output); cc->SetMainDependency(main_dependency); cc->SetDepends(depends); + cc->SetCodegen(codegen); cc->SetImplicitDepends(implicit_depends); mf.AddCustomCommandToOutput(std::move(cc)); } else { diff --git a/Source/cmAddDependenciesCommand.cxx b/Source/cmAddDependenciesCommand.cxx index 2d55a5a4b..aa0401897 100644 --- a/Source/cmAddDependenciesCommand.cxx +++ b/Source/cmAddDependenciesCommand.cxx @@ -30,6 +30,7 @@ bool cmAddDependenciesCommand(std::vector const& args, // skip over target_name for (std::string const& arg : cmMakeRange(args).advance(1)) { target->AddUtility(arg, false, &mf); + target->AddCodegenDependency(arg); } } else { mf.IssueMessage( diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx index a0d5732fd..ff17e87bc 100644 --- a/Source/cmAddTestCommand.cxx +++ b/Source/cmAddTestCommand.cxx @@ -2,14 +2,19 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmAddTestCommand.h" +#include + #include #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmTest.h" #include "cmTestGenerator.h" +static std::string const keywordCMP0178 = "__CMP0178"; + static bool cmAddTestCommandHandleNameMode( std::vector const& args, cmExecutionStatus& status); @@ -29,8 +34,30 @@ bool cmAddTestCommand(std::vector const& args, } cmMakefile& mf = status.GetMakefile(); + cmPolicies::PolicyStatus cmp0178; + + // If the __CMP0178 keyword is present, it is always at the end + auto endOfCommandIter = + std::find(args.begin() + 2, args.end(), keywordCMP0178); + if (endOfCommandIter != args.end()) { + auto cmp0178Iter = endOfCommandIter + 1; + if (cmp0178Iter == args.end()) { + status.SetError(cmStrCat(keywordCMP0178, " keyword missing value")); + return false; + } + if (*cmp0178Iter == "NEW") { + cmp0178 = cmPolicies::PolicyStatus::NEW; + } else if (*cmp0178Iter == "OLD") { + cmp0178 = cmPolicies::PolicyStatus::OLD; + } else { + cmp0178 = cmPolicies::PolicyStatus::WARN; + } + } else { + cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178); + } + // Collect the command with arguments. - std::vector command(args.begin() + 1, args.end()); + std::vector command(args.begin() + 1, endOfCommandIter); // Create the test but add a generator only the first time it is // seen. This preserves behavior from before test generators. @@ -46,6 +73,7 @@ bool cmAddTestCommand(std::vector const& args, } else { test = mf.CreateTest(args[0]); test->SetOldStyle(true); + test->SetCMP0178(cmp0178); mf.AddTestGenerator(cm::make_unique(test)); } test->SetCommand(command); @@ -56,11 +84,14 @@ bool cmAddTestCommand(std::vector const& args, bool cmAddTestCommandHandleNameMode(std::vector const& args, cmExecutionStatus& status) { + cmMakefile& mf = status.GetMakefile(); + std::string name; std::vector configurations; std::string working_directory; std::vector command; bool command_expand_lists = false; + cmPolicies::PolicyStatus cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178); // Read the arguments. enum Doing @@ -69,6 +100,7 @@ bool cmAddTestCommandHandleNameMode(std::vector const& args, DoingCommand, DoingConfigs, DoingWorkingDirectory, + DoingCmp0178, DoingNone }; Doing doing = DoingName; @@ -91,6 +123,8 @@ bool cmAddTestCommandHandleNameMode(std::vector const& args, return false; } doing = DoingWorkingDirectory; + } else if (args[i] == keywordCMP0178) { + doing = DoingCmp0178; } else if (args[i] == "COMMAND_EXPAND_LISTS") { if (command_expand_lists) { status.SetError(" may be given at most one COMMAND_EXPAND_LISTS."); @@ -108,6 +142,15 @@ bool cmAddTestCommandHandleNameMode(std::vector const& args, } else if (doing == DoingWorkingDirectory) { working_directory = args[i]; doing = DoingNone; + } else if (doing == DoingCmp0178) { + if (args[i] == "NEW") { + cmp0178 = cmPolicies::PolicyStatus::NEW; + } else if (args[i] == "OLD") { + cmp0178 = cmPolicies::PolicyStatus::OLD; + } else { + cmp0178 = cmPolicies::PolicyStatus::WARN; + } + doing = DoingNone; } else { status.SetError(cmStrCat(" given unknown argument:\n ", args[i], "\n")); return false; @@ -126,8 +169,6 @@ bool cmAddTestCommandHandleNameMode(std::vector const& args, return false; } - cmMakefile& mf = status.GetMakefile(); - // Require a unique test name within the directory. if (mf.GetTest(name)) { status.SetError(cmStrCat(" given test NAME \"", name, @@ -138,6 +179,7 @@ bool cmAddTestCommandHandleNameMode(std::vector const& args, // Add the test. cmTest* test = mf.CreateTest(name); test->SetOldStyle(false); + test->SetCMP0178(cmp0178); test->SetCommand(command); if (!working_directory.empty()) { test->SetProperty("WORKING_DIRECTORY", working_directory); diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index ad57a88a5..22dd30834 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -167,7 +167,7 @@ void Instance::Consume(std::size_t pos, cm::string_view arg) } } - if (this->UnparsedArguments != nullptr) { + if (this->UnparsedArguments) { this->UnparsedArguments->emplace_back(arg); } } @@ -178,7 +178,7 @@ void Instance::FinishKeyword() return; } if (this->KeywordValuesSeen < this->KeywordValuesExpected) { - if (this->ParseResults != nullptr) { + if (this->ParseResults) { this->ParseResults->AddKeywordError(this->Keyword, " missing required value\n"); } diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index b35d7f3bc..c479b27e5 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "cmArgumentParserTypes.h" // IWYU pragma: keep @@ -244,8 +245,11 @@ class cmArgumentParser : private ArgumentParser::Base public: // I *think* this function could be made `constexpr` when the code is // compiled as C++20. This would allow building a parser at compile time. - template - cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) + template , + typename mT = cm::remove_member_pointer_t, + typename = cm::enable_if_t::value>, + typename = cm::enable_if_t::value>> + cmArgumentParser& Bind(cm::static_string_view name, T member) { this->Base::Bind(name, [member](Instance& instance) { instance.Bind(static_cast(instance.Result)->*member); diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx index e2a8f9210..ca4072633 100644 --- a/Source/cmBinUtilsLinuxELFLinker.cxx +++ b/Source/cmBinUtilsLinuxELFLinker.cxx @@ -3,7 +3,10 @@ #include "cmBinUtilsLinuxELFLinker.h" +#include #include +#include +#include #include #include @@ -89,8 +92,6 @@ bool cmBinUtilsLinuxELFLinker::Prepare() bool cmBinUtilsLinuxELFLinker::ScanDependencies( std::string const& file, cmStateEnums::TargetType /* unused */) { - std::vector parentRpaths; - cmELF elf(file.c_str()); if (!elf) { return false; @@ -106,40 +107,53 @@ bool cmBinUtilsLinuxELFLinker::ScanDependencies( } } - return this->ScanDependencies(file, parentRpaths); + return this->ScanDependencies(file); } -bool cmBinUtilsLinuxELFLinker::ScanDependencies( - std::string const& file, std::vector const& parentRpaths) +bool cmBinUtilsLinuxELFLinker::ScanDependencies(std::string const& mainFile) { - std::string origin = cmSystemTools::GetFilenamePath(file); - std::vector needed; - std::vector rpaths; - std::vector runpaths; - if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) { - return false; - } - for (auto& runpath : runpaths) { - runpath = ReplaceOrigin(runpath, origin); - } - for (auto& rpath : rpaths) { - rpath = ReplaceOrigin(rpath, origin); - } + std::unordered_set resolvedDependencies; + std::queue>> queueToResolve; + queueToResolve.push(std::make_pair(mainFile, std::vector{})); + + while (!queueToResolve.empty()) { + std::string file = std::move(queueToResolve.front().first); + std::vector parentRpaths = + std::move(queueToResolve.front().second); + queueToResolve.pop(); + + std::string origin = cmSystemTools::GetFilenamePath(file); + std::vector needed; + std::vector rpaths; + std::vector runpaths; + if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) { + return false; + } + for (auto& runpath : runpaths) { + runpath = ReplaceOrigin(runpath, origin); + } + for (auto& rpath : rpaths) { + rpath = ReplaceOrigin(rpath, origin); + } - std::vector searchPaths; - if (!runpaths.empty()) { - searchPaths = runpaths; - } else { - searchPaths = rpaths; - searchPaths.insert(searchPaths.end(), parentRpaths.begin(), - parentRpaths.end()); - } + std::vector searchPaths; + if (!runpaths.empty()) { + searchPaths = runpaths; + } else { + searchPaths = rpaths; + searchPaths.insert(searchPaths.end(), parentRpaths.begin(), + parentRpaths.end()); + } + + searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(), + this->LDConfigPaths.end()); - searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(), - this->LDConfigPaths.end()); + for (auto const& dep : needed) { + if (resolvedDependencies.count(dep) != 0 || + this->Archive->IsPreExcluded(dep)) { + continue; + } - for (auto const& dep : needed) { - if (!this->Archive->IsPreExcluded(dep)) { std::string path; bool resolved = false; if (dep.find('/') != std::string::npos) { @@ -150,6 +164,7 @@ bool cmBinUtilsLinuxELFLinker::ScanDependencies( return false; } if (resolved) { + resolvedDependencies.emplace(dep); if (!this->Archive->IsPostExcluded(path)) { bool unique; this->Archive->AddResolvedPath(dep, path, unique); @@ -157,9 +172,8 @@ bool cmBinUtilsLinuxELFLinker::ScanDependencies( std::vector combinedParentRpaths = parentRpaths; combinedParentRpaths.insert(combinedParentRpaths.end(), rpaths.begin(), rpaths.end()); - if (!this->ScanDependencies(path, combinedParentRpaths)) { - return false; - } + + queueToResolve.push(std::make_pair(path, combinedParentRpaths)); } } } else { diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h index 395ed567b..0b0d8b5e6 100644 --- a/Source/cmBinUtilsLinuxELFLinker.h +++ b/Source/cmBinUtilsLinuxELFLinker.h @@ -34,8 +34,7 @@ private: std::vector LDConfigPaths; std::uint16_t Machine = 0; - bool ScanDependencies(std::string const& file, - std::vector const& parentRpaths); + bool ScanDependencies(std::string const& mainFile); bool ResolveDependency(std::string const& name, std::vector const& searchPaths, diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx index 7db7cb019..082f3488a 100644 --- a/Source/cmBinUtilsMacOSMachOLinker.cxx +++ b/Source/cmBinUtilsMacOSMachOLinker.cxx @@ -87,7 +87,7 @@ bool cmBinUtilsMacOSMachOLinker::ScanDependencies( executablePath = cmSystemTools::GetFilenamePath(executableFile); } const FileInfo* file_info = this->GetFileInfo(file); - if (file_info == nullptr) { + if (!file_info) { return false; } return this->ScanDependencies(file, file_info->libs, file_info->rpaths, @@ -120,7 +120,7 @@ bool cmBinUtilsMacOSMachOLinker::GetFileDependencies( auto filename = cmSystemTools::GetFilenameName(path); bool unique; const FileInfo* dep_file_info = this->GetFileInfo(path); - if (dep_file_info == nullptr) { + if (!dep_file_info) { return false; } diff --git a/Source/cmBuildDatabase.cxx b/Source/cmBuildDatabase.cxx new file mode 100644 index 000000000..dc5f9067f --- /dev/null +++ b/Source/cmBuildDatabase.cxx @@ -0,0 +1,581 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmBuildDatabase.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "cmsys/FStream.hxx" + +#include "cmComputeLinkInformation.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmSourceFile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { + +std::string PlaceholderName = "<__CMAKE_UNKNOWN>"; + +} + +cmBuildDatabase::cmBuildDatabase() = default; +cmBuildDatabase::cmBuildDatabase(cmBuildDatabase const&) = default; +cmBuildDatabase::~cmBuildDatabase() = default; + +cmBuildDatabase::LookupTable cmBuildDatabase::GenerateLookupTable() +{ + LookupTable lut; + + for (auto& Set_ : this->Sets) { + for (auto& TranslationUnit_ : Set_.TranslationUnits) { + // This table is from source path to TU instance. This is fine because a + // single target (where this is used) cannot contain the same source file + // multiple times. + lut[TranslationUnit_.Source] = &TranslationUnit_; + } + } + + return lut; +} + +bool cmBuildDatabase::HasPlaceholderNames() const +{ + for (auto const& Set_ : this->Sets) { + for (auto const& TranslationUnit_ : Set_.TranslationUnits) { + for (auto const& provide : TranslationUnit_.Provides) { + if (provide.first == PlaceholderName) { + return true; + } + if (provide.second == PlaceholderName) { + return true; + } + } + } + } + + return false; +} + +void cmBuildDatabase::Write(std::string const& path) const +{ + Json::Value mcdb = Json::objectValue; + + mcdb["version"] = 1; + mcdb["revision"] = 0; + + Json::Value& sets = mcdb["sets"] = Json::arrayValue; + + for (auto const& Set_ : this->Sets) { + Json::Value set = Json::objectValue; + + set["name"] = Set_.Name; + set["family-name"] = Set_.FamilyName; + + Json::Value& visible_sets = set["visible-sets"] = Json::arrayValue; + for (auto const& VisibleSet : Set_.VisibleSets) { + visible_sets.append(VisibleSet); + } + + Json::Value& tus = set["translation-units"] = Json::arrayValue; + for (auto const& TranslationUnit_ : Set_.TranslationUnits) { + Json::Value tu = Json::objectValue; + + if (!TranslationUnit_.WorkDirectory.empty()) { + tu["work-directory"] = TranslationUnit_.WorkDirectory; + } + tu["source"] = TranslationUnit_.Source; + if (TranslationUnit_.Object) { + tu["object"] = *TranslationUnit_.Object; + } + tu["private"] = TranslationUnit_.Private; + + Json::Value& reqs = tu["requires"] = Json::arrayValue; + for (auto const& Require : TranslationUnit_.Requires) { + reqs.append(Require); + } + + Json::Value& provides = tu["provides"] = Json::objectValue; + for (auto const& Provide : TranslationUnit_.Provides) { + provides[Provide.first] = Provide.second; + } + + Json::Value& baseline_arguments = tu["baseline-arguments"] = + Json::arrayValue; + for (auto const& BaselineArgument : TranslationUnit_.BaselineArguments) { + baseline_arguments.append(BaselineArgument); + } + + Json::Value& local_arguments = tu["local-arguments"] = Json::arrayValue; + for (auto const& LocalArgument : TranslationUnit_.LocalArguments) { + local_arguments.append(LocalArgument); + } + + Json::Value& arguments = tu["arguments"] = Json::arrayValue; + for (auto const& Argument : TranslationUnit_.Arguments) { + arguments.append(Argument); + } + + tus.append(tu); + } + + sets.append(set); + } + + cmGeneratedFileStream mcdbf(path); + mcdbf << mcdb; +} + +static bool ParseFilename(Json::Value const& val, std::string& result) +{ + if (val.isString()) { + result = val.asString(); + } else { + return false; + } + + return true; +} + +#define PARSE_BLOB(val, res) \ + do { \ + if (!ParseFilename(val, res)) { \ + cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \ + "parse ", path, ": invalid blob")); \ + return {}; \ + } \ + } while (0) + +#define PARSE_FILENAME(val, res, make_full) \ + do { \ + if (!ParseFilename(val, res)) { \ + cmSystemTools::Error(cmStrCat("-E cmake_module_compile_db failed to ", \ + "parse ", path, ": invalid filename")); \ + return {}; \ + } \ + \ + if (make_full && work_directory && !work_directory->empty() && \ + !cmSystemTools::FileIsFullPath(res)) { \ + res = cmStrCat(*work_directory, '/', res); \ + } \ + } while (0) + +std::unique_ptr cmBuildDatabase::Load(std::string const& path) +{ + Json::Value mcdb; + { + cmsys::ifstream mcdbf(path.c_str(), std::ios::in | std::ios::binary); + Json::Reader reader; + if (!reader.parse(mcdbf, mcdb, false)) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + reader.getFormattedErrorMessages())); + return {}; + } + } + + Json::Value const& version = mcdb["version"]; + if (version.asUInt() > 1) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": version ", version.asString())); + return {}; + } + + auto db = cm::make_unique(); + + Json::Value const& sets = mcdb["sets"]; + if (sets.isArray()) { + for (auto const& set : sets) { + Set Set_; + + Json::Value const& name = set["name"]; + if (!name.isString()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": name is not a string")); + return {}; + } + Set_.Name = name.asString(); + + Json::Value const& family_name = set["family-name"]; + if (!family_name.isString()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": family-name is not a string")); + return {}; + } + Set_.FamilyName = family_name.asString(); + + Json::Value const& visible_sets = set["visible-sets"]; + if (!visible_sets.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": visible-sets is not an array")); + return {}; + } + for (auto const& visible_set : visible_sets) { + if (!visible_set.isString()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a visible-sets item is not a string")); + return {}; + } + + Set_.VisibleSets.emplace_back(visible_set.asString()); + } + + Json::Value const& translation_units = set["translation-units"]; + if (!translation_units.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": translation-units is not an array")); + return {}; + } + for (auto const& translation_unit : translation_units) { + if (!translation_unit.isObject()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a translation-units item is not an object")); + return {}; + } + + TranslationUnit TranslationUnit_; + + cm::optional work_directory; + Json::Value const& workdir = translation_unit["work-directory"]; + if (workdir.isString()) { + PARSE_BLOB(workdir, TranslationUnit_.WorkDirectory); + work_directory = TranslationUnit_.WorkDirectory; + } else if (!workdir.isNull()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": work-directory is not a string")); + return {}; + } + + Json::Value const& source = translation_unit["source"]; + PARSE_FILENAME(source, TranslationUnit_.Source, true); + + if (translation_unit.isMember("object")) { + Json::Value const& object = translation_unit["object"]; + if (!object.isNull()) { + TranslationUnit_.Object = ""; + PARSE_FILENAME(object, *TranslationUnit_.Object, false); + } + } + + if (translation_unit.isMember("private")) { + Json::Value const& priv = translation_unit["private"]; + if (!priv.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": private is not a boolean")); + return {}; + } + TranslationUnit_.Private = priv.asBool(); + } + + if (translation_unit.isMember("requires")) { + Json::Value const& reqs = translation_unit["requires"]; + if (!reqs.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": requires is not an array")); + return {}; + } + + for (auto const& require : reqs) { + if (!require.isString()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a requires item is not a string")); + return {}; + } + + TranslationUnit_.Requires.emplace_back(require.asString()); + } + } + + if (translation_unit.isMember("provides")) { + Json::Value const& provides = translation_unit["provides"]; + if (!provides.isObject()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": provides is not an object")); + return {}; + } + + for (auto i = provides.begin(); i != provides.end(); ++i) { + if (!i->isString()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a provides value is not a string")); + return {}; + } + + TranslationUnit_.Provides[i.key().asString()] = i->asString(); + } + } + + if (translation_unit.isMember("baseline-arguments")) { + Json::Value const& baseline_arguments = + translation_unit["baseline-arguments"]; + if (!baseline_arguments.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": baseline_arguments is not an array")); + return {}; + } + + for (auto const& baseline_argument : baseline_arguments) { + if (baseline_argument.isString()) { + TranslationUnit_.BaselineArguments.emplace_back( + baseline_argument.asString()); + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a baseline argument is not a string")); + return {}; + } + } + } + + if (translation_unit.isMember("local-arguments")) { + Json::Value const& local_arguments = + translation_unit["local-arguments"]; + if (!local_arguments.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": local_arguments is not an array")); + return {}; + } + + for (auto const& local_argument : local_arguments) { + if (local_argument.isString()) { + TranslationUnit_.LocalArguments.emplace_back( + local_argument.asString()); + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": a local argument is not a string")); + return {}; + } + } + } + + if (translation_unit.isMember("arguments")) { + Json::Value const& arguments = translation_unit["arguments"]; + if (!arguments.isArray()) { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": arguments is not an array")); + return {}; + } + + for (auto const& argument : arguments) { + if (argument.isString()) { + TranslationUnit_.Arguments.emplace_back(argument.asString()); + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db failed to parse ", path, + ": an argument is not a string")); + return {}; + } + } + } + + Set_.TranslationUnits.emplace_back(std::move(TranslationUnit_)); + } + + db->Sets.emplace_back(std::move(Set_)); + } + } + + return db; +} + +cmBuildDatabase cmBuildDatabase::Merge( + std::vector const& components) +{ + cmBuildDatabase db; + + for (auto const& component : components) { + db.Sets.insert(db.Sets.end(), component.Sets.begin(), + component.Sets.end()); + } + + return db; +} + +cmBuildDatabase cmBuildDatabase::ForTarget(cmGeneratorTarget* gt, + std::string const& config) +{ + cmBuildDatabase db; + + Set set; + set.Name = cmStrCat(gt->GetName(), '@', config); + set.FamilyName = gt->GetFamilyName(); + if (auto* cli = gt->GetLinkInformation(config)) { + std::set emitted; + std::vector targets; + for (auto const& item : cli->GetItems()) { + auto const* linkee = item.Target; + if (linkee && linkee->HaveCxx20ModuleSources() && + !linkee->IsImported() && emitted.insert(linkee).second) { + set.VisibleSets.push_back(cmStrCat(linkee->GetName(), '@', config)); + } + } + } + + for (auto const& sfbt : gt->GetSourceFiles(config)) { + auto const* sf = sfbt.Value; + + bool isCXXModule = false; + bool isPrivate = true; + if (sf->GetLanguage() != "CXX"_s) { + auto const* fs = gt->GetFileSetForSource(config, sf); + if (fs && fs->GetType() == "CXX_MODULES"_s) { + isCXXModule = true; + isPrivate = !cmFileSetVisibilityIsForInterface(fs->GetVisibility()); + } + } + + TranslationUnit tu; + + // FIXME: Makefiles will want this to be the current working directory. + tu.WorkDirectory = gt->GetLocalGenerator()->GetBinaryDirectory(); + tu.Source = sf->GetFullPath(); + if (!gt->IsSynthetic()) { + auto* gg = gt->GetGlobalGenerator(); + std::string const objectDir = gg->ConvertToOutputPath( + cmStrCat(gt->GetSupportDirectory(), gg->GetConfigDirectory(config))); + std::string const objectFileName = gt->GetObjectName(sf); + tu.Object = cmStrCat(objectDir, '/', objectFileName); + } + if (isCXXModule) { + tu.Provides[PlaceholderName] = PlaceholderName; + } + + cmGeneratorTarget::ClassifiedFlags classifiedFlags = + gt->GetClassifiedFlagsForSource(sf, config); + for (auto const& classifiedFlag : classifiedFlags) { + if (classifiedFlag.Classification == + cmGeneratorTarget::FlagClassification::BaselineFlag) { + tu.BaselineArguments.push_back(classifiedFlag.Flag); + tu.LocalArguments.push_back(classifiedFlag.Flag); + } else if (classifiedFlag.Classification == + cmGeneratorTarget::FlagClassification::PrivateFlag) { + tu.LocalArguments.push_back(classifiedFlag.Flag); + } + tu.Arguments.push_back(classifiedFlag.Flag); + } + tu.Private = isPrivate; + + set.TranslationUnits.emplace_back(std::move(tu)); + } + + db.Sets.emplace_back(std::move(set)); + + return db; +} + +int cmcmd_cmake_module_compile_db( + std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd) +{ + const std::string* command = nullptr; + const std::string* output = nullptr; + std::vector inputs; + + bool next_is_output = false; + for (auto i = argBeg; i != argEnd; ++i) { + // The first argument is always the command. + if (!command) { + command = &(*i); + continue; + } + + if (*i == "-o"_s) { + next_is_output = true; + continue; + } + if (next_is_output) { + if (output) { + cmSystemTools::Error( + "-E cmake_module_compile_db only supports one output file"); + return EXIT_FAILURE; + } + + output = &(*i); + next_is_output = false; + continue; + } + + inputs.emplace_back(&(*i)); + } + + if (!command) { + cmSystemTools::Error("-E cmake_module_compile_db requires a subcommand"); + return EXIT_FAILURE; + } + + int ret = EXIT_SUCCESS; + + if (*command == "verify"_s) { + if (output) { + cmSystemTools::Error( + "-E cmake_module_compile_db verify does not support an output"); + return EXIT_FAILURE; + } + + for (auto const* i : inputs) { + auto db = cmBuildDatabase::Load(*i); + if (!db) { + cmSystemTools::Error(cmStrCat("failed to verify ", *i)); + ret = EXIT_FAILURE; + } + } + } else if (*command == "merge"_s) { + if (!output) { + cmSystemTools::Error( + "-E cmake_module_compile_db verify requires an output"); + return EXIT_FAILURE; + } + + std::vector dbs; + + for (auto const* i : inputs) { + auto db = cmBuildDatabase::Load(*i); + if (!db) { + cmSystemTools::Error(cmStrCat("failed to read ", *i)); + return EXIT_FAILURE; + } + + dbs.emplace_back(std::move(*db)); + } + + auto db = cmBuildDatabase::Merge(dbs); + db.Write(*output); + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_module_compile_db unknown subcommand ", *command)); + return EXIT_FAILURE; + } + + return ret; +} diff --git a/Source/cmBuildDatabase.h b/Source/cmBuildDatabase.h new file mode 100644 index 000000000..a2733cb43 --- /dev/null +++ b/Source/cmBuildDatabase.h @@ -0,0 +1,61 @@ +/* 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 +#include +#include +#include + +#include + +class cmGeneratorTarget; + +class cmBuildDatabase +{ +public: + struct TranslationUnit + { + std::string WorkDirectory; + std::string Source; + cm::optional Object; + std::vector Requires; + std::map Provides; + std::vector BaselineArguments; + std::vector LocalArguments; + std::vector Arguments; + bool Private = false; + }; + + struct Set + { + std::string Name; + std::string FamilyName; + std::vector VisibleSets; + std::vector TranslationUnits; + }; + + cmBuildDatabase(); + cmBuildDatabase(cmBuildDatabase const&); + ~cmBuildDatabase(); + + using LookupTable = std::map; + // Generate a lookup table for the database. + // + // Only use when loading a single target's database in order to populate it. + LookupTable GenerateLookupTable(); + + bool HasPlaceholderNames() const; + + void Write(std::string const& path) const; + + static std::unique_ptr Load(std::string const& path); + static cmBuildDatabase Merge(std::vector const& components); + static cmBuildDatabase ForTarget(cmGeneratorTarget* gt, + std::string const& config); + +private: + std::vector Sets; +}; diff --git a/Source/cmCMakePath.cxx b/Source/cmCMakePath.cxx index 5080f58d7..be5c74a56 100644 --- a/Source/cmCMakePath.cxx +++ b/Source/cmCMakePath.cxx @@ -57,11 +57,13 @@ cmCMakePath cmCMakePath::GetWideExtension() const cmCMakePath cmCMakePath::GetNarrowStem() const { auto stem = this->Path.stem().string(); - if (!stem.empty()) { - auto pos = stem.find('.', stem[0] == '.' ? 1 : 0); - if (pos != std::string::npos) { - return stem.substr(0, pos); - } + if (stem.empty() || stem == "." || stem == "..") { + return stem; + } + + auto pos = stem.find('.', stem[0] == '.' ? 1 : 0); + if (pos != std::string::npos) { + return stem.substr(0, pos); } return stem; } diff --git a/Source/cmCMakePath.h b/Source/cmCMakePath.h index a42ac9841..fd71c1fb5 100644 --- a/Source/cmCMakePath.h +++ b/Source/cmCMakePath.h @@ -749,8 +749,7 @@ inline cmCMakePath::iterator cmCMakePath::end() const inline bool operator==(const cmCMakePath::iterator& lhs, const cmCMakePath::iterator& rhs) { - return lhs.Path == rhs.Path && lhs.Path != nullptr && - lhs.Iterator == rhs.Iterator; + return lhs.Path == rhs.Path && lhs.Path && lhs.Iterator == rhs.Iterator; } inline bool operator!=(const cmCMakePath::iterator& lhs, diff --git a/Source/cmCMakePkgConfigCommand.cxx b/Source/cmCMakePkgConfigCommand.cxx new file mode 100644 index 000000000..468d2ab56 --- /dev/null +++ b/Source/cmCMakePkgConfigCommand.cxx @@ -0,0 +1,722 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmCMakePkgConfigCommand.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cmsys/FStream.hxx" + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExecutionStatus.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPkgConfigParser.h" +#include "cmPkgConfigResolver.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSubcommandTable.h" +#include "cmSystemTools.h" +#include "cmValue.h" +#include + +// IWYU wants this +namespace { +struct ExtractArguments; +} + +namespace { + +cm::optional GetPkgConfigBin(cmMakefile& mf) +{ + cm::optional result; + + auto pkgcfg = mf.GetDefinition("CMAKE_PKG_CONFIG_BIN"); + if (pkgcfg.IsNOTFOUND()) { + return result; + } + + if (pkgcfg) { + result = *pkgcfg; + return result; + } + + std::string path = cmSystemTools::FindProgram("pkgconf"); + if (path.empty()) { + path = cmSystemTools::FindProgram("pkg-config"); + if (path.empty()) { + mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", "pkg-config-NOTFOUND", + "Location of pkg-config or pkgconf binary", + cmStateEnums::FILEPATH); + return result; + } + } + + mf.AddCacheDefinition("CMAKE_PKG_CONFIG_BIN", path, + "Location of pkg-config or pkgconf binary", + cmStateEnums::FILEPATH); + + result = std::move(path); + return result; +} + +std::vector GetLocations(cmMakefile& mf, const char* cachevar, + const char* envvar, const char* desc, + const char* pcvar, bool need_pkgconf, + std::vector default_locs) +{ + auto def = mf.GetDefinition(cachevar); + if (def) { + return cmList(def); + } + + std::string paths; + if (cmSystemTools::GetEnv(envvar, paths)) { + cmPkgConfigResolver::ReplaceSep(paths); + mf.AddCacheDefinition(cachevar, paths, desc, cmStateEnums::STRING); + return cmList(paths); + } + + auto pkgcfg = GetPkgConfigBin(mf); + if (!pkgcfg || (need_pkgconf && (pkgcfg->find("pkgconf") == pkgcfg->npos))) { + mf.AddCacheDefinition(cachevar, cmList::to_string(default_locs), desc, + cmStateEnums::STRING); + return default_locs; + } + + std::string out; + cmSystemTools::RunSingleCommand({ *pkgcfg, pcvar, "pkg-config" }, &out, + nullptr, nullptr, nullptr, + cmSystemTools::OUTPUT_NONE); + + cmPkgConfigResolver::ReplaceSep(out); + out = cmTrimWhitespace(out); + mf.AddCacheDefinition(cachevar, out, desc, cmStateEnums::STRING); + return cmList(out); +} + +std::vector GetPcLibDirs(cmMakefile& mf) +{ + std::vector default_locs = { +#ifndef _WIN32 + "/usr/lib/pkgconfig", "/usr/share/pkgconfig" +#endif + }; + return GetLocations(mf, "CMAKE_PKG_CONFIG_PC_LIB_DIRS", "PKG_CONFIG_LIBDIR", + "Default search locations for package files", + "--variable=pc_path", false, std::move(default_locs)); +} + +std::vector GetSysLibDirs(cmMakefile& mf) +{ + std::vector default_locs = { +#ifndef _WIN32 + "/lib", "/usr/lib" +#endif + }; + return GetLocations( + mf, "CMAKE_PKG_CONFIG_SYS_LIB_DIRS", "PKG_CONFIG_SYSTEM_LIBRARY_PATH", + "System library directories filtered by flag mangling", + "--variable=pc_system_libdirs", true, std::move(default_locs)); +} + +std::vector GetSysCflags(cmMakefile& mf) +{ + std::vector default_locs = { +#ifndef _WIN32 + "/usr/include" +#endif + }; + return GetLocations( + mf, "CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS", "PKG_CONFIG_SYSTEM_INCLUDE_PATH", + "System include directories filtered by flag mangling", + "--variable=pc_system_includedirs", true, std::move(default_locs)); +} + +std::vector GetPkgConfSysLibs(cmMakefile& mf) +{ + auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS"); + if (def) { + return cmList(def); + } + + std::string paths; + if (!cmSystemTools::GetEnv("LIBRARY_PATH", paths)) { + return {}; + } + + cmPkgConfigResolver::ReplaceSep(paths); + mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS", paths, + "Additional system library directories filtered by " + "flag mangling in PKGCONF mode", + cmStateEnums::STRING); + return cmList(paths); +} + +std::vector GetPkgConfSysCflags(cmMakefile& mf) +{ + auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES"); + if (def) { + return cmList(def); + } + + std::string paths; + auto get_and_append = [&](const char* var) { + if (paths.empty()) { + cmSystemTools::GetEnv(var, paths); + } else { + std::string tmp; + cmSystemTools::GetEnv(var, tmp); + if (!tmp.empty()) { + paths += ";" + tmp; + } + } + }; + + get_and_append("CPATH"); + get_and_append("C_INCLUDE_PATH"); + get_and_append("CPLUS_INCLUDE_PATH"); + get_and_append("OBJC_INCLUDE_PATH"); + +#ifdef _WIN32 + get_and_append("INCLUDE"); +#endif + + cmPkgConfigResolver::ReplaceSep(paths); + mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PKGCONF_INCLUDES", paths, + "Additional system include directories filtered by " + "flag mangling in PKGCONF mode", + cmStateEnums::STRING); + return cmList(paths); +} + +std::vector GetPcPath(cmMakefile& mf) +{ + auto def = mf.GetDefinition("CMAKE_PKG_CONFIG_PC_PATH"); + if (def) { + return cmList(def); + } + + std::string pcpath; + if (cmSystemTools::GetEnv("PKG_CONFIG_PATH", pcpath)) { + auto result = cmSystemTools::SplitString(pcpath, cmPkgConfigResolver::Sep); + mf.AddCacheDefinition( + "CMAKE_PKG_CONFIG_PC_PATH", cmList::to_string(result), + "Additional search locations for package files", cmStateEnums::STRING); + return result; + } + + mf.AddCacheDefinition("CMAKE_PKG_CONFIG_PC_PATH", "", + "Additional search locations for package files", + cmStateEnums::STRING); + return {}; +} + +cm::optional GetPath(cmMakefile& mf, const char* cachevar, + const char* envvar, const char* desc) +{ + cm::optional result; + + auto def = mf.GetDefinition(cachevar); + if (def) { + result = *def; + return result; + } + + std::string path; + if (cmSystemTools::GetEnv(envvar, path)) { + mf.AddCacheDefinition(cachevar, path, desc, cmStateEnums::FILEPATH); + result = std::move(path); + return result; + } + + return result; +} + +cm::optional GetSysrootDir(cmMakefile& mf) +{ + return GetPath(mf, "CMAKE_PKG_CONFIG_SYSROOT_DIR", "PKG_CONFIG_SYSROOT_DIR", + "System root used for re-rooting package includes and " + "library directories"); +} + +cm::optional GetTopBuildDir(cmMakefile& mf) +{ + return GetPath(mf, "CMAKE_PKG_CONFIG_TOP_BUILD_DIR", + "PKG_CONFIG_TOP_BUILD_DIR", + "Package file top_build_dir variable default value"); +} + +bool GetBool(cmMakefile& mf, const char* cachevar, const char* envvar, + const char* desc) +{ + auto def = mf.GetDefinition(cachevar); + if (def) { + return def.IsOn(); + } + + if (cmSystemTools::HasEnv(envvar)) { + mf.AddCacheDefinition(cachevar, "ON", desc, cmStateEnums::BOOL); + return true; + } + + return false; +} + +bool GetDisableUninstalled(cmMakefile& mf) +{ + return GetBool(mf, "CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED", + "PKG_CONFIG_DISABLE_UNINSTALLED", + "Disable search for `-uninstalled` (build tree) packages"); +} + +bool GetAllowSysLibs(cmMakefile& mf) +{ + return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS", + "PKG_CONFIG_ALLOW_SYSTEM_LIBS", + "Allow system library directories during flag mangling"); +} + +bool GetAllowSysInclude(cmMakefile& mf) +{ + return GetBool(mf, "CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES", + "PKG_CONFIG_ALLOW_SYSTEM_CFLAGS", + "Allow system include paths during flag manglging"); +} + +struct CommonArguments : ArgumentParser::ParseResult +{ + bool Required = false; + bool Exact = false; + bool Quiet = false; + + enum StrictnessType + { + STRICTNESS_STRICT, + STRICTNESS_PERMISSIVE, + STRICTNESS_BEST_EFFORT, + }; + + StrictnessType Strictness = STRICTNESS_PERMISSIVE; + std::string StrictnessError; + + ArgumentParser::Continue SetStrictness(cm::string_view strictness) + { + if (strictness == "STRICT"_s) { + Strictness = STRICTNESS_STRICT; + } else if (strictness == "PERMISSIVE"_s) { + Strictness = STRICTNESS_PERMISSIVE; + } else if (strictness == "BEST_EFFORT"_s) { + Strictness = STRICTNESS_BEST_EFFORT; + } else { + StrictnessError = + cmStrCat("Invalid 'STRICTNESS' '", strictness, + "'; must be one of 'STRICT', 'PERMISSIVE', or 'BEST_EFFORT'"); + } + return ArgumentParser::Continue::Yes; + } + + enum EnvModeType + { + ENVMODE_FDO, + ENVMODE_PKGCONF, + ENVMODE_IGNORE, + }; + + EnvModeType EnvMode = ENVMODE_PKGCONF; + std::string EnvModeError; + + ArgumentParser::Continue SetEnvMode(cm::string_view envMode) + { + if (envMode == "FDO"_s) { + EnvMode = ENVMODE_FDO; + } else if (envMode == "PKGCONF"_s) { + EnvMode = ENVMODE_PKGCONF; + } else if (envMode == "IGNORE"_s) { + EnvMode = ENVMODE_IGNORE; + } else { + EnvModeError = + cmStrCat("Invalid 'ENV_MODE' '", envMode, + "'; must be one of 'FDO', 'PKGCONF', or 'IGNORE'"); + } + return ArgumentParser::Continue::Yes; + } + + cm::optional Package; + cm::optional Version; + cm::optional SysrootDir; + cm::optional TopBuildDir; + + cm::optional DisableUninstalled; + + cm::optional>> PcPath; + cm::optional>> PcLibdir; + + bool CheckArgs(cmExecutionStatus& status) const + { + + if (!Package) { + status.SetError("A package name or absolute path must be specified"); + return false; + } + + if (!StrictnessError.empty()) { + status.SetError(StrictnessError); + return false; + } + + if (!EnvModeError.empty()) { + status.SetError(EnvModeError); + return false; + } + + return true; + } +}; + +#define BIND_COMMON(argtype) \ + (cmArgumentParser{}) \ + .Bind(1, &argtype::Package) \ + .Bind(2, &argtype::Version) \ + .Bind("REQUIRED"_s, &argtype::Required) \ + .Bind("EXACT"_s, &argtype::Exact) \ + .Bind("QUIET"_s, &argtype::Quiet) \ + .Bind("STRICTNESS"_s, &argtype::SetStrictness) \ + .Bind("ENV_MODE"_s, &argtype::SetEnvMode) \ + .Bind("PC_SYSROOT_DIR"_s, &argtype::SysrootDir) \ + .Bind("TOP_BUILD_DIR"_s, &argtype::TopBuildDir) \ + .Bind("DISABLE_UNINSTALLED"_s, &argtype::DisableUninstalled) \ + .Bind("PC_LIBDIR"_s, &argtype::PcLibdir) \ + .Bind("PC_PATH"_s, &argtype::PcPath) + +void CollectEnv(cmMakefile& mf, cmPkgConfigEnv& env, + CommonArguments::EnvModeType mode) +{ + if (mode == CommonArguments::EnvModeType::ENVMODE_IGNORE) { + return; + } + + if (!env.Path) { + env.Path = GetPcPath(mf); + } + + if (!env.LibDirs) { + env.LibDirs = GetPcLibDirs(mf); + } + + if (!env.DisableUninstalled) { + env.DisableUninstalled = GetDisableUninstalled(mf); + } + + if (!env.SysrootDir) { + env.SysrootDir = GetSysrootDir(mf); + } + + if (!env.TopBuildDir) { + env.TopBuildDir = GetTopBuildDir(mf); + } + + env.AllowSysCflags = GetAllowSysInclude(mf); + env.SysCflags = GetSysCflags(mf); + + env.AllowSysLibs = GetAllowSysLibs(mf); + env.SysLibs = GetSysLibDirs(mf); + + if (mode == CommonArguments::EnvModeType::ENVMODE_FDO) { + return; + } + + *env.SysCflags += GetPkgConfSysCflags(mf); + *env.SysLibs += GetPkgConfSysLibs(mf); +} + +cm::optional HandleCommon(CommonArguments& args, + cmExecutionStatus& status) +{ + + auto& mf = status.GetMakefile(); + + if (!args.CheckArgs(status)) { + return {}; + } + + auto warn_or_error = [&](const std::string& err) { + if (args.Required) { + status.SetError(err); + cmSystemTools::SetFatalErrorOccurred(); + } else if (!args.Quiet) { + mf.IssueMessage(MessageType::WARNING, err); + } + }; + + cm::filesystem::path path{ *args.Package }; + + cmPkgConfigEnv env; + + if (args.PcLibdir) { + env.LibDirs = std::move(*args.PcLibdir); + } + + if (args.PcPath) { + env.Path = std::move(*args.PcPath); + } + + if (args.DisableUninstalled) { + env.DisableUninstalled = args.DisableUninstalled; + } + + if (args.SysrootDir) { + env.SysrootDir = std::move(*args.SysrootDir); + } + + if (args.TopBuildDir) { + env.TopBuildDir = std::move(*args.TopBuildDir); + } + + CollectEnv(mf, env, args.EnvMode); + + if (path.extension() == ".pc") { + if (!cmSystemTools::FileExists(path.string())) { + warn_or_error(cmStrCat("Could not find '", *args.Package, "'")); + return {}; + } + } else { + + std::vector search; + if (env.Path) { + search = *env.Path; + if (env.LibDirs) { + search += *env.LibDirs; + } + } else if (env.LibDirs) { + search = *env.LibDirs; + } + + if (env.DisableUninstalled && !*env.DisableUninstalled) { + auto uninstalled = path; + uninstalled.concat("-uninstalled.pc"); + uninstalled = + cmSystemTools::FindFile(uninstalled.string(), search, true); + if (uninstalled.empty()) { + path = + cmSystemTools::FindFile(path.concat(".pc").string(), search, true); + if (path.empty()) { + warn_or_error(cmStrCat("Could not find '", *args.Package, "'")); + return {}; + } + } else { + path = uninstalled; + } + } else { + path = + cmSystemTools::FindFile(path.concat(".pc").string(), search, true); + if (path.empty()) { + warn_or_error(cmStrCat("Could not find '", *args.Package, "'")); + return {}; + } + } + } + + auto len = cmSystemTools::FileLength(path.string()); + + // Windows requires this weird string -> c_str dance + cmsys::ifstream ifs(path.string().c_str(), std::ios::binary); + + if (!ifs) { + warn_or_error(cmStrCat("Could not open file '", path.string(), "'")); + return {}; + } + + std::unique_ptr buf(new char[len]); + ifs.read(buf.get(), len); + + // Shouldn't have hit eof on previous read, should hit eof now + if (ifs.fail() || ifs.eof() || ifs.get() != EOF) { + warn_or_error(cmStrCat("Error while reading file '", path.string(), "'")); + return {}; + } + + using StrictnessType = CommonArguments::StrictnessType; + + cmPkgConfigParser parser; + auto err = parser.Finish(buf.get(), len); + + if (args.Strictness != StrictnessType::STRICTNESS_BEST_EFFORT && + err != PCE_OK) { + warn_or_error(cmStrCat("Parsing failed for file '", path.string(), "'")); + return {}; + } + + cm::optional result; + if (args.Strictness == StrictnessType::STRICTNESS_STRICT) { + result = cmPkgConfigResolver::ResolveStrict(parser.Data(), std::move(env)); + } else if (args.Strictness == StrictnessType::STRICTNESS_PERMISSIVE) { + result = + cmPkgConfigResolver::ResolvePermissive(parser.Data(), std::move(env)); + } else { + result = + cmPkgConfigResolver::ResolveBestEffort(parser.Data(), std::move(env)); + } + + if (!result) { + warn_or_error( + cmStrCat("Resolution failed for file '", path.string(), "'")); + } else if (args.Exact) { + std::string ver; + + if (args.Version) { + ver = cmPkgConfigResolver::ParseVersion(*args.Version).Version; + } + + if (ver != result->Version()) { + warn_or_error( + cmStrCat("Package '", *args.Package, "' version '", result->Version(), + "' does not meet exact version requirement '", ver, "'")); + return {}; + } + + } else if (args.Version) { + auto rv = cmPkgConfigResolver::ParseVersion(*args.Version); + if (!cmPkgConfigResolver::CheckVersion(rv, result->Version())) { + warn_or_error( + cmStrCat("Package '", *args.Package, "' version '", result->Version(), + "' does not meet version requirement '", *args.Version, "'")); + return {}; + } + } + + return result; +} + +struct ExtractArguments : CommonArguments +{ + cm::optional AllowSystemIncludes; + cm::optional AllowSystemLibs; + + cm::optional>> + SystemIncludeDirs; + cm::optional>> + SystemLibraryDirs; +}; + +const auto ExtractParser = + BIND_COMMON(ExtractArguments) + .Bind("ALLOW_SYSTEM_INCLUDES"_s, &ExtractArguments::AllowSystemIncludes) + .Bind("ALLOW_SYSTEM_LIBS"_s, &ExtractArguments::AllowSystemLibs) + .Bind("SYSTEM_INCLUDE_DIRS"_s, &ExtractArguments::SystemIncludeDirs) + .Bind("SYSTEM_LIBRARY_DIRS"_s, &ExtractArguments::SystemLibraryDirs); + +bool HandleExtractCommand(std::vector const& args, + cmExecutionStatus& status) +{ + + std::vector unparsed; + auto parsedArgs = ExtractParser.Parse(args, &unparsed); + auto maybeResolved = HandleCommon(parsedArgs, status); + + if (!maybeResolved) { + return !parsedArgs.Required; + } + + auto& resolved = *maybeResolved; + auto version = resolved.Version(); + + if (parsedArgs.AllowSystemIncludes) { + resolved.env.AllowSysCflags = *parsedArgs.AllowSystemIncludes; + } + + if (parsedArgs.AllowSystemLibs) { + resolved.env.AllowSysLibs = *parsedArgs.AllowSystemLibs; + } + + if (parsedArgs.SystemIncludeDirs) { + resolved.env.SysCflags = *parsedArgs.SystemIncludeDirs; + } + + if (parsedArgs.SystemLibraryDirs) { + resolved.env.SysLibs = *parsedArgs.SystemLibraryDirs; + } + + auto& mf = status.GetMakefile(); + mf.AddDefinition("CMAKE_PKG_CONFIG_NAME", resolved.Name()); + mf.AddDefinition("CMAKE_PKG_CONFIG_DESCRIPTION", resolved.Description()); + mf.AddDefinition("CMAKE_PKG_CONFIG_VERSION", version); + + auto make_list = [&](const char* def, + const std::vector& deps) { + std::vector vec; + vec.reserve(deps.size()); + + for (const auto& dep : deps) { + vec.emplace_back(dep.Name); + } + + mf.AddDefinition(def, cmList::to_string(vec)); + }; + + make_list("CMAKE_PKG_CONFIG_CONFLICTS", resolved.Conflicts()); + make_list("CMAKE_PKG_CONFIG_PROVIDES", resolved.Provides()); + make_list("CMAKE_PKG_CONFIG_REQUIRES", resolved.Requires()); + make_list("CMAKE_PKG_CONFIG_REQUIRES_PRIVATE", resolved.Requires(true)); + + auto cflags = resolved.Cflags(); + mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS", cflags.Flagline); + mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES", + cmList::to_string(cflags.Includes)); + mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS", + cmList::to_string(cflags.CompileOptions)); + + cflags = resolved.Cflags(true); + mf.AddDefinition("CMAKE_PKG_CONFIG_CFLAGS_PRIVATE", cflags.Flagline); + mf.AddDefinition("CMAKE_PKG_CONFIG_INCLUDES_PRIVATE", + cmList::to_string(cflags.Includes)); + mf.AddDefinition("CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE", + cmList::to_string(cflags.CompileOptions)); + + auto libs = resolved.Libs(); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS", libs.Flagline); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS", + cmList::to_string(libs.LibDirs)); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES", + cmList::to_string(libs.LibNames)); + mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS", + cmList::to_string(libs.LinkOptions)); + + libs = resolved.Libs(true); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBS_PRIVATE", libs.Flagline); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE", + cmList::to_string(libs.LibDirs)); + mf.AddDefinition("CMAKE_PKG_CONFIG_LIBNAMES_PRIVATE", + cmList::to_string(libs.LibNames)); + mf.AddDefinition("CMAKE_PKG_CONFIG_LINK_OPTIONS_PRIVATE", + cmList::to_string(libs.LinkOptions)); + + return true; +} +} // namespace + +bool cmCMakePkgConfigCommand(std::vector const& args, + cmExecutionStatus& status) +{ + if (args.size() < 2) { + status.SetError("must be called with at least two arguments."); + return false; + } + + static cmSubcommandTable const subcommand{ + { "EXTRACT"_s, HandleExtractCommand }, + }; + + return subcommand(args[0], args, status); +} diff --git a/Source/cmCMakePkgConfigCommand.h b/Source/cmCMakePkgConfigCommand.h new file mode 100644 index 000000000..589881866 --- /dev/null +++ b/Source/cmCMakePkgConfigCommand.h @@ -0,0 +1,13 @@ +/* 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 +#include + +class cmExecutionStatus; + +bool cmCMakePkgConfigCommand(std::vector const& args, + cmExecutionStatus& status); diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx index 1b88a0c79..668476a31 100644 --- a/Source/cmCMakePresetsErrors.cxx +++ b/Source/cmCMakePresetsErrors.cxx @@ -31,7 +31,7 @@ const Json::Value* getPreset(cmJSONState* state) std::string getPresetName(cmJSONState* state) { const Json::Value* preset = getPreset(state); - if (preset != nullptr && preset->isMember("name")) { + if (preset && preset->isMember("name")) { return preset->operator[]("name").asString(); } return ""; @@ -177,6 +177,12 @@ void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state) "support"); } +void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state) +{ + state->AddError( + "File version must be 10 or higher for graphviz preset support"); +} + void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state) { state->AddError(cmStrCat("Cyclic include among preset files: ", file)); @@ -232,6 +238,16 @@ void TRACE_UNSUPPORTED(cmJSONState* state) state->AddError("File version must be 7 or higher for trace preset support"); } +JsonErrors::ErrorGenerator UNRECOGNIZED_VERSION_RANGE(int min, int max) +{ + return [min, max](const Json::Value* value, cmJSONState* state) -> void { + state->AddErrorAtValue(cmStrCat("Unrecognized \"version\" ", + value->asString(), ": must be >=", min, + " and <=", max), + value); + }; +} + JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION( const std::string& version, int current, int required) { diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h index b755c2567..22830d297 100644 --- a/Source/cmCMakePresetsErrors.h +++ b/Source/cmCMakePresetsErrors.h @@ -70,6 +70,8 @@ void CONDITION_UNSUPPORTED(cmJSONState* state); void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state); +void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state); + void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state); void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state); @@ -92,6 +94,8 @@ void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state); void TRACE_UNSUPPORTED(cmJSONState* state); +JsonErrors::ErrorGenerator UNRECOGNIZED_VERSION_RANGE(int min, int max); + JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION( const std::string& version, int current, int required); diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index 008f2dd63..b3952763d 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx @@ -293,6 +293,12 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, out->ToolchainFile = toolchain; } + if (!preset.GraphVizFile.empty()) { + std::string graphVizFile = preset.GraphVizFile; + CHECK_EXPAND(out, graphVizFile, macroExpanders, graph.GetVersion(preset)); + out->GraphVizFile = graphVizFile; + } + for (auto& variable : out->CacheVariables) { if (variable.second) { CHECK_EXPAND(out, variable.second->Value, macroExpanders, @@ -775,6 +781,7 @@ bool cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit( InheritString(preset.BinaryDir, parent.BinaryDir); InheritString(preset.InstallDir, parent.InstallDir); InheritString(preset.ToolchainFile, parent.ToolchainFile); + InheritString(preset.GraphVizFile, parent.GraphVizFile); InheritOptionalValue(preset.WarnDev, parent.WarnDev); InheritOptionalValue(preset.ErrorDev, parent.ErrorDev); InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated); @@ -1195,12 +1202,12 @@ bool cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) const ConfigurePreset* configurePreset = nullptr; for (auto const& step : it.second.Unexpanded.Steps) { - if (configurePreset == nullptr && step.PresetType != Type::Configure) { + if (!configurePreset && step.PresetType != Type::Configure) { cmCMakePresetsErrors::FIRST_WORKFLOW_STEP_NOT_CONFIGURE( step.PresetName, &this->parseState); return false; } - if (configurePreset != nullptr && step.PresetType == Type::Configure) { + if (configurePreset && step.PresetType == Type::Configure) { cmCMakePresetsErrors::CONFIGURE_WORKFLOW_STEP_NOT_FIRST( step.PresetName, &this->parseState); return false; @@ -1233,7 +1240,7 @@ bool cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) } } - if (configurePreset == nullptr) { + if (!configurePreset) { cmCMakePresetsErrors::NO_WORKFLOW_STEPS(it.first, &this->parseState); return false; } diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index a000e7dee..6ff6a0ef1 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h @@ -120,6 +120,7 @@ public: std::string Toolset; cm::optional ToolsetStrategy; std::string ToolchainFile; + std::string GraphVizFile; std::string BinaryDir; std::string InstallDir; diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index 1cf950066..18ae9af68 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx @@ -40,7 +40,7 @@ using cmCMakePresetsGraphInternal::BaseMacroExpander; using cmCMakePresetsGraphInternal::ExpandMacros; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 9; +constexpr int MAX_VERSION = 10; struct CMakeVersion { @@ -256,9 +256,14 @@ auto const VersionIntHelper = auto const VersionHelper = JSONHelperBuilder::Required( cmCMakePresetsErrors::NO_VERSION, VersionIntHelper); +auto const VersionRangeHelper = JSONHelperBuilder::Checked( + cmCMakePresetsErrors::UNRECOGNIZED_VERSION_RANGE(MIN_VERSION, MAX_VERSION), + VersionHelper, + [](const int v) -> bool { return v >= MIN_VERSION && v <= MAX_VERSION; }); + auto const RootVersionHelper = JSONHelperBuilder::Object(cmCMakePresetsErrors::INVALID_ROOT_OBJECT) - .Bind("version"_s, VersionHelper, false); + .Bind("version"_s, VersionRangeHelper, false); auto const CMakeVersionUIntHelper = JSONHelperBuilder::UInt(cmCMakePresetsErrors::INVALID_VERSION); @@ -481,11 +486,6 @@ bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename, if ((result = RootVersionHelper(v, &root, &parseState)) != true) { return result; } - if (v < MIN_VERSION || v > MAX_VERSION) { - cmCMakePresetsErrors::UNRECOGNIZED_VERSION(&root["version"], - &this->parseState); - return false; - } // Support for build and test presets added in version 2. if (v < 2) { @@ -528,6 +528,9 @@ bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename, return false; } + // Support for $comment added in version 10. + this->parseState.allowComments = (v >= 10); + RootPresets presets; if ((result = RootPresetsHelper(presets, &root, &parseState)) != true) { return result; @@ -608,6 +611,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename, return false; } + // Support for graphviz argument added in version 10. + if (v < 10 && !preset.GraphVizFile.empty()) { + cmCMakePresetsErrors::GRAPHVIZ_FILE_UNSUPPORTED(&this->parseState); + return false; + } + this->ConfigurePresetOrder.push_back(preset.Name); } diff --git a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx index c52a187c4..8f49d69a2 100644 --- a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx +++ b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx @@ -272,6 +272,8 @@ auto const ConfigurePresetHelper = .Bind("toolset"_s, ToolsetHelper, false) .Bind("toolchainFile"_s, &ConfigurePreset::ToolchainFile, cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("graphviz"_s, &ConfigurePreset::GraphVizFile, + cmCMakePresetsGraphInternal::PresetStringHelper, false) .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir, cmCMakePresetsGraphInternal::PresetStringHelper, false) .Bind("installDir"_s, &ConfigurePreset::InstallDir, diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index e1aefd9d0..829fbf923 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -235,8 +235,8 @@ struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag) char buf[1024]; // add todays year day and month to the time in str because // curl_getdate no longer assumes the day is today - snprintf(buf, sizeof(buf), "%d%02d%02d %s", lctime->tm_year + 1900, - lctime->tm_mon + 1, lctime->tm_mday, str.c_str()); + std::snprintf(buf, sizeof(buf), "%d%02d%02d %s", lctime->tm_year + 1900, + lctime->tm_mon + 1, lctime->tm_mday, str.c_str()); cmCTestLog(this, OUTPUT, "Determine Nightly Start Time" << std::endl << " Specified time: " << str @@ -538,8 +538,7 @@ int cmCTest::Initialize(const std::string& binary_dir, } tfin.close(); } - if (tag.empty() || (nullptr != command) || - this->Impl->Parts[PartStart]) { + if (tag.empty() || command || this->Impl->Parts[PartStart]) { cmCTestOptionalLog( this, DEBUG, "TestModel: " << this->GetTestModelString() << std::endl, quiet); @@ -573,7 +572,7 @@ int cmCTest::Initialize(const std::string& binary_dir, } } ofs.close(); - if (nullptr == command) { + if (!command) { cmCTestOptionalLog(this, OUTPUT, "Create new tag: " << tag << " - " << this->GetTestModelString() @@ -1074,11 +1073,6 @@ int cmCTest::GetTestModelFromString(const std::string& str) return cmCTest::EXPERIMENTAL; } -// ###################################################################### -// ###################################################################### -// ###################################################################### -// ###################################################################### - bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, int* retVal, const char* dir, cmDuration timeout, std::ostream& ofs, Encoding encoding) @@ -1140,10 +1134,9 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, << "K\n " << std::flush); } } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, strdata); if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + ofs << strdata; } }, [this, &processOutput, &output, &ofs]() { @@ -1151,10 +1144,9 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { output.append(strdata); - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, strdata); if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + ofs << strdata; } } }); @@ -1193,11 +1185,6 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, return true; } -// ###################################################################### -// ###################################################################### -// ###################################################################### -// ###################################################################### - bool cmCTest::RunTest(const std::vector& argv, std::string* output, int* retVal, std::ostream* log, cmDuration testTimeOut, @@ -1306,8 +1293,7 @@ bool cmCTest::RunTest(const std::vector& argv, if (output) { cm::append(tempOutput, data.data(), data.data() + data.size()); } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, strdata); if (log) { log->write(strdata.c_str(), strdata.size()); } @@ -1316,8 +1302,7 @@ bool cmCTest::RunTest(const std::vector& argv, std::string strdata; processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, strdata); if (log) { log->write(strdata.c_str(), strdata.size()); } @@ -1448,6 +1433,7 @@ void cmCTest::StartXML(cmXMLWriter& xml, bool append) xml.Attribute("VendorID", info.GetVendorID()); xml.Attribute("FamilyID", info.GetFamilyID()); xml.Attribute("ModelID", info.GetModelID()); + xml.Attribute("ModelName", info.GetModelName()); xml.Attribute("ProcessorCacheSize", info.GetProcessorCacheSize()); xml.Attribute("NumberOfLogicalCPU", info.GetNumberOfLogicalCPU()); xml.Attribute("NumberOfPhysicalCPU", info.GetNumberOfPhysicalCPU()); @@ -1661,8 +1647,8 @@ std::string cmCTest::Base64GzipEncodeFile(std::string const& file) std::vector files; files.push_back(file); - if (!cmSystemTools::CreateTar(tarFile, files, cmSystemTools::TarCompressGZip, - false)) { + if (!cmSystemTools::CreateTar(tarFile, files, {}, + cmSystemTools::TarCompressGZip, false)) { cmCTestLog(this, ERROR_MESSAGE, "Error creating tar while " "encoding file: " @@ -1861,22 +1847,20 @@ bool cmCTest::AddTestsForDashboardType(std::string& targ) void cmCTest::ErrorMessageUnknownDashDValue(std::string& val) { cmCTestLog(this, ERROR_MESSAGE, - "CTest -D called with incorrect option: " << val << std::endl); - - cmCTestLog( - this, ERROR_MESSAGE, - "Available options are:" - << std::endl - << " ctest -D Continuous" << std::endl - << " ctest -D Continuous(Start|Update|Configure|Build)" << std::endl - << " ctest -D Continuous(Test|Coverage|MemCheck|Submit)" << std::endl - << " ctest -D Experimental" << std::endl - << " ctest -D Experimental(Start|Update|Configure|Build)" << std::endl - << " ctest -D Experimental(Test|Coverage|MemCheck|Submit)" << std::endl - << " ctest -D Nightly" << std::endl - << " ctest -D Nightly(Start|Update|Configure|Build)" << std::endl - << " ctest -D Nightly(Test|Coverage|MemCheck|Submit)" << std::endl - << " ctest -D NightlyMemoryCheck" << std::endl); + "CTest -D called with incorrect option: " << val << '\n'); + + cmCTestLog(this, ERROR_MESSAGE, + "Available options are:\n" + " ctest -D Continuous\n" + " ctest -D Continuous(Start|Update|Configure|Build)\n" + " ctest -D Continuous(Test|Coverage|MemCheck|Submit)\n" + " ctest -D Experimental\n" + " ctest -D Experimental(Start|Update|Configure|Build)\n" + " ctest -D Experimental(Test|Coverage|MemCheck|Submit)\n" + " ctest -D Nightly\n" + " ctest -D Nightly(Start|Update|Configure|Build)\n" + " ctest -D Nightly(Test|Coverage|MemCheck|Submit)\n" + " ctest -D NightlyMemoryCheck\n"); } bool cmCTest::CheckArgument(const std::string& arg, cm::string_view varg1, @@ -1986,7 +1970,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, this->SetTestLoad(load); } else { cmCTestLog(this, WARNING, - "Invalid value for 'Test Load' : " << args[i] << std::endl); + "Invalid value for 'Test Load' : " << args[i] << '\n'); } } @@ -2912,21 +2896,21 @@ bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i, if (!this->SetTest(args[i], false)) { success = false; cmCTestLog(this, ERROR_MESSAGE, - "CTest -T called with incorrect option: " << args[i] - << std::endl); + "CTest -T called with incorrect option: " << args[i] << '\n'); + /* clang-format off */ cmCTestLog(this, ERROR_MESSAGE, - "Available options are:" - << std::endl - << " " << ctestExec << " -T all" << std::endl - << " " << ctestExec << " -T start" << std::endl - << " " << ctestExec << " -T update" << std::endl - << " " << ctestExec << " -T configure" << std::endl - << " " << ctestExec << " -T build" << std::endl - << " " << ctestExec << " -T test" << std::endl - << " " << ctestExec << " -T coverage" << std::endl - << " " << ctestExec << " -T memcheck" << std::endl - << " " << ctestExec << " -T notes" << std::endl - << " " << ctestExec << " -T submit" << std::endl); + "Available options are:\n" + " " << ctestExec << " -T all\n" + " " << ctestExec << " -T start\n" + " " << ctestExec << " -T update\n" + " " << ctestExec << " -T configure\n" + " " << ctestExec << " -T build\n" + " " << ctestExec << " -T test\n" + " " << ctestExec << " -T coverage\n" + " " << ctestExec << " -T memcheck\n" + " " << ctestExec << " -T notes\n" + " " << ctestExec << " -T submit\n"); + /* clang-format on */ } } return success; @@ -2952,14 +2936,14 @@ bool cmCTest::HandleTestModelArgument(const char* ctestExec, size_t& i, } else { success = false; cmCTestLog(this, ERROR_MESSAGE, - "CTest -M called with incorrect option: " << str - << std::endl); + "CTest -M called with incorrect option: " << str << '\n'); + /* clang-format off */ cmCTestLog(this, ERROR_MESSAGE, - "Available options are:" - << std::endl - << " " << ctestExec << " -M Continuous" << std::endl - << " " << ctestExec << " -M Experimental" << std::endl - << " " << ctestExec << " -M Nightly" << std::endl); + "Available options are:\n" + " " << ctestExec << " -M Continuous\n" + " " << ctestExec << " -M Experimental\n" + " " << ctestExec << " -M Nightly\n"); + /* clang-format on */ } } return success; @@ -3501,10 +3485,10 @@ void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr) size_t epos = overStr.find('='); if (epos == std::string::npos) { cmCTestLog(this, ERROR_MESSAGE, - "CTest configuration overwrite specified in the wrong format." - << std::endl - << "Valid format is: --overwrite key=value" << std::endl - << "The specified was: --overwrite " << overStr << std::endl); + "CTest configuration overwrite specified in the wrong format.\n" + "Valid format is: --overwrite key=value\n" + "The specified was: --overwrite " + << overStr << '\n'); return; } std::string key = overStr.substr(0, epos); diff --git a/Source/cmCTest.h b/Source/cmCTest.h index ae2431ceb..85f75fd76 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -549,29 +549,6 @@ private: std::unique_ptr Impl; }; -class cmCTestLogWrite -{ -public: - cmCTestLogWrite(const char* data, size_t length) - : Data(data) - , Length(length) - { - } - - const char* Data; - size_t Length; -}; - -inline std::ostream& operator<<(std::ostream& os, const cmCTestLogWrite& c) -{ - if (!c.Length) { - return os; - } - os.write(c.Data, c.Length); - os.flush(); - return os; -} - #define cmCTestLog(ctSelf, logType, msg) \ do { \ std::ostringstream cmCTestLog_msg; \ diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 91f7691df..089bafc71 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -94,6 +94,7 @@ # include "cmAuxSourceDirectoryCommand.h" # include "cmBuildNameCommand.h" # include "cmCMakeHostSystemInformationCommand.h" +# include "cmCMakePkgConfigCommand.h" # include "cmExportCommand.h" # include "cmExportLibraryDependenciesCommand.h" # include "cmFLTKWrapUICommand.h" @@ -208,6 +209,7 @@ void GetScriptingCommands(cmState* state) #if !defined(CMAKE_BOOTSTRAP) state->AddBuiltinCommand("cmake_host_system_information", cmCMakeHostSystemInformationCommand); + state->AddBuiltinCommand("cmake_pkg_config", cmCMakePkgConfigCommand); state->AddBuiltinCommand("load_cache", cmLoadCacheCommand); state->AddBuiltinCommand("remove", cmRemoveCommand); state->AddBuiltinCommand("variable_watch", cmVariableWatchCommand); diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 193767413..551a45b88 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -314,8 +314,10 @@ const LinkLibraryFeatureAttributeSet& GetLinkLibraryFeatureAttributes( } // LINK_GROUP helpers -const auto LG_BEGIN = "GetBacktrace()); CM_FALLTHROUGH; case cmPolicies::NEW: { + // Policy 0179 applies only when policy 0156 is new + switch (target->GetPolicyStatusCMP0179()) { + case cmPolicies::WARN: + if (!makefile->GetCMakeInstance()->GetIsInTryCompile() && + makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0179")) { + makefile->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0179), + "\nSince the policy is not set, static libraries " + "de-duplication will keep the last occurrence of the " + "static libraries."), + target->GetBacktrace()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + makefile->GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0179), + target->GetBacktrace()); + CM_FALLTHROUGH; + case cmPolicies::NEW: + break; + } + if (auto libProcessing = makefile->GetDefinition(cmStrCat( "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) { // UNICITY keyword is just for compatibility with previous @@ -430,8 +460,8 @@ public: this->Groups = &groups; // record all libraries as part of groups to ensure correct // deduplication: libraries as part of groups are always kept. - for (const auto& group : groups) { - for (auto index : group.second) { + for (const auto& g : groups) { + for (auto index : g.second) { this->Emitted.insert(index); } } @@ -441,9 +471,28 @@ public: void AddLibraries(const std::vector& libEntries) { if (this->Order == Reverse) { + std::vector entries; + if (this->Deduplication == All && + this->Target->GetPolicyStatusCMP0179() == cmPolicies::NEW) { + // keep the first occurrence of the static libraries + std::set emitted{ this->Emitted }; + for (auto index : libEntries) { + LinkEntry const& entry = this->Entries[index]; + if (!entry.Target || + entry.Target->GetType() != cmStateEnums::STATIC_LIBRARY) { + entries.emplace_back(index); + continue; + } + if (this->IncludeEntry(entry) || emitted.insert(index).second) { + entries.emplace_back(index); + } + } + } else { + entries = libEntries; + } // Iterate in reverse order so we can keep only the last occurrence - // of a library. - this->AddLibraries(cmReverseRange(libEntries)); + // of the shared libraries. + this->AddLibraries(cmReverseRange(entries)); } else { this->AddLibraries(cmMakeRange(libEntries)); } @@ -475,9 +524,9 @@ public: } // expand groups - if (this->Groups != nullptr) { - for (const auto& group : *this->Groups) { - const LinkEntry& groupEntry = this->Entries[group.first]; + if (this->Groups) { + for (const auto& g : *this->Groups) { + const LinkEntry& groupEntry = this->Entries[g.first]; auto it = this->FinalEntries.begin(); while (true) { it = std::find_if(it, this->FinalEntries.end(), @@ -487,13 +536,13 @@ public: if (it == this->FinalEntries.end()) { break; } - it->Item.Value = ""; - for (auto index = group.second.rbegin(); - index != group.second.rend(); ++index) { + it->Item.Value = std::string(LG_ITEM_END); + for (auto index = g.second.rbegin(); index != g.second.rend(); + ++index) { it = this->FinalEntries.insert(it, this->Entries[*index]); } it = this->FinalEntries.insert(it, groupEntry); - it->Item.Value = ""; + it->Item.Value = std::string(LG_ITEM_BEGIN); } } } @@ -518,7 +567,7 @@ private: if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) { auto const& featureAttributes = GetLinkLibraryFeatureAttributes( this->Target->Makefile, this->LinkLanguage, entry.Feature); - if ((entry.Target == nullptr || + if ((!entry.Target || featureAttributes.LibraryTypes.find(entry.Target->GetType()) != featureAttributes.LibraryTypes.end()) && featureAttributes.Deduplication != @@ -530,7 +579,7 @@ private: return this->Deduplication == None || (this->Deduplication == Shared && - (entry.Target == nullptr || + (!entry.Target || entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) || (this->Deduplication == All && entry.Kind != LinkEntry::Library); } @@ -562,16 +611,21 @@ std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT = cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, const std::string& config, - const std::string& linkLanguage) -{ - // Store context information. - this->Target = target; - this->Makefile = this->Target->Target->GetMakefile(); - this->GlobalGenerator = - this->Target->GetLocalGenerator()->GetGlobalGenerator(); - this->CMakeInstance = this->GlobalGenerator->GetCMakeInstance(); - this->LinkLanguage = linkLanguage; + const std::string& linkLanguage, + LinkLibrariesStrategy strategy) + : Target(target) + , Makefile(this->Target->Target->GetMakefile()) + , GlobalGenerator(this->Target->GetLocalGenerator()->GetGlobalGenerator()) + , CMakeInstance(this->GlobalGenerator->GetCMakeInstance()) + , Config(config) + , DebugMode(this->Makefile->IsOn("CMAKE_LINK_DEPENDS_DEBUG_MODE") || + this->Target->GetProperty("LINK_DEPENDS_DEBUG_MODE").IsOn()) + , LinkLanguage(linkLanguage) + , LinkType(CMP0003_ComputeLinkType( + this->Config, this->Makefile->GetCMakeInstance()->GetDebugConfigs())) + , Strategy(strategy) +{ // target oriented feature override property takes precedence over // global override property cm::string_view lloPrefix = "LINK_LIBRARY_OVERRIDE_"_s; @@ -623,22 +677,6 @@ cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target, }); } } - - // The configuration being linked. - this->HasConfig = !config.empty(); - this->Config = (this->HasConfig) ? config : std::string(); - std::vector debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); - this->LinkType = CMP0003_ComputeLinkType(this->Config, debugConfigs); - - // Enable debug mode if requested. - this->DebugMode = this->Makefile->IsOn("CMAKE_LINK_DEPENDS_DEBUG_MODE"); - - // Assume no compatibility until set. - this->OldLinkDirMode = false; - - // No computation has been done. - this->CCG = nullptr; } cmComputeLinkDepends::~cmComputeLinkDepends() = default; @@ -689,7 +727,7 @@ cmComputeLinkDepends::Compute() "---------------------------------------\n"); fprintf(stderr, "Link dependency analysis for target %s, config %s\n", this->Target->GetName().c_str(), - this->HasConfig ? this->Config.c_str() : "noconfig"); + this->Config.empty() ? "noconfig" : this->Config.c_str()); this->DisplayConstraintGraph(); } @@ -709,6 +747,11 @@ cmComputeLinkDepends::Compute() // Compute the final ordering. this->OrderLinkEntries(); + // Display the final ordering. + if (this->DebugMode) { + this->DisplayOrderedEntries(); + } + // Compute the final set of link entries. EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage, this->EntryList, @@ -749,7 +792,7 @@ cmComputeLinkDepends::AllocateLinkEntry(cmLinkItem const& item) } std::pair cmComputeLinkDepends::AddLinkEntry( - cmLinkItem const& item, size_t groupIndex) + cmLinkItem const& item, cm::optional const& groupIndex) { // Allocate a spot for the item entry. auto lei = this->AllocateLinkEntry(item); @@ -825,10 +868,7 @@ void cmComputeLinkDepends::AddLinkObject(cmLinkItem const& item) void cmComputeLinkDepends::FollowLinkEntry(BFSEntry qe) { // Get this entry representation. - size_t depender_index = - qe.GroupIndex == cmComputeComponentGraph::INVALID_COMPONENT - ? qe.Index - : qe.GroupIndex; + size_t depender_index = qe.GroupIndex ? *qe.GroupIndex : qe.Index; LinkEntry const& entry = this->EntryList[qe.Index]; // Follow the item's dependencies. @@ -927,8 +967,8 @@ void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep) } } -void cmComputeLinkDepends::AddVarLinkEntries(size_t depender_index, - const char* value) +void cmComputeLinkDepends::AddVarLinkEntries( + cm::optional const& depender_index, const char* value) { // This is called to add the dependencies named by // _LIB_DEPENDS. The variable contains a semicolon-separated @@ -989,15 +1029,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries() // Add direct link dependencies in this configuration. cmLinkImplementation const* impl = this->Target->GetLinkImplementation( this->Config, cmGeneratorTarget::UseTo::Link); - this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT, - impl->Libraries); + this->AddLinkEntries(cm::nullopt, impl->Libraries); this->AddLinkObjects(impl->Objects); for (auto const& language : impl->Languages) { auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language); if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) { - this->AddLinkEntries(cmComputeComponentGraph::INVALID_COMPONENT, - runtimeEntries->second); + this->AddLinkEntries(cm::nullopt, runtimeEntries->second); } } for (cmLinkItem const& wi : impl->WrongConfigLibraries) { @@ -1006,16 +1044,13 @@ void cmComputeLinkDepends::AddDirectLinkEntries() } template -void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, - std::vector const& libs) +void cmComputeLinkDepends::AddLinkEntries( + cm::optional const& depender_index, std::vector const& libs) { // Track inferred dependency sets implied by this list. std::map dependSets; - bool inGroup = false; - std::pair groupIndex{ - cmComputeComponentGraph::INVALID_COMPONENT, false - }; + cm::optional> group; std::vector groupItems; // Loop over the libraries linked directly by the depender. @@ -1029,10 +1064,9 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, // emit a warning if an undefined feature is used as part of // an imported target - if (item.Feature != LinkEntry::DEFAULT && - depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { - const auto& depender = this->EntryList[depender_index]; - if (depender.Target != nullptr && depender.Target->IsImported() && + if (item.Feature != LinkEntry::DEFAULT && depender_index) { + const auto& depender = this->EntryList[*depender_index]; + if (depender.Target && depender.Target->IsImported() && !IsFeatureSupported(this->Makefile, this->LinkLanguage, item.Feature)) { this->CMakeInstance->IssueMessage( @@ -1052,18 +1086,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, if (cmHasPrefix(item.AsStr(), LG_BEGIN) && cmHasSuffix(item.AsStr(), '>')) { - groupIndex = this->AddLinkEntry(item); - if (groupIndex.second) { - LinkEntry& entry = this->EntryList[groupIndex.first]; + group = this->AddLinkEntry(item, cm::nullopt); + if (group->second) { + LinkEntry& entry = this->EntryList[group->first]; entry.Feature = ExtractGroupFeature(item.AsStr()); } - inGroup = true; - if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { - this->EntryConstraintGraph[depender_index].emplace_back( - groupIndex.first, false, false, cmListFileBacktrace()); + if (depender_index) { + this->EntryConstraintGraph[*depender_index].emplace_back( + group->first, false, false, cmListFileBacktrace()); } else { // This is a direct dependency of the target being linked. - this->OriginalEntries.push_back(groupIndex.first); + this->OriginalEntries.push_back(group->first); } continue; } @@ -1071,21 +1104,20 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, size_t dependee_index; if (cmHasPrefix(item.AsStr(), LG_END) && cmHasSuffix(item.AsStr(), '>')) { - dependee_index = groupIndex.first; - if (groupIndex.second) { - this->GroupItems.emplace(groupIndex.first, groupItems); + assert(group); + dependee_index = group->first; + if (group->second) { + this->GroupItems.emplace(group->first, std::move(groupItems)); } - inGroup = false; - groupIndex = std::make_pair(-1, false); + group = cm::nullopt; groupItems.clear(); continue; } - if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT && - inGroup) { - const auto& depender = this->EntryList[depender_index]; - const auto& groupFeature = this->EntryList[groupIndex.first].Feature; - if (depender.Target != nullptr && depender.Target->IsImported() && + if (depender_index && group) { + const auto& depender = this->EntryList[*depender_index]; + const auto& groupFeature = this->EntryList[group->first].Feature; + if (depender.Target && depender.Target->IsImported() && !IsGroupFeatureSupported(this->Makefile, this->LinkLanguage, groupFeature)) { this->CMakeInstance->IssueMessage( @@ -1104,18 +1136,19 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, } // Add a link entry for this item. - auto ale = this->AddLinkEntry(item, groupIndex.first); + auto ale = this->AddLinkEntry( + item, group ? cm::optional(group->first) : cm::nullopt); dependee_index = ale.first; LinkEntry& entry = this->EntryList[dependee_index]; bool supportedItem = true; auto const& itemFeature = this->GetCurrentFeature(entry.Item.Value, item.Feature); - if (inGroup && ale.second && entry.Target != nullptr && + if (group && ale.second && entry.Target && (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY || entry.Target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY)) { supportedItem = false; - const auto& groupFeature = this->EntryList[groupIndex.first].Feature; + const auto& groupFeature = this->EntryList[group->first].Feature; this->CMakeInstance->IssueMessage( MessageType::AUTHOR_WARNING, cmStrCat( @@ -1130,7 +1163,7 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, this->Target->GetBacktrace()); } // check if feature is applicable to this item - if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr) { + if (itemFeature != LinkEntry::DEFAULT && entry.Target) { auto const& featureAttributes = GetLinkLibraryFeatureAttributes( this->Makefile, this->LinkLanguage, itemFeature); if (featureAttributes.LibraryTypes.find(entry.Target->GetType()) == @@ -1156,8 +1189,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, } if (supportedItem) { - if (inGroup) { - const auto& currentFeature = this->EntryList[groupIndex.first].Feature; + if (group) { + const auto& currentFeature = this->EntryList[group->first].Feature; for (const auto& g : this->GroupItems) { const auto& groupFeature = this->EntryList[g.first].Feature; if (groupFeature == currentFeature) { @@ -1228,17 +1261,17 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, } } - if (inGroup) { + if (group) { // store item index for dependencies handling groupItems.push_back(dependee_index); } else { std::vector indexes; bool entryHandled = false; // search any occurrence of the library in already defined groups - for (const auto& group : this->GroupItems) { - for (auto index : group.second) { + for (const auto& g : this->GroupItems) { + for (auto index : g.second) { if (entry.Item.Value == this->EntryList[index].Item.Value) { - indexes.push_back(group.first); + indexes.push_back(g.first); entryHandled = true; break; } @@ -1250,8 +1283,8 @@ void cmComputeLinkDepends::AddLinkEntries(size_t depender_index, for (auto index : indexes) { // The dependee must come after the depender. - if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { - this->EntryConstraintGraph[depender_index].emplace_back( + if (depender_index) { + this->EntryConstraintGraph[*depender_index].emplace_back( index, false, false, cmListFileBacktrace()); } else { // This is a direct dependency of the target being linked. @@ -1294,14 +1327,14 @@ void cmComputeLinkDepends::AddLinkObjects(std::vector const& objs) } } -cmLinkItem cmComputeLinkDepends::ResolveLinkItem(size_t depender_index, - const std::string& name) +cmLinkItem cmComputeLinkDepends::ResolveLinkItem( + cm::optional const& depender_index, const std::string& name) { // Look for a target in the scope of the depender. cmGeneratorTarget const* from = this->Target; - if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) { + if (depender_index) { if (cmGeneratorTarget const* depender = - this->EntryList[depender_index].Target) { + this->EntryList[*depender_index].Target) { from = depender; } } @@ -1477,9 +1510,8 @@ void cmComputeLinkDepends::OrderLinkEntries() this->ComponentOrderId = n; // Run in reverse order so the topological order will preserve the // original order where there are no constraints. - for (size_t c = n - 1; c != cmComputeComponentGraph::INVALID_COMPONENT; - --c) { - this->VisitComponent(c); + for (size_t c = n; c > 0; --c) { + this->VisitComponent(c - 1); } // Display the component graph. @@ -1488,8 +1520,22 @@ void cmComputeLinkDepends::OrderLinkEntries() } // Start with the original link line. - for (size_t originalEntry : this->OriginalEntries) { - this->VisitEntry(originalEntry); + switch (this->Strategy) { + case LinkLibrariesStrategy::REORDER_MINIMALLY: { + // Emit the direct dependencies in their original order. + // This gives projects control over ordering. + for (size_t originalEntry : this->OriginalEntries) { + this->VisitEntry(originalEntry); + } + } break; + case LinkLibrariesStrategy::REORDER_FREELY: { + // Schedule the direct dependencies for emission in topo order. + // This may produce more efficient link lines. + for (size_t originalEntry : this->OriginalEntries) { + this->MakePendingComponent( + this->CCG->GetComponentMap()[originalEntry]); + } + } break; } // Now explore anything left pending. Since the component graph is @@ -1654,26 +1700,49 @@ size_t cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl) return count; } -void cmComputeLinkDepends::DisplayFinalEntries() +namespace { +void DisplayLinkEntry(int& count, cmComputeLinkDepends::LinkEntry const& entry) { - fprintf(stderr, "target [%s] links to:\n", this->Target->GetName().c_str()); - char space[] = " "; - int count = 2; - for (LinkEntry const& lei : this->FinalLinkEntries) { - if (lei.Kind == LinkEntry::Group) { - fprintf(stderr, " %s group", - lei.Item.Value == "" ? "start" : "end"); - count = lei.Item.Value == "" ? 4 : 2; - } else if (lei.Target) { - fprintf(stderr, "%*starget [%s]", count, space, - lei.Target->GetName().c_str()); + if (entry.Kind == cmComputeLinkDepends::LinkEntry::Group) { + if (entry.Item.Value == LG_ITEM_BEGIN) { + fprintf(stderr, " start group"); + count = 4; + } else if (entry.Item.Value == LG_ITEM_END) { + fprintf(stderr, " end group"); + count = 2; } else { - fprintf(stderr, "%*sitem [%s]", count, space, lei.Item.Value.c_str()); + fprintf(stderr, " group"); } - if (lei.Feature != LinkEntry::DEFAULT) { - fprintf(stderr, ", feature [%s]", lei.Feature.c_str()); - } - fprintf(stderr, "\n"); + } else if (entry.Target) { + fprintf(stderr, "%*starget [%s]", count, "", + entry.Target->GetName().c_str()); + } else { + fprintf(stderr, "%*sitem [%s]", count, "", entry.Item.Value.c_str()); + } + if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) { + fprintf(stderr, ", feature [%s]", entry.Feature.c_str()); + } + fprintf(stderr, "\n"); +} +} + +void cmComputeLinkDepends::DisplayOrderedEntries() +{ + fprintf(stderr, "target [%s] link dependency ordering:\n", + this->Target->GetName().c_str()); + int count = 2; + for (auto index : this->FinalLinkOrder) { + DisplayLinkEntry(count, this->EntryList[index]); + } + fprintf(stderr, "\n"); +} + +void cmComputeLinkDepends::DisplayFinalEntries() +{ + fprintf(stderr, "target [%s] link line:\n", this->Target->GetName().c_str()); + int count = 2; + for (LinkEntry const& entry : this->FinalLinkEntries) { + DisplayLinkEntry(count, entry); } fprintf(stderr, "\n"); } diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 66daa071d..dc2fa4591 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -13,18 +13,26 @@ #include #include -#include "cmComputeComponentGraph.h" +#include + #include "cmGraphAdjacencyList.h" #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmTargetLinkLibraryType.h" +class cmComputeComponentGraph; class cmGeneratorTarget; class cmGlobalGenerator; class cmMakefile; class cmSourceFile; class cmake; +enum class LinkLibrariesStrategy +{ + REORDER_MINIMALLY, + REORDER_FREELY, +}; + /** \class cmComputeLinkDepends * \brief Compute link dependencies for targets. */ @@ -33,7 +41,8 @@ class cmComputeLinkDepends public: cmComputeLinkDepends(cmGeneratorTarget const* target, const std::string& config, - const std::string& linkLanguage); + const std::string& linkLanguage, + LinkLibrariesStrategy strategy); ~cmComputeLinkDepends(); cmComputeLinkDepends(const cmComputeLinkDepends&) = delete; @@ -84,12 +93,16 @@ public: private: // Context information. - cmGeneratorTarget const* Target; - cmMakefile* Makefile; - cmGlobalGenerator const* GlobalGenerator; + cmGeneratorTarget const* Target = nullptr; + cmMakefile* Makefile = nullptr; + cmGlobalGenerator const* GlobalGenerator = nullptr; cmake* CMakeInstance; - std::string LinkLanguage; std::string Config; + bool DebugMode = false; + std::string LinkLanguage; + cmTargetLinkLibraryType LinkType; + LinkLibrariesStrategy Strategy; + EntryVector FinalLinkEntries; std::map LinkLibraryOverride; @@ -98,16 +111,18 @@ private: std::pair::iterator, bool> AllocateLinkEntry( cmLinkItem const& item); - std::pair AddLinkEntry( - cmLinkItem const& item, - size_t groupIndex = cmComputeComponentGraph::INVALID_COMPONENT); + std::pair AddLinkEntry(cmLinkItem const& item, + cm::optional const& groupIndex); void AddLinkObject(cmLinkItem const& item); - void AddVarLinkEntries(size_t depender_index, const char* value); + void AddVarLinkEntries(cm::optional const& depender_index, + const char* value); void AddDirectLinkEntries(); template - void AddLinkEntries(size_t depender_index, std::vector const& libs); + void AddLinkEntries(cm::optional const& depender_index, + std::vector const& libs); void AddLinkObjects(std::vector const& objs); - cmLinkItem ResolveLinkItem(size_t depender_index, const std::string& name); + cmLinkItem ResolveLinkItem(cm::optional const& depender_index, + const std::string& name); // One entry for each unique item. std::vector EntryList; @@ -120,7 +135,7 @@ private: struct BFSEntry { size_t Index; - size_t GroupIndex; + cm::optional GroupIndex; const char* LibDepends; }; std::queue BFSQueue; @@ -192,6 +207,7 @@ private: void VisitEntry(size_t index); PendingComponent& MakePendingComponent(size_t component); size_t ComputeComponentCount(NodeList const& nl); + void DisplayOrderedEntries(); void DisplayFinalEntries(); // Record of the original link line. @@ -203,8 +219,5 @@ private: std::vector ObjectEntries; size_t ComponentOrderId; - cmTargetLinkLibraryType LinkType; - bool HasConfig; - bool DebugMode; - bool OldLinkDirMode; + bool OldLinkDirMode = false; }; diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index b631610e9..c2c210ba2 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -567,8 +568,25 @@ bool cmComputeLinkInformation::Compute() return false; } + LinkLibrariesStrategy strategy = LinkLibrariesStrategy::REORDER_MINIMALLY; + if (cmValue s = this->Target->GetProperty("LINK_LIBRARIES_STRATEGY")) { + if (*s == "REORDER_MINIMALLY"_s) { + strategy = LinkLibrariesStrategy::REORDER_MINIMALLY; + } else if (*s == "REORDER_FREELY"_s) { + strategy = LinkLibrariesStrategy::REORDER_FREELY; + } else { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("LINK_LIBRARIES_STRATEGY value '", *s, + "' is not recognized."), + this->Target->GetBacktrace()); + return false; + } + } + // Compute the ordered link line items. - cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage); + cmComputeLinkDepends cld(this->Target, this->Config, this->LinkLanguage, + strategy); cld.SetOldLinkDirMode(this->OldLinkDirMode); cmComputeLinkDepends::EntryVector const& linkEntries = cld.Compute(); FeatureDescriptor const* currentFeature = nullptr; @@ -578,8 +596,7 @@ bool cmComputeLinkInformation::Compute() if (linkEntry.Kind == cmComputeLinkDepends::LinkEntry::Group) { const auto& groupFeature = this->GetGroupFeature(linkEntry.Feature); if (groupFeature.Supported) { - if (linkEntry.Item.Value == "" && - currentFeature != nullptr) { + if (linkEntry.Item.Value == "" && currentFeature) { // emit feature suffix, if any if (!currentFeature->Suffix.empty()) { this->Items.emplace_back( @@ -599,8 +616,7 @@ bool cmComputeLinkInformation::Compute() continue; } - if (currentFeature != nullptr && - linkEntry.Feature != currentFeature->Name) { + if (currentFeature && linkEntry.Feature != currentFeature->Name) { // emit feature suffix, if any if (!currentFeature->Suffix.empty()) { this->Items.emplace_back( @@ -612,8 +628,7 @@ bool cmComputeLinkInformation::Compute() } if (linkEntry.Feature != DEFAULT && - (currentFeature == nullptr || - linkEntry.Feature != currentFeature->Name)) { + (!currentFeature || linkEntry.Feature != currentFeature->Name)) { if (!this->AddLibraryFeature(linkEntry.Feature)) { continue; } @@ -633,7 +648,7 @@ bool cmComputeLinkInformation::Compute() } } - if (currentFeature != nullptr) { + if (currentFeature) { // emit feature suffix, if any if (!currentFeature->Suffix.empty()) { this->Items.emplace_back( @@ -738,13 +753,13 @@ public: private: std::string ExpandVariable(std::string const& variable) override { - if (this->Library != nullptr && variable == "LIBRARY") { + if (this->Library && variable == "LIBRARY") { return *this->Library; } - if (this->LibItem != nullptr && variable == "LIB_ITEM") { + if (this->LibItem && variable == "LIB_ITEM") { return *this->LibItem; } - if (this->LinkItem != nullptr && variable == "LINK_ITEM") { + if (this->LinkItem && variable == "LINK_ITEM") { return *this->LinkItem; } diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index 2a0653003..97db90df8 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -77,7 +77,7 @@ public: BT GetFormattedItem(std::string const& path) const { - return { (this->Feature != nullptr) + return { this->Feature ? this->Feature->GetDecoratedItem(path, this->IsPath) : path, Value.Backtrace }; diff --git a/Source/cmCryptoHash.cxx b/Source/cmCryptoHash.cxx index 55e3a5b51..1b5c3a10e 100644 --- a/Source/cmCryptoHash.cxx +++ b/Source/cmCryptoHash.cxx @@ -79,7 +79,7 @@ std::unique_ptr cmCryptoHash::New(cm::string_view algo) if (algo == "SHA3_512") { return cm::make_unique(AlgoSHA3_512); } - return std::unique_ptr(nullptr); + return std::unique_ptr(); } std::string cmCryptoHash::GetHashAlgoName() const diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx index 51b95c3c1..b9133ed7d 100644 --- a/Source/cmCurl.cxx +++ b/Source/cmCurl.cxx @@ -152,6 +152,15 @@ std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile) check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: "); } # undef CMAKE_CAPATH_COMMON +# ifdef _AIX +# define CMAKE_CAPATH_AIX "/var/ssl/certs" + if (cmSystemTools::FileIsDirectory(CMAKE_CAPATH_AIX)) { + ::CURLcode res = + ::curl_easy_setopt(curl, CURLOPT_CAPATH, CMAKE_CAPATH_AIX); + check_curl_result(res, "Unable to set TLS/SSL Verify CAPATH: "); + } +# undef CMAKE_CAPATH_AIX +# endif } #endif return e; diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index 167e601e7..6f63d0a16 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -132,6 +132,10 @@ public: const std::string& GetTarget() const; void SetTarget(const std::string& target); + /** Record if the custom command can be used for code generation. */ + bool GetCodegen() const { return Codegen; } + void SetCodegen(bool b) { Codegen = b; } + private: std::vector Outputs; std::vector Byproducts; @@ -153,6 +157,7 @@ private: bool StdPipesUTF8 = false; bool HasMainDependency_ = false; bool DependsExplicitOnly = false; + bool Codegen = false; // Policies are NEW for synthesized custom commands, and set by cmMakefile for // user-created custom commands. diff --git a/Source/cmDebuggerVariables.cxx b/Source/cmDebuggerVariables.cxx index 8acce346c..8e3c2be87 100644 --- a/Source/cmDebuggerVariables.cxx +++ b/Source/cmDebuggerVariables.cxx @@ -62,7 +62,7 @@ cmDebuggerVariables::cmDebuggerVariables( void cmDebuggerVariables::AddSubVariables( std::shared_ptr const& variables) { - if (variables != nullptr) { + if (variables) { SubVariables.emplace_back(variables); } } @@ -71,7 +71,7 @@ dap::array cmDebuggerVariables::HandleVariablesRequest() { dap::array variables; - if (GetKeyValuesFunction != nullptr) { + if (GetKeyValuesFunction) { auto values = GetKeyValuesFunction(); for (auto const& entry : values) { if (IgnoreEmptyStringEntries && entry.Type == "string" && diff --git a/Source/cmDebuggerVariables.h b/Source/cmDebuggerVariables.h index eaaf2a8f4..753b81111 100644 --- a/Source/cmDebuggerVariables.h +++ b/Source/cmDebuggerVariables.h @@ -45,7 +45,7 @@ struct cmDebuggerVariableEntry } cmDebuggerVariableEntry(std::string name, const char* value) : Name(std::move(name)) - , Value(value == nullptr ? "" : value) + , Value(value ? value : "") , Type("string") { } diff --git a/Source/cmDebuggerVariablesHelper.cxx b/Source/cmDebuggerVariablesHelper.cxx index 73f91116e..6880eabd2 100644 --- a/Source/cmDebuggerVariablesHelper.cxx +++ b/Source/cmDebuggerVariablesHelper.cxx @@ -193,7 +193,7 @@ std::shared_ptr cmDebuggerVariablesHelper::CreateIfAny( std::shared_ptr const& variablesManager, std::string const& name, bool supportsVariableType, cmFileSet* fileSet) { - if (fileSet == nullptr) { + if (!fileSet) { return {}; } @@ -471,7 +471,7 @@ std::shared_ptr cmDebuggerVariablesHelper::CreateIfAny( std::shared_ptr const& variablesManager, std::string const& name, bool supportsVariableType, cmTest* test) { - if (test == nullptr) { + if (!test) { return {}; } @@ -519,7 +519,7 @@ std::shared_ptr cmDebuggerVariablesHelper::CreateIfAny( std::shared_ptr const& variablesManager, std::string const& name, bool supportsVariableType, cmMakefile* mf) { - if (mf == nullptr) { + if (!mf) { return {}; } @@ -575,7 +575,7 @@ std::shared_ptr cmDebuggerVariablesHelper::CreateIfAny( std::shared_ptr const& variablesManager, std::string const& name, bool supportsVariableType, cmGlobalGenerator* gen) { - if (gen == nullptr) { + if (!gen) { return {}; } @@ -624,10 +624,9 @@ std::shared_ptr cmDebuggerVariablesHelper::CreateIfAny( return ret; }); - if (gen->GetInstallComponents() != nullptr) { - variables->AddSubVariables( - CreateIfAny(variablesManager, "InstallComponents", supportsVariableType, - *gen->GetInstallComponents())); + if (const auto* ic = gen->GetInstallComponents()) { + variables->AddSubVariables(CreateIfAny( + variablesManager, "InstallComponents", supportsVariableType, *ic)); } variables->SetIgnoreEmptyStringEntries(true); diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index 04bccce5e..34f67d064 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -154,7 +154,7 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends, dependee = line.substr(1); // Add dependee to depender's list - if (currentDependencies != nullptr) { + if (currentDependencies) { currentDependencies->push_back(dependee); } @@ -210,7 +210,7 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends, // Remove the information of this depender from the map, it needs // to be rescanned - if (currentDependencies != nullptr) { + if (currentDependencies) { validDeps.erase(depender); currentDependencies = nullptr; } diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index 408a85b32..a39bb372c 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -96,7 +96,7 @@ bool cmDependsC::WriteDependencies(const std::set& sources, // loaded in ValidDeps with this path as a key. std::string obj_i = this->LocalGenerator->MaybeRelativeToTopBinDir(obj); - if (this->ValidDeps != nullptr) { + if (this->ValidDeps) { auto const tmpIt = this->ValidDeps->find(obj_i); if (tmpIt != this->ValidDeps->end()) { dependencies.insert(tmpIt->second.begin(), tmpIt->second.end()); @@ -295,7 +295,7 @@ void cmDependsC::ReadCacheFile() } } } - } else if (cacheEntry != nullptr) { + } else if (cacheEntry) { UnscannedEntry entry; entry.FileName = line; if (cmSystemTools::GetLineFromStream(fin, line)) { diff --git a/Source/cmDependsCompiler.cxx b/Source/cmDependsCompiler.cxx index 27f328d0b..17ec43bf1 100644 --- a/Source/cmDependsCompiler.cxx +++ b/Source/cmDependsCompiler.cxx @@ -63,7 +63,7 @@ bool cmDependsCompiler::CheckDependencies( continue; } // This is a dependee line - if (currentDependencies != nullptr) { + if (currentDependencies) { currentDependencies->emplace_back(line.substr(1)); } } diff --git a/Source/cmDependsJavaParserHelper.cxx b/Source/cmDependsJavaParserHelper.cxx index 6e617f60f..e2718e4dd 100644 --- a/Source/cmDependsJavaParserHelper.cxx +++ b/Source/cmDependsJavaParserHelper.cxx @@ -309,7 +309,7 @@ void cmDependsJavaParserHelper::Error(const char* str) void cmDependsJavaParserHelper::UpdateCombine(const char* str1, const char* str2) { - if (this->CurrentCombine.empty() && str1 != nullptr) { + if (this->CurrentCombine.empty() && str1) { this->CurrentCombine = str1; } this->CurrentCombine += "."; diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index bfe3a75ea..1c05f25fe 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx @@ -16,6 +16,7 @@ #include +#include "cmBuildDatabase.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" #include "cmFileSet.h" @@ -39,13 +40,55 @@ namespace { -Json::Value CollationInformationCxxModules( - cmGeneratorTarget const* gt, std::string const& config, - cmDyndepGeneratorCallbacks const& cb) +struct TdiSourceInfo { + Json::Value Sources; + Json::Value CxxModules; +}; + +TdiSourceInfo CollationInformationSources(cmGeneratorTarget const* gt, + std::string const& config, + cmDyndepGeneratorCallbacks const& cb) +{ + TdiSourceInfo info; cmTarget const* tgt = gt->Target; auto all_file_sets = tgt->GetAllFileSetNames(); - Json::Value tdi_cxx_module_info = Json::objectValue; + Json::Value& tdi_sources = info.Sources = Json::objectValue; + Json::Value& tdi_cxx_module_info = info.CxxModules = Json::objectValue; + + enum class CompileType + { + ObjectAndBmi, + BmiOnly, + }; + std::map> sf_map; + { + auto fill_sf_map = [gt, tgt, &sf_map](cmSourceFile const* sf, + CompileType type) { + auto full_path = sf->GetFullPath(); + if (full_path.empty()) { + gt->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" has a full path-less source file.")); + return; + } + sf_map[full_path] = std::make_pair(sf, type); + }; + + std::vector objectSources; + gt->GetObjectSources(objectSources, config); + for (auto const* sf : objectSources) { + fill_sf_map(sf, CompileType::ObjectAndBmi); + } + + std::vector cxxModuleSources; + gt->GetCxxModuleSources(cxxModuleSources, config); + for (auto const* sf : cxxModuleSources) { + fill_sf_map(sf, CompileType::BmiOnly); + } + } + for (auto const& file_set_name : all_file_sets) { auto const* file_set = tgt->GetFileSet(file_set_name); if (!file_set) { @@ -73,39 +116,6 @@ Json::Value CollationInformationCxxModules( gt->LocalGenerator, config, gt); } - enum class CompileType - { - ObjectAndBmi, - BmiOnly, - }; - std::map> sf_map; - { - auto fill_sf_map = [gt, tgt, &sf_map](cmSourceFile const* sf, - CompileType type) { - auto full_path = sf->GetFullPath(); - if (full_path.empty()) { - gt->Makefile->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" has a full path-less source file.")); - return; - } - sf_map[full_path] = std::make_pair(sf, type); - }; - - std::vector objectSources; - gt->GetObjectSources(objectSources, config); - for (auto const* sf : objectSources) { - fill_sf_map(sf, CompileType::ObjectAndBmi); - } - - std::vector cxxModuleSources; - gt->GetCxxModuleSources(cxxModuleSources, config); - for (auto const* sf : cxxModuleSources) { - fill_sf_map(sf, CompileType::BmiOnly); - } - } - Json::Value fs_dest = Json::nullValue; for (auto const& ig : gt->Makefile->GetInstallGenerators()) { if (auto const* fsg = @@ -134,6 +144,8 @@ Json::Value CollationInformationCxxModules( auto const* sf = lookup->second.first; CompileType const ct = lookup->second.second; + sf_map.erase(lookup); + if (!sf) { gt->Makefile->IssueMessage( MessageType::INTERNAL_ERROR, @@ -160,7 +172,40 @@ Json::Value CollationInformationCxxModules( } } - return tdi_cxx_module_info; + for (auto const& sf_entry : sf_map) { + CompileType const ct = sf_entry.second.second; + if (ct == CompileType::BmiOnly) { + continue; + } + + auto const* sf = sf_entry.second.first; + if (!gt->NeedDyndepForSource(sf->GetLanguage(), config, sf)) { + continue; + } + + auto full_file = cmSystemTools::CollapseFullPath(sf->GetFullPath()); + auto obj_path = cb.ObjectFilePath(sf, config); + Json::Value& tdi_source_info = tdi_sources[obj_path] = Json::objectValue; + + tdi_source_info["source"] = full_file; + tdi_source_info["language"] = sf->GetLanguage(); + } + + return info; +} + +Json::Value CollationInformationDatabaseInfo(cmGeneratorTarget const* gt, + std::string const& config) +{ + Json::Value db_info; + + auto db_path = gt->BuildDatabasePath("CXX", config); + if (!db_path.empty()) { + db_info["template-path"] = cmStrCat(db_path, ".in"); + db_info["output"] = db_path; + } + + return db_info; } Json::Value CollationInformationBmiInstallation(cmGeneratorTarget const* gt, @@ -289,12 +334,21 @@ void cmDyndepCollation::AddCollationInformation( Json::Value& tdi, cmGeneratorTarget const* gt, std::string const& config, cmDyndepGeneratorCallbacks const& cb) { - tdi["cxx-modules"] = CollationInformationCxxModules(gt, config, cb); + auto sourcesInfo = CollationInformationSources(gt, config, cb); + tdi["sources"] = sourcesInfo.Sources; + tdi["cxx-modules"] = sourcesInfo.CxxModules; + tdi["database-info"] = CollationInformationDatabaseInfo(gt, config); tdi["bmi-installation"] = CollationInformationBmiInstallation(gt, config); tdi["exports"] = CollationInformationExports(gt); tdi["config"] = config; } +struct SourceInfo +{ + std::string SourcePath; + std::string Language; +}; + struct CxxModuleFileSet { std::string Name; @@ -306,6 +360,12 @@ struct CxxModuleFileSet cm::optional Destination; }; +struct CxxModuleDatabaseInfo +{ + std::string TemplatePath; + std::string Output; +}; + struct CxxModuleBmiInstall { std::string Component; @@ -330,7 +390,9 @@ struct CxxModuleExport struct cmCxxModuleExportInfo { + std::map ObjectToSource; std::map ObjectToFileSet; + cm::optional DatabaseInfo; cm::optional BmiInstallation; std::vector Exports; std::string Config; @@ -367,6 +429,15 @@ cmDyndepCollation::ParseExportInfo(Json::Value const& tdi) export_info->Exports.push_back(exp); } } + auto const& database_info = tdi["database-info"]; + if (database_info.isObject()) { + CxxModuleDatabaseInfo db_info; + + db_info.TemplatePath = database_info["template-path"].asString(); + db_info.Output = database_info["output"].asString(); + + export_info->DatabaseInfo = db_info; + } auto const& bmi_installation = tdi["bmi-installation"]; if (bmi_installation.isObject()) { CxxModuleBmiInstall bmi_install; @@ -405,6 +476,15 @@ cmDyndepCollation::ParseExportInfo(Json::Value const& tdi) } } } + Json::Value const& tdi_sources = tdi["sources"]; + if (tdi_sources.isObject()) { + for (auto i = tdi_sources.begin(); i != tdi_sources.end(); ++i) { + SourceInfo& si = export_info->ObjectToSource[i.key().asString()]; + auto const& tdi_source = *i; + si.SourcePath = tdi_source["source"].asString(); + si.Language = tdi_source["language"].asString(); + } + } return export_info; } @@ -446,6 +526,21 @@ bool cmDyndepCollation::WriteDyndepMetadata( exports.emplace_back(std::move(properties), &exp); } + std::unique_ptr module_database; + cmBuildDatabase::LookupTable build_database_lookup; + if (export_info.DatabaseInfo) { + module_database = + cmBuildDatabase::Load(export_info.DatabaseInfo->TemplatePath); + if (module_database) { + build_database_lookup = module_database->GenerateLookupTable(); + } else { + cmSystemTools::Error( + cmStrCat("Failed to read the template build database ", + export_info.DatabaseInfo->TemplatePath)); + result = false; + } + } + std::unique_ptr bmi_install_script; if (export_info.BmiInstallation) { bmi_install_script = cm::make_unique( @@ -475,6 +570,25 @@ bool cmDyndepCollation::WriteDyndepMetadata( #ifdef _WIN32 cmSystemTools::ConvertToUnixSlashes(output_path); #endif + + auto source_info_itr = export_info.ObjectToSource.find(output_path); + + // Update the module compilation database `requires` field if needed. + if (source_info_itr != export_info.ObjectToSource.end()) { + auto const& sourcePath = source_info_itr->second.SourcePath; + auto bdb_entry = build_database_lookup.find(sourcePath); + if (bdb_entry != build_database_lookup.end()) { + bdb_entry->second->Requires.clear(); + for (auto const& req : object.Requires) { + bdb_entry->second->Requires.push_back(req.LogicalName); + } + } else if (export_info.DatabaseInfo) { + cmSystemTools::Error( + cmStrCat("Failed to find module database entry for ", sourcePath)); + result = false; + } + } + // Find the fileset for this object. auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path); bool const has_provides = !object.Provides.empty(); @@ -499,6 +613,31 @@ bool cmDyndepCollation::WriteDyndepMetadata( auto const& file_set = fileset_info_itr->second; + // Update the module compilation database `provides` field if needed. + { + auto bdb_entry = build_database_lookup.find(file_set.SourcePath); + if (bdb_entry != build_database_lookup.end()) { + // Clear the provides mapping; we will re-initialize it here. + if (!object.Provides.empty()) { + bdb_entry->second->Provides.clear(); + } + for (auto const& prov : object.Provides) { + auto bmiName = cb.ModuleFile(prov.LogicalName); + if (bmiName) { + bdb_entry->second->Provides[prov.LogicalName] = *bmiName; + } else { + cmSystemTools::Error( + cmStrCat("Failed to find BMI location for ", prov.LogicalName)); + result = false; + } + } + } else if (export_info.DatabaseInfo) { + cmSystemTools::Error(cmStrCat( + "Failed to find module database entry for ", file_set.SourcePath)); + result = false; + } + } + // Verify the fileset type for the object. if (file_set.Type == "CXX_MODULES"_s) { if (!has_provides) { @@ -660,6 +799,16 @@ bool cmDyndepCollation::WriteDyndepMetadata( } } + if (module_database) { + if (module_database->HasPlaceholderNames()) { + cmSystemTools::Error( + "Module compilation database still contains placeholders"); + result = false; + } else { + module_database->Write(export_info.DatabaseInfo->Output); + } + } + return result; } diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx index a71e5f128..003f47b43 100644 --- a/Source/cmELF.cxx +++ b/Source/cmELF.cxx @@ -112,6 +112,9 @@ public: virtual bool IsMips() const = 0; virtual void PrintInfo(std::ostream& os) const = 0; + /** Returns true if the ELF file has a dynamic section **/ + bool HasDynamicSection() const { return this->DynamicSectionIndex >= 0; } + // Lookup the SONAME in the DYNAMIC section. StringEntry const* GetSOName() { @@ -461,7 +464,7 @@ template bool cmELFInternalImpl::LoadDynamicSection() { // If there is no dynamic section we are done. - if (this->DynamicSectionIndex < 0) { + if (!this->HasDynamicSection()) { return false; } @@ -772,6 +775,11 @@ std::vector cmELF::EncodeDynamicEntries( return std::vector(); } +bool cmELF::HasDynamicSection() const +{ + return this->Valid() && this->Internal->HasDynamicSection(); +} + bool cmELF::GetSOName(std::string& soname) { if (StringEntry const* se = this->GetSOName()) { diff --git a/Source/cmELF.h b/Source/cmELF.h index ce8bd7fb9..dd37c6530 100644 --- a/Source/cmELF.h +++ b/Source/cmELF.h @@ -88,6 +88,9 @@ public: std::vector EncodeDynamicEntries( const DynamicEntryList& entries) const; + /** Returns true if the ELF file has a dynamic section **/ + bool HasDynamicSection() const; + /** Get the SONAME field if any. */ bool GetSOName(std::string& soname); StringEntry const* GetSOName(); diff --git a/Source/cmExecProgramCommand.cxx b/Source/cmExecProgramCommand.cxx index e069b7712..21ea4633e 100644 --- a/Source/cmExecProgramCommand.cxx +++ b/Source/cmExecProgramCommand.cxx @@ -202,9 +202,7 @@ bool RunCommand(std::string command, std::string& output, int& retVal, # endif command = commandInDir; if (verbose) { - cmSystemTools::Stdout("running "); - cmSystemTools::Stdout(command); - cmSystemTools::Stdout("\n"); + cmSystemTools::Stdout(cmStrCat("running ", command, '\n')); } fflush(stdout); fflush(stderr); diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 2b923dfbf..555977c13 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -69,7 +71,7 @@ bool cmExecuteProcessCommand(std::vector const& args, bool ErrorStripTrailingWhitespace = false; bool EchoOutputVariable = false; bool EchoErrorVariable = false; - std::string Encoding; + cm::optional Encoding; std::string CommandErrorIsFatal; }; @@ -296,8 +298,24 @@ bool cmExecuteProcessCommand(std::vector const& args, }; ReadData outputData; ReadData errorData; - cmProcessOutput processOutput( - cmProcessOutput::FindEncoding(arguments.Encoding)); + cmPolicies::PolicyStatus const cmp0176 = + status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0176); + cmProcessOutput::Encoding encoding = + cmp0176 == cmPolicies::OLD || cmp0176 == cmPolicies::WARN + ? cmProcessOutput::Auto + : cmProcessOutput::UTF8; + if (arguments.Encoding) { + if (cm::optional maybeEncoding = + cmProcessOutput::FindEncoding(*arguments.Encoding)) { + encoding = *maybeEncoding; + } else { + status.GetMakefile().IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("ENCODING option given unknown value \"", *arguments.Encoding, + "\". Ignoring.")); + } + } + cmProcessOutput processOutput(encoding); std::string strdata; std::unique_ptr outputHandle; diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx index a2e6e70c6..85eff1a87 100644 --- a/Source/cmExperimental.cxx +++ b/Source/cmExperimental.cxx @@ -30,7 +30,7 @@ cmExperimental::FeatureData LookupTable[] = { false }, // WindowsKernelModeDriver { "WindowsKernelModeDriver", - "5c2d848d-4efa-4529-a768-efd57171bf68", + "fac18f65-504e-4dbb-b068-f356bb1f2ddb", "CMAKE_EXPERIMENTAL_WINDOWS_KERNEL_MODE_DRIVER", "CMake's Windows kernel-mode driver support is experimental. It is meant " "only for experimentation and feedback to CMake developers.", @@ -46,6 +46,25 @@ cmExperimental::FeatureData LookupTable[] = { {}, cmExperimental::TryCompileCondition::Always, false }, + // ExportPackageInfo + { "ExportPackageInfo", + "b80be207-778e-46ba-8080-b23bba22639e", + "CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO", + "CMake's support for exporting package information in the Common Package " + "Specification format. It is meant only for experimentation and feedback " + "to CMake developers.", + {}, + cmExperimental::TryCompileCondition::Always, + false }, + // ExportBuildDatabase + { "ExportBuildDatabase", + "4bd552e2-b7fb-429a-ab23-c83ef53f3f13", + "CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE", + "CMake's support for exporting build databases is experimental. It is " + "meant only for experimentation and feedback to CMake developers.", + {}, + cmExperimental::TryCompileCondition::Never, + false }, }; static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == static_cast(cmExperimental::Feature::Sentinel), diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h index 05764f947..875491c7c 100644 --- a/Source/cmExperimental.h +++ b/Source/cmExperimental.h @@ -20,6 +20,8 @@ public: ExportPackageDependencies, WindowsKernelModeDriver, CxxImportStd, + ExportPackageInfo, + ExportBuildDatabase, Sentinel, }; diff --git a/Source/cmExportAndroidMKGenerator.cxx b/Source/cmExportAndroidMKGenerator.cxx new file mode 100644 index 000000000..34dc1a792 --- /dev/null +++ b/Source/cmExportAndroidMKGenerator.cxx @@ -0,0 +1,165 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportAndroidMKGenerator.h" + +#include +#include +#include + +#include +#include + +#include "cmGeneratorTarget.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +cmExportAndroidMKGenerator::cmExportAndroidMKGenerator() = default; + +cm::string_view cmExportAndroidMKGenerator::GetImportPrefixWithSlash() const +{ + return "$(_IMPORT_PREFIX)/"_s; +} + +bool cmExportAndroidMKGenerator::GenerateImportFile(std::ostream& os) +{ + if (!this->AppendMode) { + // Start with the import file header. + this->GenerateImportHeaderCode(os); + } + + // Create all the imported targets. + std::stringstream mainFileBuffer; + bool result = this->GenerateMainFile(mainFileBuffer); + + // Write cached import code. + os << mainFileBuffer.rdbuf(); + + return result; +} + +void cmExportAndroidMKGenerator::GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + ImportPropertyMap const& properties) +{ + std::string const config = + (this->Configurations.empty() ? std::string{} : this->Configurations[0]); + GenerateType const type = this->GetGenerateType(); + + bool const newCMP0022Behavior = + target->GetPolicyStatusCMP0022() != cmPolicies::WARN && + target->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (!newCMP0022Behavior) { + std::ostringstream w; + if (type == BUILD) { + w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022"; + } else { + w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022"; + } + w << " set to OLD for target " << target->Target->GetName() << ". " + << "The export will only work with CMP0022 set to NEW."; + target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } + if (!properties.empty()) { + os << "LOCAL_CPP_FEATURES := rtti exceptions\n"; + for (auto const& property : properties) { + if (property.first == "INTERFACE_COMPILE_OPTIONS") { + os << "LOCAL_CPP_FEATURES += "; + os << (property.second) << "\n"; + } else if (property.first == "INTERFACE_LINK_LIBRARIES") { + std::string staticLibs; + std::string sharedLibs; + std::string ldlibs; + cmLinkInterfaceLibraries const* linkIFace = + target->GetLinkInterfaceLibraries(config, target, + cmGeneratorTarget::UseTo::Link); + for (cmLinkItem const& item : linkIFace->Libraries) { + cmGeneratorTarget const* gt = item.Target; + std::string const& lib = item.AsStr(); + if (gt) { + + if (gt->GetType() == cmStateEnums::SHARED_LIBRARY || + gt->GetType() == cmStateEnums::MODULE_LIBRARY) { + sharedLibs += " " + lib; + } else { + staticLibs += " " + lib; + } + } else { + bool relpath = false; + if (type == INSTALL) { + relpath = cmHasLiteralPrefix(lib, "../"); + } + // check for full path or if it already has a -l, or + // in the case of an install check for relative paths + // if it is full or a link library then use string directly + if (cmSystemTools::FileIsFullPath(lib) || + cmHasLiteralPrefix(lib, "-l") || relpath) { + ldlibs += " " + lib; + // if it is not a path and does not have a -l then add -l + } else if (!lib.empty()) { + ldlibs += " -l" + lib; + } + } + } + if (!sharedLibs.empty()) { + os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n"; + } + if (!staticLibs.empty()) { + os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n"; + } + if (!ldlibs.empty()) { + os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n"; + } + } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") { + std::string includes = property.second; + cmList includeList{ includes }; + os << "LOCAL_EXPORT_C_INCLUDES := "; + std::string end; + for (std::string const& i : includeList) { + os << end << i; + end = "\\\n"; + } + os << "\n"; + } else if (property.first == "INTERFACE_LINK_OPTIONS") { + os << "LOCAL_EXPORT_LDFLAGS := "; + cmList linkFlagsList{ property.second }; + os << linkFlagsList.join(" ") << "\n"; + } else { + os << "# " << property.first << " " << (property.second) << "\n"; + } + } + } + + // Tell the NDK build system if prebuilt static libraries use C++. + if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { + cmLinkImplementation const* li = + target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link); + if (cm::contains(li->Languages, "CXX")) { + os << "LOCAL_HAS_CPP := true\n"; + } + } + + switch (target->GetType()) { + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + os << "include $(PREBUILT_SHARED_LIBRARY)\n"; + break; + case cmStateEnums::STATIC_LIBRARY: + os << "include $(PREBUILT_STATIC_LIBRARY)\n"; + break; + case cmStateEnums::EXECUTABLE: + case cmStateEnums::UTILITY: + case cmStateEnums::OBJECT_LIBRARY: + case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::INTERFACE_LIBRARY: + case cmStateEnums::UNKNOWN_LIBRARY: + break; + } + os << "\n"; +} diff --git a/Source/cmExportAndroidMKGenerator.h b/Source/cmExportAndroidMKGenerator.h new file mode 100644 index 000000000..fb0f03b20 --- /dev/null +++ b/Source/cmExportAndroidMKGenerator.h @@ -0,0 +1,73 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include +#include + +#include + +#include "cmExportFileGenerator.h" +#include "cmStateTypes.h" + +class cmGeneratorTarget; + +/** \class cmExportAndroidMKGenerator + * \brief Generate CMake configuration files exporting targets from a build or + * install tree. + * + * cmExportAndroidMKGenerator is the superclass for + * cmExportBuildAndroidMKGenerator and cmExportInstallAndroidMKGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +class cmExportAndroidMKGenerator : virtual public cmExportFileGenerator +{ +public: + cmExportAndroidMKGenerator(); + + using cmExportFileGenerator::GenerateImportFile; + +protected: + enum GenerateType + { + BUILD, + INSTALL + }; + virtual GenerateType GetGenerateType() const = 0; + + using ImportPropertyMap = std::map; + + cm::string_view GetImportPrefixWithSlash() const override; + + void GenerateInterfaceProperties(cmGeneratorTarget const* target, + std::ostream& os, + ImportPropertyMap const& properties); + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + virtual void GenerateImportHeaderCode(std::ostream& os, + std::string const& config = "") = 0; + virtual void GenerateImportTargetCode( + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) = 0; + + void GenerateImportTargetsConfig(std::ostream& /*os*/, + std::string const& /*config*/, + std::string const& /*suffix*/) override + { + } + + std::string GetCxxModuleFile(std::string const& /*name*/) const override + { + return {}; + } + + void GenerateCxxModuleConfigInformation(std::string const& /*name*/, + std::ostream& /*os*/) const override + { + } +}; diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index cbc05ddab..a5f6ca39d 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx @@ -2,43 +2,56 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportBuildAndroidMKGenerator.h" -#include #include -#include #include -#include - +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" -#include "cmLinkItem.h" -#include "cmList.h" -#include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" -cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() -{ - this->LG = nullptr; - this->ExportSet = nullptr; -} +cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() = default; -void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode( - std::ostream& os, const std::string&) +bool cmExportBuildAndroidMKGenerator::GenerateMainFile(std::ostream& os) { - os << "LOCAL_PATH := $(call my-dir)\n\n"; -} + if (!this->CollectExports([&](cmGeneratorTarget const*) {})) { + return false; + } -void cmExportBuildAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) -{ + // Create all the imported targets. + for (auto const& exp : this->Exports) { + cmGeneratorTarget* gte = exp.Target; + + this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); + + gte->Target->AppendBuildInterfaceIncludes(); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(gte, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gte, cmGeneratorExpression::BuildInterface, properties); + } + + this->GenerateInterfaceProperties(gte, os, properties); + } + + return true; } -void cmExportBuildAndroidMKGenerator::GenerateExpectedTargetsCode( - std::ostream&, const std::string&) +void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode( + std::ostream& os, std::string const&) { + os << "LOCAL_PATH := $(call my-dir)\n\n"; } void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( @@ -55,143 +68,3 @@ void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( cmSystemTools::ConvertToOutputPath(target->GetFullPath(noConfig)); os << path << "\n"; } - -void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, const std::string&, - cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) -{ -} - -void cmExportBuildAndroidMKGenerator::GenerateMissingTargetsCheckCode( - std::ostream&) -{ -} - -void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& properties) -{ - std::string config; - if (!this->Configurations.empty()) { - config = this->Configurations[0]; - } - cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - target, os, properties, cmExportBuildAndroidMKGenerator::BUILD, config); -} - -void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& properties, GenerateType type, - std::string const& config) -{ - const bool newCMP0022Behavior = - target->GetPolicyStatusCMP0022() != cmPolicies::WARN && - target->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (!newCMP0022Behavior) { - std::ostringstream w; - if (type == cmExportBuildAndroidMKGenerator::BUILD) { - w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022"; - } else { - w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022"; - } - w << " set to OLD for target " << target->Target->GetName() << ". " - << "The export will only work with CMP0022 set to NEW."; - target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); - } - if (!properties.empty()) { - os << "LOCAL_CPP_FEATURES := rtti exceptions\n"; - for (auto const& property : properties) { - if (property.first == "INTERFACE_COMPILE_OPTIONS") { - os << "LOCAL_CPP_FEATURES += "; - os << (property.second) << "\n"; - } else if (property.first == "INTERFACE_LINK_LIBRARIES") { - std::string staticLibs; - std::string sharedLibs; - std::string ldlibs; - cmLinkInterfaceLibraries const* linkIFace = - target->GetLinkInterfaceLibraries(config, target, - cmGeneratorTarget::UseTo::Link); - for (cmLinkItem const& item : linkIFace->Libraries) { - cmGeneratorTarget const* gt = item.Target; - std::string const& lib = item.AsStr(); - if (gt) { - - if (gt->GetType() == cmStateEnums::SHARED_LIBRARY || - gt->GetType() == cmStateEnums::MODULE_LIBRARY) { - sharedLibs += " " + lib; - } else { - staticLibs += " " + lib; - } - } else { - bool relpath = false; - if (type == cmExportBuildAndroidMKGenerator::INSTALL) { - relpath = cmHasLiteralPrefix(lib, "../"); - } - // check for full path or if it already has a -l, or - // in the case of an install check for relative paths - // if it is full or a link library then use string directly - if (cmSystemTools::FileIsFullPath(lib) || - cmHasLiteralPrefix(lib, "-l") || relpath) { - ldlibs += " " + lib; - // if it is not a path and does not have a -l then add -l - } else if (!lib.empty()) { - ldlibs += " -l" + lib; - } - } - } - if (!sharedLibs.empty()) { - os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n"; - } - if (!staticLibs.empty()) { - os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n"; - } - if (!ldlibs.empty()) { - os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n"; - } - } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") { - std::string includes = property.second; - cmList includeList{ includes }; - os << "LOCAL_EXPORT_C_INCLUDES := "; - std::string end; - for (std::string const& i : includeList) { - os << end << i; - end = "\\\n"; - } - os << "\n"; - } else if (property.first == "INTERFACE_LINK_OPTIONS") { - os << "LOCAL_EXPORT_LDFLAGS := "; - cmList linkFlagsList{ property.second }; - os << linkFlagsList.join(" ") << "\n"; - } else { - os << "# " << property.first << " " << (property.second) << "\n"; - } - } - } - - // Tell the NDK build system if prebuilt static libraries use C++. - if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { - cmLinkImplementation const* li = - target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link); - if (cm::contains(li->Languages, "CXX")) { - os << "LOCAL_HAS_CPP := true\n"; - } - } - - switch (target->GetType()) { - case cmStateEnums::SHARED_LIBRARY: - case cmStateEnums::MODULE_LIBRARY: - os << "include $(PREBUILT_SHARED_LIBRARY)\n"; - break; - case cmStateEnums::STATIC_LIBRARY: - os << "include $(PREBUILT_STATIC_LIBRARY)\n"; - break; - case cmStateEnums::EXECUTABLE: - case cmStateEnums::UTILITY: - case cmStateEnums::OBJECT_LIBRARY: - case cmStateEnums::GLOBAL_TARGET: - case cmStateEnums::INTERFACE_LIBRARY: - case cmStateEnums::UNKNOWN_LIBRARY: - break; - } - os << "\n"; -} diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index 9562cee93..deb389354 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -7,6 +7,7 @@ #include #include +#include "cmExportAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmStateTypes.h" @@ -21,42 +22,26 @@ class cmGeneratorTarget; * * This is used to implement the export() command. */ -class cmExportBuildAndroidMKGenerator : public cmExportBuildFileGenerator +class cmExportBuildAndroidMKGenerator + : public cmExportBuildFileGenerator + , public cmExportAndroidMKGenerator { public: cmExportBuildAndroidMKGenerator(); - // this is so cmExportInstallAndroidMKGenerator can share this - // function as they are almost the same - enum GenerateType - { - BUILD, - INSTALL - }; - static void GenerateInterfaceProperties(cmGeneratorTarget const* target, - std::ostream& os, - const ImportPropertyMap& properties, - GenerateType type, - std::string const& config); + + /** Set whether to append generated code to the output file. */ + void SetAppendMode(bool append) { this->AppendMode = append; } protected: + GenerateType GetGenerateType() const override { return BUILD; } + // Implement virtual methods from the superclass. - void GeneratePolicyHeaderCode(std::ostream&) override {} - void GeneratePolicyFooterCode(std::ostream&) override {} + bool GenerateMainFile(std::ostream& os) override; void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = "") override; - void GenerateImportFooterCode(std::ostream& os) override; + std::string const& config = "") override; void GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) override; - void GenerateExpectedTargetsCode( - std::ostream& os, const std::string& expectedTargets) override; - void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation) override; - void GenerateMissingTargetsCheckCode(std::ostream& os) override; - void GenerateFindDependencyCalls(std::ostream&) override {} - void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) override; + + std::string GetCxxModulesDirectory() const override { return {}; } }; diff --git a/Source/cmExportBuildCMakeConfigGenerator.cxx b/Source/cmExportBuildCMakeConfigGenerator.cxx new file mode 100644 index 000000000..87aeb3cbb --- /dev/null +++ b/Source/cmExportBuildCMakeConfigGenerator.cxx @@ -0,0 +1,343 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportBuildCMakeConfigGenerator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmCryptoHash.h" +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmPolicies.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator() +{ + this->LG = nullptr; + this->ExportSet = nullptr; +} + +bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os) +{ + { + std::string expectedTargets; + std::string sep; + bool generatedInterfaceRequired = false; + auto visitor = [&](cmGeneratorTarget const* te) { + expectedTargets += sep + this->Namespace + te->GetExportName(); + sep = " "; + + generatedInterfaceRequired |= + this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY; + }; + + if (!this->CollectExports(visitor)) { + return false; + } + + if (generatedInterfaceRequired) { + this->SetRequiredCMakeVersion(3, 0, 0); + } + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + // Create all the imported targets. + for (auto const& exp : this->Exports) { + cmGeneratorTarget* gte = exp.Target; + this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); + + gte->Target->AppendBuildInterfaceIncludes(); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(gte, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gte, cmGeneratorExpression::BuildInterface, properties); + } + + this->GenerateInterfaceProperties(gte, os, properties); + + this->GenerateTargetFileSets(gte, os); + } + + std::string cxx_modules_name; + if (this->ExportSet) { + cxx_modules_name = this->ExportSet->GetName(); + } else { + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); + constexpr std::size_t HASH_TRUNCATION = 12; + for (auto const& target : this->Targets) { + hasher.Append(target.Name); + } + cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION); + } + + this->GenerateCxxModuleInformation(cxx_modules_name, 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(cxx_modules_name, c); + } + + this->GenerateMissingTargetsCheckCode(os); + + return true; +} + +void cmExportBuildCMakeConfigGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + for (auto const& exp : this->Exports) { + cmGeneratorTarget* target = exp.Target; + + // Collect import properties for this target. + ImportPropertyMap properties; + + if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) { + this->SetImportLocationProperty(config, suffix, target, properties); + } + if (!properties.empty()) { + // Get the rest of the target details. + if (this->GetExportTargetType(target) != + cmStateEnums::INTERFACE_LIBRARY) { + this->SetImportDetailProperties(config, suffix, target, properties); + this->SetImportLinkInterface(config, suffix, + cmGeneratorExpression::BuildInterface, + target, properties); + } + + // TODO: PUBLIC_HEADER_LOCATION + // This should wait until the build feature propagation stuff + // is done. Then this can be a propagated include directory. + // this->GenerateImportProperty(config, te->HeaderGenerator, + // properties); + + // Generate code in the export file. + std::string importedXcFrameworkLocation = exp.XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::BuildInterface); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config, + exp.Target, nullptr, exp.Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) { + importedXcFrameworkLocation = + cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', + importedXcFrameworkLocation); + } + } + this->GenerateImportPropertyCode(os, config, suffix, target, properties, + importedXcFrameworkLocation); + } + } +} + +namespace { +bool EntryIsContextSensitive( + std::unique_ptr const& cge) +{ + return cge->GetHadContextSensitiveCondition(); +} +} + +std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) +{ + std::vector resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + bool const contextSensitive = + 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) { + 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); + + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$:", dest, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', dest, '"')); + break; + } + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) +{ + std::vector resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + auto fileEntries = fileSet->CompileFileEntries(); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + std::map> files; + for (auto const& entry : fileEntries) { + fileSet->EvaluateFileEntry(directories, files, entry, + gte->LocalGenerator, config, gte); + } + + bool const contextSensitive = + std::any_of(directoryEntries.begin(), directoryEntries.end(), + EntryIsContextSensitive) || + 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) { + 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( + filename, cmOutputConverter::WrapQuotes::NoWrap); + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$:", escapedFile, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + } + } + } + + if (!(contextSensitive && configs.size() != 1)) { + break; + } + } + + return cmJoin(resultVector, " "); +} + +void cmExportBuildCMakeConfigGenerator::GenerateCxxModuleConfigInformation( + std::string const& name, std::ostream& os) const +{ + char const* 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-" << name << '-' + << c << ".cmake\"" << opt << ")\n"; + } +} + +bool cmExportBuildCMakeConfigGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, + 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-", name, + '-', 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) { + // Only targets with C++ module sources will have a + // collator-generated install script. + if (!tgt->HaveCxx20ModuleSources()) { + continue; + } + + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" + << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n"; + } + + return true; +} diff --git a/Source/cmExportBuildCMakeConfigGenerator.h b/Source/cmExportBuildCMakeConfigGenerator.h new file mode 100644 index 000000000..0648dc3a8 --- /dev/null +++ b/Source/cmExportBuildCMakeConfigGenerator.h @@ -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 +#include + +#include "cmExportBuildFileGenerator.h" +#include "cmExportCMakeConfigGenerator.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmTargetExport; + +/** \class cmExportBuildCMakeConfigGenerator + * \brief Generate a file exporting targets from a build tree. + * + * cmExportBuildCMakeConfigGenerator generates a file exporting targets from + * a build tree. This exports the targets to CMake's native package + * configuration format. A single file exports information for all + * configurations built. + * + * This is used to implement the export() command. + */ +class cmExportBuildCMakeConfigGenerator + : public cmExportCMakeConfigGenerator + , public cmExportBuildFileGenerator +{ +public: + cmExportBuildCMakeConfigGenerator(); + + /** Set whether to append generated code to the output file. */ + void SetAppendMode(bool append) { this->AppendMode = append; } + +protected: + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + + std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + + void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, + std::string) const; +}; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index d877d767e..a319f0cb6 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -3,20 +3,13 @@ #include "cmExportBuildFileGenerator.h" #include -#include #include #include #include #include #include -#include -#include - -#include "cmCryptoHash.h" #include "cmExportSet.h" -#include "cmFileSet.h" -#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -24,11 +17,8 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" -#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" -#include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" @@ -50,195 +40,6 @@ void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg) } } -bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) -{ - { - std::string expectedTargets; - std::string sep; - std::vector targets; - bool generatedInterfaceRequired = false; - this->GetTargets(targets); - for (auto const& tei : targets) { - cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name); - expectedTargets += sep + this->Namespace + te->GetExportName(); - sep = " "; - if (this->ExportedTargets.insert(te).second) { - this->Exports.emplace_back(te, tei.XcFrameworkLocation); - } else { - std::ostringstream e; - e << "given target \"" << te->GetName() << "\" more than once."; - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - generatedInterfaceRequired |= - this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY; - } - - if (generatedInterfaceRequired) { - this->SetRequiredCMakeVersion(3, 0, 0); - } - this->GenerateExpectedTargetsCode(os, expectedTargets); - } - - // Create all the imported targets. - for (auto const& exp : this->Exports) { - cmGeneratorTarget* gte = exp.Target; - this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); - - gte->Target->AppendBuildInterfaceIncludes(); - - ImportPropertyMap properties; - - this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte, - properties); - - std::string errorMessage; - if (!this->PopulateCxxModuleExportProperties( - gte, properties, cmGeneratorExpression::BuildInterface, {}, - errorMessage)) { - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - - if (!this->PopulateExportProperties(gte, properties, errorMessage)) { - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - - const bool newCMP0022Behavior = - gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && - gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (newCMP0022Behavior) { - this->PopulateInterfaceLinkLibrariesProperty( - gte, cmGeneratorExpression::BuildInterface, properties); - } - this->PopulateCompatibleInterfaceProperties(gte, properties); - this->PopulateCustomTransitiveInterfaceProperties( - gte, cmGeneratorExpression::BuildInterface, properties); - - this->GenerateInterfaceProperties(gte, os, properties); - - this->GenerateTargetFileSets(gte, os); - } - - std::string cxx_modules_name; - if (this->ExportSet) { - cxx_modules_name = this->ExportSet->GetName(); - } else { - cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); - constexpr std::size_t HASH_TRUNCATION = 12; - for (auto const& target : this->Targets) { - hasher.Append(target.Name); - } - cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION); - } - - this->GenerateCxxModuleInformation(cxx_modules_name, 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(cxx_modules_name, c); - } - - this->GenerateMissingTargetsCheckCode(os); - - return true; -} - -void cmExportBuildFileGenerator::GenerateImportTargetsConfig( - std::ostream& os, const std::string& config, std::string const& suffix) -{ - for (auto const& exp : this->Exports) { - cmGeneratorTarget* target = exp.Target; - - // Collect import properties for this target. - ImportPropertyMap properties; - - if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) { - this->SetImportLocationProperty(config, suffix, target, properties); - } - if (!properties.empty()) { - // Get the rest of the target details. - if (this->GetExportTargetType(target) != - cmStateEnums::INTERFACE_LIBRARY) { - this->SetImportDetailProperties(config, suffix, target, properties); - this->SetImportLinkInterface(config, suffix, - cmGeneratorExpression::BuildInterface, - target, properties); - } - - // TODO: PUBLIC_HEADER_LOCATION - // This should wait until the build feature propagation stuff - // is done. Then this can be a propagated include directory. - // this->GenerateImportProperty(config, te->HeaderGenerator, - // properties); - - // Generate code in the export file. - std::string importedXcFrameworkLocation = exp.XcFrameworkLocation; - if (!importedXcFrameworkLocation.empty()) { - importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( - importedXcFrameworkLocation, - cmGeneratorExpression::PreprocessContext::BuildInterface); - importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( - importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config, - exp.Target, nullptr, exp.Target); - if (!importedXcFrameworkLocation.empty() && - !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) { - importedXcFrameworkLocation = - cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', - importedXcFrameworkLocation); - } - } - this->GenerateImportPropertyCode(os, config, suffix, target, properties, - importedXcFrameworkLocation); - } - } -} - cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType( cmGeneratorTarget const* target) const { @@ -260,7 +61,7 @@ void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet) } void cmExportBuildFileGenerator::SetImportLocationProperty( - const std::string& config, std::string const& suffix, + std::string const& config, std::string const& suffix, cmGeneratorTarget* target, ImportPropertyMap& properties) { // Get the makefile in which to lookup target information. @@ -276,7 +77,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( std::string const obj_dir = target->GetObjectDirectory(config); std::vector objects; for (cmSourceFile const* sf : objectSources) { - const std::string& obj = target->GetObjectName(sf); + std::string const& obj = target->GetObjectName(sf); objects.push_back(obj_dir + obj); } @@ -311,17 +112,34 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( } } +bool cmExportBuildFileGenerator::CollectExports( + std::function visitor) +{ + auto pred = [&](cmExportBuildFileGenerator::TargetExport& tei) -> bool { + cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name); + if (this->ExportedTargets.insert(te).second) { + this->Exports.emplace_back(te, tei.XcFrameworkLocation); + visitor(te); + return true; + } + + this->ComplainAboutDuplicateTarget(te->GetName()); + return false; + }; + + std::vector targets; + this->GetTargets(targets); + return std::all_of(targets.begin(), targets.end(), pred); +} + void cmExportBuildFileGenerator::HandleMissingTarget( std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) { // The target is not in the export. if (!this->AppendMode) { - const std::string name = dependee->GetName(); - cmGlobalGenerator* gg = - dependee->GetLocalGenerator()->GetGlobalGenerator(); - auto exportInfo = this->FindBuildExportInfo(gg, name); - std::vector const& exportFiles = exportInfo.first; + auto const& exportInfo = this->FindExportInfo(dependee); + auto const& exportFiles = exportInfo.first; if (exportFiles.size() == 1) { std::string missingTarget = exportInfo.second; @@ -357,33 +175,34 @@ void cmExportBuildFileGenerator::GetTargets( targets = this->Targets; } -std::pair, std::string> -cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg, - const std::string& name) +cmExportFileGenerator::ExportInfo cmExportBuildFileGenerator::FindExportInfo( + cmGeneratorTarget const* target) const { std::vector exportFiles; std::string ns; - auto& exportSets = gg->GetBuildExportSets(); + auto const& name = target->GetName(); + auto& exportSets = + target->GetLocalGenerator()->GetGlobalGenerator()->GetBuildExportSets(); for (auto const& exp : exportSets) { - const auto& exportSet = exp.second; + auto const& exportSet = exp.second; std::vector targets; exportSet->GetTargets(targets); if (std::any_of( targets.begin(), targets.end(), - [&name](const TargetExport& te) { return te.Name == name; })) { + [&name](TargetExport const& te) { return te.Name == name; })) { exportFiles.push_back(exp.first); ns = exportSet->GetNamespace(); } } - return { exportFiles, ns }; + return { exportFiles, exportFiles.size() == 1 ? ns : std::string{} }; } void cmExportBuildFileGenerator::ComplainAboutMissingTarget( cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, - std::vector const& exportFiles) + std::vector const& exportFiles) const { std::ostringstream e; e << "export called with target \"" << depender->GetName() @@ -399,13 +218,27 @@ void cmExportBuildFileGenerator::ComplainAboutMissingTarget( << dependee->GetName() << "\" target to a single export."; } + this->ReportError(e.str()); +} + +void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "given target \"" << targetName << "\" more than once."; + this->ReportError(e.str()); +} + +void cmExportBuildFileGenerator::ReportError( + std::string const& errorMessage) const +{ this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), + MessageType::FATAL_ERROR, errorMessage, this->LG->GetMakefile()->GetBacktrace()); } std::string cmExportBuildFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; @@ -417,185 +250,22 @@ std::string cmExportBuildFileGenerator::InstallNameDir( return install_name_dir; } -namespace { -bool EntryIsContextSensitive( - const std::unique_ptr& cge) -{ - return cge->GetHadContextSensitiveCondition(); -} -} - -std::string cmExportBuildFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/) -{ - std::vector resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); - - bool const contextSensitive = - 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) { - 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); - - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$:", dest, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', dest, '"')); - break; - } - } - } - - return cmJoin(resultVector, " "); -} - -std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* /*te*/) -{ - std::vector resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); - - std::map> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, - gte->LocalGenerator, config, gte); - } - - bool const contextSensitive = - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - 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) { - 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( - filename, cmOutputConverter::WrapQuotes::NoWrap); - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$:", escapedFile, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); - } - } - } - - if (!(contextSensitive && configs.size() != 1)) { - break; - } - } - - return cmJoin(resultVector, " "); -} - -std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const -{ - return this->CxxModulesDirectory; -} - -void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( - std::string const& name, 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-" << name << '-' - << c << ".cmake\"" << opt << ")\n"; - } -} - -bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( - std::string const& name, std::string config) const +bool cmExportBuildFileGenerator::PopulateInterfaceProperties( + cmGeneratorTarget const* target, ImportPropertyMap& properties) { - 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-", name, - '-', 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) { - // Only targets with C++ module sources will have a - // collator-generated install script. - if (!tgt->HaveCxx20ModuleSources()) { - continue; - } - - os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" - << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n"; - } - - return true; + this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_SOURCES", target, + cmGeneratorExpression::BuildInterface, + properties); + + return this->PopulateInterfaceProperties( + target, {}, cmGeneratorExpression::BuildInterface, properties); } diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index ee4779f82..dfd041619 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -4,7 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include +#include #include #include #include @@ -15,22 +15,18 @@ #include "cmStateTypes.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; -class cmGlobalGenerator; class cmLocalGenerator; -class cmTargetExport; -/** \class cmExportBuildFileGenerator +/** \class cmExportBuildCMakeConfigGenerator * \brief Generate a file exporting targets from a build tree. * - * cmExportBuildFileGenerator generates a file exporting targets from - * a build tree. A single file exports information for all - * configurations built. + * cmExportBuildCMakeConfigGenerator is the interface class for generating a + * file exporting targets from a build tree. * * This is used to implement the export() command. */ -class cmExportBuildFileGenerator : public cmExportFileGenerator +class cmExportBuildFileGenerator : virtual public cmExportFileGenerator { public: struct TargetExport @@ -64,54 +60,55 @@ public: { this->CxxModulesDirectory = std::move(cxx_module_dir); } - const std::string& GetCxxModuleDirectory() const + std::string const& GetCxxModuleDirectory() const { return this->CxxModulesDirectory; } - /** Set whether to append generated code to the output file. */ - void SetAppendMode(bool append) { this->AppendMode = append; } - void Compute(cmLocalGenerator* lg); protected: - // Implement virtual methods from the superclass. - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream& os, const std::string& config, - std::string const& suffix) override; cmStateEnums::TargetType GetExportTargetType( cmGeneratorTarget const* target) const; + + /** Walk the list of targets to be exported. Returns true iff no duplicates + are found. */ + bool CollectExports(std::function visitor); + void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) override; - void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - std::vector const& namespaces); + void ComplainAboutMissingTarget( + cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, + std::vector const& exportFiles) const; + + void ComplainAboutDuplicateTarget( + std::string const& targetName) const override; + + void ReportError(std::string const& errorMessage) const override; /** Fill in properties indicating built file locations. */ - void SetImportLocationProperty(const std::string& config, + void SetImportLocationProperty(std::string const& config, std::string const& suffix, cmGeneratorTarget* target, ImportPropertyMap& properties); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; cmExportSet* GetExportSet() const override { return this->ExportSet; } - std::string GetCxxModulesDirectory() const override; - void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream&) const override; - bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, - std::string) const; + std::string GetCxxModulesDirectory() const override + { + return this->CxxModulesDirectory; + } + + ExportInfo FindExportInfo(cmGeneratorTarget const* target) const override; - std::pair, std::string> FindBuildExportInfo( - cmGlobalGenerator* gg, const std::string& name); + using cmExportFileGenerator::PopulateInterfaceProperties; + bool PopulateInterfaceProperties(cmGeneratorTarget const* target, + ImportPropertyMap& properties); struct TargetExportPrivate { diff --git a/Source/cmExportCMakeConfigGenerator.cxx b/Source/cmExportCMakeConfigGenerator.cxx new file mode 100644 index 000000000..4f4765c96 --- /dev/null +++ b/Source/cmExportCMakeConfigGenerator.cxx @@ -0,0 +1,686 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportCMakeConfigGenerator.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmFindPackageStack.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorTarget.h" +#include "cmLinkItem.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmPolicies.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" +#include "cmVersion.h" + +static std::string cmExportFileGeneratorEscape(std::string const& str) +{ + // Escape a property value for writing into a .cmake file. + std::string result = cmOutputConverter::EscapeForCMake(str); + // Un-escape variable references generated by our own export code. + cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}", + "${_IMPORT_PREFIX}"); + cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}", + "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); + return result; +} + +cmExportCMakeConfigGenerator::cmExportCMakeConfigGenerator() = default; + +cm::string_view cmExportCMakeConfigGenerator::GetImportPrefixWithSlash() const +{ + return "${_IMPORT_PREFIX}/"_s; +} + +bool cmExportCMakeConfigGenerator::GenerateImportFile(std::ostream& os) +{ + std::stringstream mainFileWithHeadersAndFootersBuffer; + + // Start with the import file header. + this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer); + + // Create all the imported targets. + std::stringstream mainFileBuffer; + bool result = this->GenerateMainFile(mainFileBuffer); + + // Export find_dependency() calls. Must be done after GenerateMainFile(), + // because that's when target dependencies are gathered, which we need for + // the find_dependency() calls. + if (!this->AppendMode && this->GetExportSet() && + this->ExportPackageDependencies) { + this->SetRequiredCMakeVersion(3, 9, 0); + this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer); + } + + // Write cached import code. + mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf(); + + // End with the import file footer. + this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer); + this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer); + + // This has to be done last, after the minimum CMake version has been + // determined. + this->GeneratePolicyHeaderCode(os); + os << mainFileWithHeadersAndFootersBuffer.rdbuf(); + + return result; +} + +void cmExportCMakeConfigGenerator::GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + ImportPropertyMap const& properties) +{ + if (!properties.empty()) { + std::string targetName = + cmStrCat(this->Namespace, target->GetExportName()); + os << "set_target_properties(" << targetName << " PROPERTIES\n"; + for (auto const& property : properties) { + os << " " << property.first << " " + << cmExportFileGeneratorEscape(property.second) << "\n"; + } + os << ")\n\n"; + } +} + +void cmExportCMakeConfigGenerator::SetImportLinkInterface( + std::string const& config, std::string const& suffix, + cmGeneratorExpression::PreprocessContext preprocessRule, + cmGeneratorTarget const* target, ImportPropertyMap& properties) +{ + // Add the transitive link dependencies for this configuration. + cmLinkInterface const* iface = target->GetLinkInterface(config, target); + if (!iface) { + return; + } + + if (iface->ImplementationIsInterface) { + // Policy CMP0022 must not be NEW. + this->SetImportLinkProperty( + suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries, + properties, ImportLinkPropertyTargetNames::Yes); + return; + } + + cmValue propContent; + + if (cmValue prop_suffixed = + target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) { + propContent = prop_suffixed; + } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) { + propContent = prop; + } else { + return; + } + + bool const newCMP0022Behavior = + target->GetPolicyStatusCMP0022() != cmPolicies::WARN && + target->GetPolicyStatusCMP0022() != cmPolicies::OLD; + + if (newCMP0022Behavior && !this->ExportOld) { + cmLocalGenerator* lg = target->GetLocalGenerator(); + std::ostringstream e; + e << "Target \"" << target->GetName() + << "\" has policy CMP0022 enabled, " + "but also has old-style LINK_INTERFACE_LIBRARIES properties " + "populated, but it was exported without the " + "EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties"; + lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return; + } + + if (propContent->empty()) { + properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear(); + return; + } + + std::string prepro = + cmGeneratorExpression::Preprocess(*propContent, preprocessRule); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target, + ReplaceFreeTargets); + properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro; + } +} + +void cmExportCMakeConfigGenerator::GeneratePolicyHeaderCode(std::ostream& os) +{ + // Protect that file against use with older CMake versions. + /* clang-format off */ + os << "# Generated by CMake\n\n"; + os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n" + << " message(FATAL_ERROR \"CMake >= " + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << " required\")\n" + << "endif()\n" + << "if(CMAKE_VERSION VERSION_LESS \"" + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << "\")\n" + << " message(FATAL_ERROR \"CMake >= " + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << " required\")\n" + << "endif()\n"; + /* clang-format on */ + + // Isolate the file policy level. + // Support CMake versions as far back as the + // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW + // policy settings for up to CMake 3.29 (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 " + << this->RequiredCMakeVersionMajor << '.' + << this->RequiredCMakeVersionMinor << '.' + << this->RequiredCMakeVersionPatch << "...3.29)\n"; + /* clang-format on */ +} + +void cmExportCMakeConfigGenerator::GeneratePolicyFooterCode(std::ostream& os) +{ + os << "cmake_policy(POP)\n"; +} + +void cmExportCMakeConfigGenerator::GenerateImportHeaderCode( + std::ostream& os, std::string const& config) +{ + os << "#----------------------------------------------------------------\n" + << "# Generated CMake target import file"; + if (!config.empty()) { + os << " for configuration \"" << config << "\".\n"; + } else { + os << ".\n"; + } + os << "#----------------------------------------------------------------\n" + << "\n"; + this->GenerateImportVersionCode(os); +} + +void cmExportCMakeConfigGenerator::GenerateImportFooterCode(std::ostream& os) +{ + os << "# Commands beyond this point should not need to know the version.\n" + << "set(CMAKE_IMPORT_FILE_VERSION)\n"; +} + +void cmExportCMakeConfigGenerator::GenerateImportVersionCode(std::ostream& os) +{ + // Store an import file format version. This will let us change the + // format later while still allowing old import files to work. + /* clang-format off */ + os << "# Commands may need to know the format version.\n" + << "set(CMAKE_IMPORT_FILE_VERSION 1)\n" + << "\n"; + /* clang-format on */ +} + +void cmExportCMakeConfigGenerator::GenerateExpectedTargetsCode( + std::ostream& os, std::string const& expectedTargets) +{ + /* clang-format off */ + os << "# Protect against multiple inclusion, which would fail when already " + "imported targets are added once more.\n" + "set(_cmake_targets_defined \"\")\n" + "set(_cmake_targets_not_defined \"\")\n" + "set(_cmake_expected_targets \"\")\n" + "foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n" + " list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n" + " if(TARGET \"${_cmake_expected_target}\")\n" + " list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n" + " else()\n" + " list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n" + " endif()\n" + "endforeach()\n" + "unset(_cmake_expected_target)\n" + "if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n" + " unset(_cmake_targets_defined)\n" + " unset(_cmake_targets_not_defined)\n" + " unset(_cmake_expected_targets)\n" + " unset(CMAKE_IMPORT_FILE_VERSION)\n" + " cmake_policy(POP)\n" + " return()\n" + "endif()\n" + "if(NOT _cmake_targets_defined STREQUAL \"\")\n" + " string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n" + " string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n" + " message(FATAL_ERROR \"Some (but not all) targets in this export " + "set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n" + "Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n" + "endif()\n" + "unset(_cmake_targets_defined)\n" + "unset(_cmake_targets_not_defined)\n" + "unset(_cmake_expected_targets)\n" + "\n\n"; + /* clang-format on */ +} + +void cmExportCMakeConfigGenerator::GenerateImportTargetCode( + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) +{ + // Construct the imported target name. + std::string targetName = this->Namespace; + + targetName += target->GetExportName(); + + // Create the imported target. + os << "# Create imported target " << targetName << "\n"; + switch (targetType) { + case cmStateEnums::EXECUTABLE: + os << "add_executable(" << targetName << " IMPORTED)\n"; + break; + case cmStateEnums::STATIC_LIBRARY: + os << "add_library(" << targetName << " STATIC IMPORTED)\n"; + break; + case cmStateEnums::SHARED_LIBRARY: + os << "add_library(" << targetName << " SHARED IMPORTED)\n"; + break; + case cmStateEnums::MODULE_LIBRARY: + os << "add_library(" << targetName << " MODULE IMPORTED)\n"; + break; + case cmStateEnums::UNKNOWN_LIBRARY: + os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n"; + break; + case cmStateEnums::OBJECT_LIBRARY: + os << "add_library(" << targetName << " OBJECT IMPORTED)\n"; + break; + case cmStateEnums::INTERFACE_LIBRARY: + os << "add_library(" << targetName << " INTERFACE IMPORTED)\n"; + break; + default: // should never happen + break; + } + + // Mark the imported executable if it has exports. + if (target->IsExecutableWithExports() || + (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) { + os << "set_property(TARGET " << targetName + << " PROPERTY ENABLE_EXPORTS 1)\n"; + } + + // Mark the imported library if it is a framework. + if (target->IsFrameworkOnApple()) { + os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n"; + } + + // Mark the imported executable if it is an application bundle. + if (target->IsAppBundleOnApple()) { + os << "set_property(TARGET " << targetName + << " PROPERTY MACOSX_BUNDLE 1)\n"; + } + + if (target->IsCFBundleOnApple()) { + os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n"; + } + + // generate DEPRECATION + if (target->IsDeprecated()) { + os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION " + << cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n"; + } + + if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { + os << "set_property(TARGET " << targetName + << " PROPERTY IMPORTED_NO_SYSTEM 1)\n"; + } + + if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) { + os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n"; + } + + os << "\n"; +} + +void cmExportCMakeConfigGenerator::GenerateImportPropertyCode( + std::ostream& os, std::string const& config, std::string const& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + std::string const& importedXcFrameworkLocation) +{ + // Construct the imported target name. + std::string targetName = this->Namespace; + + targetName += target->GetExportName(); + + // Set the import properties. + os << "# Import target \"" << targetName << "\" for configuration \"" + << config << "\"\n"; + os << "set_property(TARGET " << targetName + << " APPEND PROPERTY IMPORTED_CONFIGURATIONS "; + if (!config.empty()) { + os << cmSystemTools::UpperCase(config); + } else { + os << "NOCONFIG"; + } + os << ")\n"; + os << "set_target_properties(" << targetName << " PROPERTIES\n"; + std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix); + for (auto const& property : properties) { + if (importedXcFrameworkLocation.empty() || + property.first != importedLocationProp) { + os << " " << property.first << " " + << cmExportFileGeneratorEscape(property.second) << "\n"; + } + } + os << " )\n"; + if (!importedXcFrameworkLocation.empty()) { + auto importedLocationIt = properties.find(importedLocationProp); + if (importedLocationIt != properties.end()) { + os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY " + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) + << ")\n" + " set_property(TARGET " + << targetName << " PROPERTY " << importedLocationProp << " " + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) + << ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY " + << importedLocationProp << " " + << cmExportFileGeneratorEscape(importedLocationIt->second) + << ")\nendif()\n"; + } + } + os << "\n"; +} + +void cmExportCMakeConfigGenerator::GenerateFindDependencyCalls( + std::ostream& os) +{ + os << "include(CMakeFindDependencyMacro)\n"; + std::map packageDependencies; + auto* exportSet = this->GetExportSet(); + if (exportSet) { + packageDependencies = exportSet->GetPackageDependencies(); + } + + for (cmGeneratorTarget const* gt : this->ExternalTargets) { + std::string findPackageName; + auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME"); + cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack(); + if (!exportFindPackageName.IsEmpty()) { + findPackageName = *exportFindPackageName; + } else { + if (!pkgStack.Empty()) { + cmFindPackageCall const& fpc = pkgStack.Top(); + findPackageName = fpc.Name; + } + } + if (!findPackageName.empty()) { + auto& dep = packageDependencies[findPackageName]; + if (!pkgStack.Empty()) { + dep.FindPackageIndex = pkgStack.Top().Index; + } + if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) { + dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On; + } + } + } + + std::vector> + packageDependenciesSorted(packageDependencies.begin(), + packageDependencies.end()); + std::sort( + packageDependenciesSorted.begin(), packageDependenciesSorted.end(), + [](std::pair const& lhs, + std::pair const& rhs) + -> bool { + if (lhs.second.SpecifiedIndex) { + if (rhs.second.SpecifiedIndex) { + return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex; + } + assert(rhs.second.FindPackageIndex); + return true; + } + assert(lhs.second.FindPackageIndex); + if (rhs.second.SpecifiedIndex) { + return false; + } + assert(rhs.second.FindPackageIndex); + return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex; + }); + + for (auto const& it : packageDependenciesSorted) { + if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) { + os << "find_dependency(" << it.first; + for (auto const& arg : it.second.ExtraArguments) { + os << " " << cmOutputConverter::EscapeForCMake(arg); + } + os << ")\n"; + } + } + os << "\n\n"; +} + +void cmExportCMakeConfigGenerator::GenerateMissingTargetsCheckCode( + std::ostream& os) +{ + if (this->MissingTargets.empty()) { + /* clang-format off */ + os << "# This file does not depend on other imported targets which have\n" + "# been exported from the same project but in a separate " + "export set.\n\n"; + /* clang-format on */ + return; + } + /* clang-format off */ + os << "# Make sure the targets which have been exported in some other\n" + "# export set exist.\n" + "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" + "foreach(_target "; + /* clang-format on */ + std::set emitted; + for (std::string const& missingTarget : this->MissingTargets) { + if (emitted.insert(missingTarget).second) { + os << "\"" << missingTarget << "\" "; + } + } + /* clang-format off */ + os << ")\n" + " if(NOT TARGET \"${_target}\" )\n" + " set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \"" + "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")" + "\n" + " endif()\n" + "endforeach()\n" + "\n" + "if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" + " if(CMAKE_FIND_PACKAGE_NAME)\n" + " set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n" + " set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE " + "\"The following imported targets are " + "referenced, but are missing: " + "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n" + " else()\n" + " message(FATAL_ERROR \"The following imported targets are " + "referenced, but are missing: " + "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n" + " endif()\n" + "endif()\n" + "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" + "\n"; + /* clang-format on */ +} + +void cmExportCMakeConfigGenerator::GenerateImportedFileCheckLoop( + std::ostream& os) +{ + // Add code which verifies at cmake time that the file which is being + // imported actually exists on disk. This should in theory always be theory + // case, but still when packages are split into normal and development + // packages this might get broken (e.g. the Config.cmake could be part of + // the non-development package, something similar happened to me without + // on SUSE with a mysql pkg-config file, which claimed everything is fine, + // but the development package was not installed.). + /* clang-format off */ + os << "# Loop over all imported files and verify that they actually exist\n" + "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n" + " if(CMAKE_VERSION VERSION_LESS \"3.28\"\n" + " OR NOT DEFINED " + "_cmake_import_check_xcframework_for_${_cmake_target}\n" + " OR NOT IS_DIRECTORY " + "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n" + " foreach(_cmake_file IN LISTS " + "\"_cmake_import_check_files_for_${_cmake_target}\")\n" + " if(NOT EXISTS \"${_cmake_file}\")\n" + " message(FATAL_ERROR \"The imported target " + "\\\"${_cmake_target}\\\" references the file\n" + " \\\"${_cmake_file}\\\"\n" + "but this file does not exist. Possible reasons include:\n" + "* The file was deleted, renamed, or moved to another location.\n" + "* An install or uninstall procedure did not complete successfully.\n" + "* The installation package was faulty and contained\n" + " \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n" + "but not all the files it references.\n" + "\")\n" + " endif()\n" + " endforeach()\n" + " endif()\n" + " unset(_cmake_file)\n" + " unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n" + "endforeach()\n" + "unset(_cmake_target)\n" + "unset(_cmake_import_check_targets)\n" + "\n"; + /* clang-format on */ +} + +void cmExportCMakeConfigGenerator::GenerateImportedFileChecksCode( + std::ostream& os, cmGeneratorTarget const* target, + ImportPropertyMap const& properties, + std::set const& importedLocations, + std::string const& importedXcFrameworkLocation) +{ + // Construct the imported target name. + std::string targetName = cmStrCat(this->Namespace, target->GetExportName()); + + os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n"; + if (!importedXcFrameworkLocation.empty()) { + os << "set(_cmake_import_check_xcframework_for_" << targetName << ' ' + << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n"; + } + os << "list(APPEND _cmake_import_check_files_for_" << targetName << " "; + + for (std::string const& li : importedLocations) { + auto pi = properties.find(li); + if (pi != properties.end()) { + os << cmExportFileGeneratorEscape(pi->second) << " "; + } + } + + os << ")\n\n"; +} + +void cmExportCMakeConfigGenerator::GenerateTargetFileSets( + cmGeneratorTarget* gte, std::ostream& os, cmTargetExport const* te) +{ + auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets(); + if (!interfaceFileSets.empty()) { + std::string targetName = cmStrCat(this->Namespace, gte->GetExportName()); + os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n" + " target_sources(" + << targetName << "\n"; + + for (auto const& name : interfaceFileSets) { + auto* fileSet = gte->Target->GetFileSet(name); + if (!fileSet) { + gte->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("File set \"", name, + "\" is listed in interface file sets of ", gte->GetName(), + " but has not been created")); + return; + } + + os << " INTERFACE" + << "\n FILE_SET " << cmOutputConverter::EscapeForCMake(name) + << "\n TYPE " + << cmOutputConverter::EscapeForCMake(fileSet->GetType()) + << "\n BASE_DIRS " + << this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES " + << this->GetFileSetFiles(gte, fileSet, te) << "\n"; + } + + os << " )\nelse()\n set_property(TARGET " << targetName + << "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES"; + for (auto const& name : interfaceFileSets) { + auto* fileSet = gte->Target->GetFileSet(name); + if (!fileSet) { + gte->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("File set \"", name, + "\" is listed in interface file sets of ", gte->GetName(), + " but has not been created")); + return; + } + + if (fileSet->GetType() == "HEADERS"_s) { + os << "\n " << this->GetFileSetDirectories(gte, fileSet, te); + } + } + os << "\n )\nendif()\n\n"; + } +} + +std::string cmExportCMakeConfigGenerator::GetCxxModuleFile( + std::string const& name) const +{ + auto const& cxxModuleDirname = this->GetCxxModulesDirectory(); + if (cxxModuleDirname.empty()) { + return {}; + } + + return cmStrCat(cmSystemTools::GetFilenamePath(this->MainImportFile), '/', + cxxModuleDirname, "/cxx-modules-", name, ".cmake"); +} + +void cmExportCMakeConfigGenerator::GenerateCxxModuleInformation( + std::string const& name, 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-" << name << ".cmake\")\n\n"; + + // Include all configuration-specific include files. + cmGeneratedFileStream ap(this->GetCxxModuleFile(name), true); + ap.SetCopyIfDifferent(true); + + this->GenerateCxxModuleConfigInformation(name, ap); +} + +void cmExportCMakeConfigGenerator::SetRequiredCMakeVersion(unsigned int major, + unsigned int minor, + unsigned int patch) +{ + if (CMake_VERSION_ENCODE(major, minor, patch) > + CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor, + this->RequiredCMakeVersionMinor, + this->RequiredCMakeVersionPatch)) { + this->RequiredCMakeVersionMajor = major; + this->RequiredCMakeVersionMinor = minor; + this->RequiredCMakeVersionPatch = patch; + } +} diff --git a/Source/cmExportCMakeConfigGenerator.h b/Source/cmExportCMakeConfigGenerator.h new file mode 100644 index 000000000..90f7aa74e --- /dev/null +++ b/Source/cmExportCMakeConfigGenerator.h @@ -0,0 +1,109 @@ +/* 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 +#include +#include +#include + +#include + +#include "cmExportFileGenerator.h" +#include "cmGeneratorExpression.h" +#include "cmStateTypes.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmTargetExport; + +/** \class cmExportCMakeConfigGenerator + * \brief Generate CMake configuration files exporting targets from a build or + * install tree. + * + * cmExportCMakeConfigGenerator is the superclass for + * cmExportBuildCMakeConfigGenerator and cmExportInstallCMakeConfigGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +class cmExportCMakeConfigGenerator : virtual public cmExportFileGenerator +{ +public: + cmExportCMakeConfigGenerator(); + + void SetExportOld(bool exportOld) { this->ExportOld = exportOld; } + + void SetExportPackageDependencies(bool exportPackageDependencies) + { + this->ExportPackageDependencies = exportPackageDependencies; + } + + using cmExportFileGenerator::GenerateImportFile; + +protected: + using ImportPropertyMap = std::map; + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + virtual void GeneratePolicyHeaderCode(std::ostream& os); + virtual void GeneratePolicyFooterCode(std::ostream& os); + virtual void GenerateImportHeaderCode(std::ostream& os, + std::string const& config = ""); + virtual void GenerateImportFooterCode(std::ostream& os); + void GenerateImportVersionCode(std::ostream& os); + virtual void GenerateImportTargetCode(std::ostream& os, + cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType); + virtual void GenerateImportPropertyCode( + std::ostream& os, std::string const& config, std::string const& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + std::string const& importedXcFrameworkLocation); + virtual void GenerateImportedFileChecksCode( + std::ostream& os, cmGeneratorTarget const* target, + ImportPropertyMap const& properties, + std::set const& importedLocations, + std::string const& importedXcFrameworkLocation); + virtual void GenerateImportedFileCheckLoop(std::ostream& os); + virtual void GenerateMissingTargetsCheckCode(std::ostream& os); + virtual void GenerateFindDependencyCalls(std::ostream& os); + + virtual void GenerateExpectedTargetsCode(std::ostream& os, + std::string const& expectedTargets); + + cm::string_view GetImportPrefixWithSlash() const override; + + virtual void GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + ImportPropertyMap const& properties); + + void SetImportLinkInterface( + std::string const& config, std::string const& suffix, + cmGeneratorExpression::PreprocessContext preprocessRule, + cmGeneratorTarget const* target, ImportPropertyMap& properties); + + void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, + cmTargetExport const* te = nullptr); + + std::string GetCxxModuleFile(std::string const& name) const override; + + void GenerateCxxModuleInformation(std::string const& name, std::ostream& os); + + virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, + cmFileSet* fileSet, + cmTargetExport const* te) = 0; + virtual std::string GetFileSetFiles(cmGeneratorTarget* gte, + cmFileSet* fileSet, + cmTargetExport const* te) = 0; + + void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, + unsigned int patch); + + bool ExportOld = false; + bool ExportPackageDependencies = false; + + unsigned int RequiredCMakeVersionMajor = 2; + unsigned int RequiredCMakeVersionMinor = 8; + unsigned int RequiredCMakeVersionPatch = 3; +}; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 0cb001107..4c6ddfcc9 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -19,6 +19,7 @@ #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" +#include "cmExportBuildCMakeConfigGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" #include "cmGeneratedFileStream.h" @@ -296,7 +297,7 @@ bool cmExportCommand(std::vector const& args, // if cmExportBuildFileGenerator is already defined for the file // and APPEND is not specified, if CMP0103 is OLD ignore previous definition // else raise an error - if (gg->GetExportedTargetsFile(fname) != nullptr) { + if (gg->GetExportedTargetsFile(fname)) { switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) { case cmPolicies::WARN: mf.IssueMessage( @@ -318,21 +319,24 @@ bool cmExportCommand(std::vector const& args, // Setup export file generation. std::unique_ptr ebfg = nullptr; if (android) { - ebfg = cm::make_unique(); + auto ebag = cm::make_unique(); + ebag->SetAppendMode(arguments.Append); + ebfg = std::move(ebag); } else { - ebfg = cm::make_unique(); + auto ebcg = cm::make_unique(); + ebcg->SetAppendMode(arguments.Append); + ebcg->SetExportOld(arguments.ExportOld); + ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies); + ebfg = std::move(ebcg); } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(arguments.Namespace); ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory); - ebfg->SetAppendMode(arguments.Append); - if (exportSet != nullptr) { + if (exportSet) { ebfg->SetExportSet(exportSet); } else { ebfg->SetTargets(targets); } - ebfg->SetExportOld(arguments.ExportOld); - ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies); // Compute the set of configurations exported. std::vector configurationTypes = @@ -341,7 +345,7 @@ bool cmExportCommand(std::vector const& args, for (std::string const& ct : configurationTypes) { ebfg->AddConfiguration(ct); } - if (exportSet != nullptr) { + if (exportSet) { gg->AddBuildExportExportSet(ebfg.get()); } else { gg->AddBuildExportSet(ebfg.get()); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 28c91dada..5e9461d65 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -2,23 +2,18 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportFileGenerator.h" -#include #include -#include -#include +#include #include #include #include -#include #include #include #include "cmsys/FStream.hxx" #include "cmComputeLinkInformation.h" -#include "cmExportSet.h" -#include "cmFileSet.h" #include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" @@ -27,40 +22,21 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" -#include "cmPolicies.h" #include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.h" -#include "cmVersion.h" -static std::string cmExportFileGeneratorEscape(std::string const& str) -{ - // Escape a property value for writing into a .cmake file. - std::string result = cmOutputConverter::EscapeForCMake(str); - // Un-escape variable references generated by our own export code. - cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}", - "${_IMPORT_PREFIX}"); - cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}", - "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); - return result; -} - -cmExportFileGenerator::cmExportFileGenerator() -{ - this->AppendMode = false; - this->ExportOld = false; -} +cmExportFileGenerator::cmExportFileGenerator() = default; -void cmExportFileGenerator::AddConfiguration(const std::string& config) +void cmExportFileGenerator::AddConfiguration(std::string const& config) { this->Configurations.push_back(config); } -void cmExportFileGenerator::SetExportFile(const char* mainFile) +void cmExportFileGenerator::SetExportFile(char const* mainFile) { this->MainImportFile = mainFile; this->FileDir = cmSystemTools::GetFilenamePath(this->MainImportFile); @@ -70,7 +46,7 @@ void cmExportFileGenerator::SetExportFile(const char* mainFile) cmSystemTools::GetFilenameLastExtension(this->MainImportFile); } -const std::string& cmExportFileGenerator::GetMainExportFileName() const +std::string const& cmExportFileGenerator::GetMainExportFileName() const { return this->MainImportFile; } @@ -98,42 +74,12 @@ bool cmExportFileGenerator::GenerateImportFile() cmSystemTools::Error(e.str()); return false; } - std::ostream& os = *foutPtr; - std::stringstream mainFileWithHeadersAndFootersBuffer; - - // Start with the import file header. - this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer); - - // Create all the imported targets. - std::stringstream mainFileBuffer; - bool result = this->GenerateMainFile(mainFileBuffer); - - // Export find_dependency() calls. Must be done after GenerateMainFile(), - // because that's when target dependencies are gathered, which we need for - // the find_dependency() calls. - if (!this->AppendMode && this->GetExportSet() && - this->ExportPackageDependencies) { - this->SetRequiredCMakeVersion(3, 9, 0); - this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer); - } - // Write cached import code. - mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf(); - - // End with the import file footer. - this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer); - this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer); - - // This has to be done last, after the minimum CMake version has been - // determined. - this->GeneratePolicyHeaderCode(os); - os << mainFileWithHeadersAndFootersBuffer.rdbuf(); - - return result; + return this->GenerateImportFile(*foutPtr); } void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, - const std::string& config) + std::string const& config) { // Construct the property configuration suffix. std::string suffix = "_"; @@ -147,9 +93,50 @@ void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, this->GenerateImportTargetsConfig(os, config, suffix); } -void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, cmGeneratorTarget const* target, +bool cmExportFileGenerator::PopulateInterfaceProperties( + cmGeneratorTarget const* target, std::string const& includesDestinationDirs, + cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties) +{ + this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", + target, properties); + + std::string errorMessage; + if (!this->PopulateCxxModuleExportProperties( + target, properties, preprocessRule, includesDestinationDirs, + errorMessage)) { + this->ReportError(errorMessage); + return false; + } + + if (!this->PopulateExportProperties(target, properties, errorMessage)) { + this->ReportError(errorMessage); + return false; + } + this->PopulateCompatibleInterfaceProperties(target, properties); + this->PopulateCustomTransitiveInterfaceProperties(target, preprocessRule, + properties); + + return true; +} + +void cmExportFileGenerator::PopulateInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + ImportPropertyMap& properties) const { cmValue input = target->GetProperty(propName); if (input) { @@ -158,7 +145,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty( } void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, const std::string& outputName, + std::string const& propName, std::string const& outputName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties) @@ -180,6 +167,15 @@ void cmExportFileGenerator::PopulateInterfaceProperty( } } +void cmExportFileGenerator::PopulateInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, + properties); +} + bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, @@ -188,7 +184,7 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( if (!target->IsLinkable()) { return false; } - static const std::array linkIfaceProps = { + static std::array const linkIfaceProps = { { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT", "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" } }; @@ -208,319 +204,27 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( return hadINTERFACE_LINK_LIBRARIES; } -static bool isSubDirectory(std::string const& a, std::string const& b) -{ - return (cmSystemTools::ComparePath(a, b) || - cmSystemTools::IsSubDirectory(a, b)); -} - -static bool checkInterfaceDirs(const std::string& prepro, - cmGeneratorTarget const* target, - const std::string& prop) -{ - std::string const& installDir = - target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); - std::string const& topSourceDir = - target->GetLocalGenerator()->GetSourceDirectory(); - std::string const& topBinaryDir = - target->GetLocalGenerator()->GetBinaryDirectory(); - - std::vector parts; - cmGeneratorExpression::Split(prepro, parts); - - const bool inSourceBuild = topSourceDir == topBinaryDir; - - bool hadFatalError = false; - - for (std::string const& li : parts) { - size_t genexPos = cmGeneratorExpression::Find(li); - if (genexPos == 0) { - continue; - } - if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) { - continue; - } - MessageType messageType = MessageType::FATAL_ERROR; - std::ostringstream e; - if (genexPos != std::string::npos) { - if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { - switch (target->GetPolicyStatusCMP0041()) { - case cmPolicies::WARN: - messageType = MessageType::WARNING; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0041) << "\n"; - break; - case cmPolicies::OLD: - continue; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - hadFatalError = true; - break; // Issue fatal message. - } - } else { - hadFatalError = true; - } - } - if (!cmSystemTools::FileIsFullPath(li)) { - /* clang-format off */ - e << "Target \"" << target->GetName() << "\" " << prop << - " property contains relative path:\n" - " \"" << li << "\""; - /* clang-format on */ - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - bool inBinary = isSubDirectory(li, topBinaryDir); - bool inSource = isSubDirectory(li, topSourceDir); - if (isSubDirectory(li, installDir)) { - // The include directory is inside the install tree. If the - // install tree is not inside the source tree or build tree then - // fall through to the checks below that the include directory is not - // also inside the source tree or build tree. - bool shouldContinue = - (!inBinary || isSubDirectory(installDir, topBinaryDir)) && - (!inSource || isSubDirectory(installDir, topSourceDir)); - - if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { - if (!shouldContinue) { - switch (target->GetPolicyStatusCMP0052()) { - case cmPolicies::WARN: { - std::ostringstream s; - s << cmPolicies::GetPolicyWarning(cmPolicies::CMP0052) << "\n"; - s << "Directory:\n \"" << li - << "\"\nin " - "INTERFACE_INCLUDE_DIRECTORIES of target \"" - << target->GetName() - << "\" is a subdirectory of the install " - "directory:\n \"" - << installDir - << "\"\nhowever it is also " - "a subdirectory of the " - << (inBinary ? "build" : "source") << " tree:\n \"" - << (inBinary ? topBinaryDir : topSourceDir) << "\"\n"; - target->GetLocalGenerator()->IssueMessage( - MessageType::AUTHOR_WARNING, s.str()); - CM_FALLTHROUGH; - } - case cmPolicies::OLD: - shouldContinue = true; - break; - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::NEW: - break; - } - } - } - if (shouldContinue) { - continue; - } - } - if (inBinary) { - /* clang-format off */ - e << "Target \"" << target->GetName() << "\" " << prop << - " property contains path:\n" - " \"" << li << "\"\nwhich is prefixed in the build directory."; - /* clang-format on */ - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - if (!inSourceBuild) { - if (inSource) { - e << "Target \"" << target->GetName() << "\" " << prop - << " property contains path:\n" - " \"" - << li << "\"\nwhich is prefixed in the source directory."; - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - } - } - return !hadFatalError; -} - -static void prefixItems(std::string& exportDirs) +void cmExportFileGenerator::AddImportPrefix(std::string& exportDirs) const { std::vector entries; cmGeneratorExpression::Split(exportDirs, entries); exportDirs.clear(); - const char* sep = ""; + char const* sep = ""; + cm::string_view const& prefixWithSlash = this->GetImportPrefixWithSlash(); for (std::string const& e : entries) { exportDirs += sep; sep = ";"; if (!cmSystemTools::FileIsFullPath(e) && - e.find("${_IMPORT_PREFIX}") == std::string::npos) { - exportDirs += "${_IMPORT_PREFIX}/"; + !cmHasPrefix(e, prefixWithSlash)) { + exportDirs += prefixWithSlash; } exportDirs += e; } } -void cmExportFileGenerator::PopulateSourcesInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_SOURCES"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateIncludeDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties, cmTargetExport const& te, - std::string& includesDestinationDirs) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - includesDestinationDirs.clear(); - - const char* propName = "INTERFACE_INCLUDE_DIRECTORIES"; - cmValue input = target->GetProperty(propName); - - cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance()); - - std::string dirs = cmGeneratorExpression::Preprocess( - cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)), - preprocessRule, true); - this->ReplaceInstallPrefix(dirs); - std::unique_ptr cge = ge.Parse(dirs); - std::string exportDirs = - cge->Evaluate(target->GetLocalGenerator(), "", target); - - if (cge->GetHadContextSensitiveCondition()) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::ostringstream e; - e << "Target \"" << target->GetName() - << "\" is installed with " - "INCLUDES DESTINATION set to a context sensitive path. Paths which " - "depend on the configuration, policy values or the link interface " - "are " - "not supported. Consider using target_include_directories instead."; - lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return; - } - - if (!input && exportDirs.empty()) { - return; - } - if ((input && input->empty()) && exportDirs.empty()) { - // Set to empty - properties[propName].clear(); - return; - } - - prefixItems(exportDirs); - includesDestinationDirs = exportDirs; - - std::string includes = (input ? *input : ""); - const char* sep = input ? ";" : ""; - includes += sep + exportDirs; - std::string prepro = - cmGeneratorExpression::Preprocess(includes, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target); - - if (!checkInterfaceDirs(prepro, target, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateLinkDependsInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_LINK_DEPENDS"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateLinkDirectoriesInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_LINK_DIRECTORIES"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, - properties); -} - -static void getPropertyContents(cmGeneratorTarget const* tgt, - const std::string& prop, - std::set& ifaceProperties) +namespace { +void getPropertyContents(cmGeneratorTarget const* tgt, std::string const& prop, + std::set& ifaceProperties) { cmValue p = tgt->GetProperty(prop); if (!p) { @@ -530,9 +234,9 @@ static void getPropertyContents(cmGeneratorTarget const* tgt, ifaceProperties.insert(content.begin(), content.end()); } -static void getCompatibleInterfaceProperties( - cmGeneratorTarget const* target, std::set& ifaceProperties, - const std::string& config) +void getCompatibleInterfaceProperties(cmGeneratorTarget const* target, + std::set& ifaceProperties, + std::string const& config) { if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { // object libraries have no link information, so nothing to compute @@ -551,7 +255,7 @@ static void getCompatibleInterfaceProperties( return; } - const cmComputeLinkInformation::ItemVector& deps = info->GetItems(); + cmComputeLinkInformation::ItemVector const& deps = info->GetItems(); for (auto const& dep : deps) { if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { @@ -567,9 +271,10 @@ static void getCompatibleInterfaceProperties( ifaceProperties); } } +} void cmExportFileGenerator::PopulateCompatibleInterfaceProperties( - cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) + cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) const { this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", gtarget, properties); @@ -626,20 +331,12 @@ void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties( } } -void cmExportFileGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& properties) +bool cmExportFileGenerator::NoteLinkedTarget( + cmGeneratorTarget const* /*target*/, std::string const& /*linkedName*/, + cmGeneratorTarget const* /*linkedTarget*/) { - if (!properties.empty()) { - std::string targetName = - cmStrCat(this->Namespace, target->GetExportName()); - os << "set_target_properties(" << targetName << " PROPERTIES\n"; - for (auto const& property : properties) { - os << " " << property.first << " " - << cmExportFileGeneratorEscape(property.second) << "\n"; - } - os << ")\n\n"; - } + // Default implementation does nothing; only needed by some generators. + return true; } bool cmExportFileGenerator::AddTargetNamespace(std::string& input, @@ -663,8 +360,9 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, if (tgt->IsImported()) { input = tgt->GetName(); - return true; + return this->NoteLinkedTarget(target, input, tgt); } + if (this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) { input = this->Namespace + tgt->GetExportName(); } else { @@ -676,7 +374,8 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input, input = tgt->GetName(); } } - return true; + + return this->NoteLinkedTarget(target, input, tgt); } void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( @@ -805,74 +504,14 @@ void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( } } -void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) +void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) const { // Do nothing } -void cmExportFileGenerator::SetImportLinkInterface( - const std::string& config, std::string const& suffix, - cmGeneratorExpression::PreprocessContext preprocessRule, - cmGeneratorTarget const* target, ImportPropertyMap& properties) -{ - // Add the transitive link dependencies for this configuration. - cmLinkInterface const* iface = target->GetLinkInterface(config, target); - if (!iface) { - return; - } - - if (iface->ImplementationIsInterface) { - // Policy CMP0022 must not be NEW. - this->SetImportLinkProperty( - suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries, - properties, ImportLinkPropertyTargetNames::Yes); - return; - } - - cmValue propContent; - - if (cmValue prop_suffixed = - target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) { - propContent = prop_suffixed; - } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) { - propContent = prop; - } else { - return; - } - - const bool newCMP0022Behavior = - target->GetPolicyStatusCMP0022() != cmPolicies::WARN && - target->GetPolicyStatusCMP0022() != cmPolicies::OLD; - - if (newCMP0022Behavior && !this->ExportOld) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::ostringstream e; - e << "Target \"" << target->GetName() - << "\" has policy CMP0022 enabled, " - "but also has old-style LINK_INTERFACE_LIBRARIES properties " - "populated, but it was exported without the " - "EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties"; - lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return; - } - - if (propContent->empty()) { - properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*propContent, preprocessRule); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target, - ReplaceFreeTargets); - properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro; - } -} - void cmExportFileGenerator::SetImportDetailProperties( - const std::string& config, std::string const& suffix, - cmGeneratorTarget* target, ImportPropertyMap& properties) + std::string const& config, std::string const& suffix, + cmGeneratorTarget const* target, ImportPropertyMap& properties) { // Get the makefile in which to lookup target information. cmMakefile* mf = target->Makefile; @@ -943,20 +582,22 @@ void cmExportFileGenerator::SetImportDetailProperties( } } -static std::string const& asString(std::string const& l) +namespace { +std::string const& asString(std::string const& l) { return l; } -static std::string const& asString(cmLinkItem const& l) +std::string const& asString(cmLinkItem const& l) { return l.AsStr(); } +} template void cmExportFileGenerator::SetImportLinkProperty( std::string const& suffix, cmGeneratorTarget const* target, - const std::string& propName, std::vector const& entries, + std::string const& propName, std::vector const& entries, ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames) { // Skip the property if there are no entries. @@ -968,7 +609,7 @@ void cmExportFileGenerator::SetImportLinkProperty( // Construct the property value. std::string link_entries; - const char* sep = ""; + char const* sep = ""; for (T const& l : entries) { // Separate this from the previous entry. link_entries += sep; @@ -988,428 +629,17 @@ void cmExportFileGenerator::SetImportLinkProperty( properties[prop] = link_entries; } -void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) -{ - // Protect that file against use with older CMake versions. - /* clang-format off */ - os << "# Generated by CMake\n\n"; - os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n" - << " message(FATAL_ERROR \"CMake >= 2.8.0 required\")\n" - << "endif()\n" - << "if(CMAKE_VERSION VERSION_LESS \"" - << this->RequiredCMakeVersionMajor << '.' - << this->RequiredCMakeVersionMinor << '.' - << this->RequiredCMakeVersionPatch << "\")\n" - << " message(FATAL_ERROR \"CMake >= " - << this->RequiredCMakeVersionMajor << '.' - << this->RequiredCMakeVersionMinor << '.' - << this->RequiredCMakeVersionPatch << " required\")\n" - << "endif()\n"; - /* clang-format on */ - - // Isolate the file policy level. - // Support CMake versions as far back as the - // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW - // policy settings for up to CMake 3.28 (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 " - << this->RequiredCMakeVersionMajor << '.' - << this->RequiredCMakeVersionMinor << '.' - << this->RequiredCMakeVersionPatch << "...3.28)\n"; - /* clang-format on */ -} - -void cmExportFileGenerator::GeneratePolicyFooterCode(std::ostream& os) -{ - os << "cmake_policy(POP)\n"; -} - -void cmExportFileGenerator::GenerateImportHeaderCode(std::ostream& os, - const std::string& config) -{ - os << "#----------------------------------------------------------------\n" - << "# Generated CMake target import file"; - if (!config.empty()) { - os << " for configuration \"" << config << "\".\n"; - } else { - os << ".\n"; - } - os << "#----------------------------------------------------------------\n" - << "\n"; - this->GenerateImportVersionCode(os); -} - -void cmExportFileGenerator::GenerateImportFooterCode(std::ostream& os) -{ - os << "# Commands beyond this point should not need to know the version.\n" - << "set(CMAKE_IMPORT_FILE_VERSION)\n"; -} - -void cmExportFileGenerator::GenerateImportVersionCode(std::ostream& os) -{ - // Store an import file format version. This will let us change the - // format later while still allowing old import files to work. - /* clang-format off */ - os << "# Commands may need to know the format version.\n" - << "set(CMAKE_IMPORT_FILE_VERSION 1)\n" - << "\n"; - /* clang-format on */ -} +template void cmExportFileGenerator::SetImportLinkProperty( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); -void cmExportFileGenerator::GenerateExpectedTargetsCode( - std::ostream& os, const std::string& expectedTargets) -{ - /* clang-format off */ - os << "# Protect against multiple inclusion, which would fail when already " - "imported targets are added once more.\n" - "set(_cmake_targets_defined \"\")\n" - "set(_cmake_targets_not_defined \"\")\n" - "set(_cmake_expected_targets \"\")\n" - "foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n" - " list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n" - " if(TARGET \"${_cmake_expected_target}\")\n" - " list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n" - " else()\n" - " list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n" - " endif()\n" - "endforeach()\n" - "unset(_cmake_expected_target)\n" - "if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n" - " unset(_cmake_targets_defined)\n" - " unset(_cmake_targets_not_defined)\n" - " unset(_cmake_expected_targets)\n" - " unset(CMAKE_IMPORT_FILE_VERSION)\n" - " cmake_policy(POP)\n" - " return()\n" - "endif()\n" - "if(NOT _cmake_targets_defined STREQUAL \"\")\n" - " string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n" - " string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n" - " message(FATAL_ERROR \"Some (but not all) targets in this export " - "set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n" - "Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n" - "endif()\n" - "unset(_cmake_targets_defined)\n" - "unset(_cmake_targets_not_defined)\n" - "unset(_cmake_expected_targets)\n" - "\n\n"; - /* clang-format on */ -} - -void cmExportFileGenerator::GenerateImportTargetCode( - std::ostream& os, cmGeneratorTarget const* target, - cmStateEnums::TargetType targetType) -{ - // Construct the imported target name. - std::string targetName = this->Namespace; - - targetName += target->GetExportName(); - - // Create the imported target. - os << "# Create imported target " << targetName << "\n"; - switch (targetType) { - case cmStateEnums::EXECUTABLE: - os << "add_executable(" << targetName << " IMPORTED)\n"; - break; - case cmStateEnums::STATIC_LIBRARY: - os << "add_library(" << targetName << " STATIC IMPORTED)\n"; - break; - case cmStateEnums::SHARED_LIBRARY: - os << "add_library(" << targetName << " SHARED IMPORTED)\n"; - break; - case cmStateEnums::MODULE_LIBRARY: - os << "add_library(" << targetName << " MODULE IMPORTED)\n"; - break; - case cmStateEnums::UNKNOWN_LIBRARY: - os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n"; - break; - case cmStateEnums::OBJECT_LIBRARY: - os << "add_library(" << targetName << " OBJECT IMPORTED)\n"; - break; - case cmStateEnums::INTERFACE_LIBRARY: - os << "add_library(" << targetName << " INTERFACE IMPORTED)\n"; - break; - default: // should never happen - break; - } - - // Mark the imported executable if it has exports. - if (target->IsExecutableWithExports() || - (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) { - os << "set_property(TARGET " << targetName - << " PROPERTY ENABLE_EXPORTS 1)\n"; - } - - // Mark the imported library if it is a framework. - if (target->IsFrameworkOnApple()) { - os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n"; - } - - // Mark the imported executable if it is an application bundle. - if (target->IsAppBundleOnApple()) { - os << "set_property(TARGET " << targetName - << " PROPERTY MACOSX_BUNDLE 1)\n"; - } - - if (target->IsCFBundleOnApple()) { - os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n"; - } - - // generate DEPRECATION - if (target->IsDeprecated()) { - os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION " - << cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n"; - } - - if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { - os << "set_property(TARGET " << targetName - << " PROPERTY IMPORTED_NO_SYSTEM 1)\n"; - } - - if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) { - os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n"; - } - - os << "\n"; -} - -void cmExportFileGenerator::GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation) -{ - // Construct the imported target name. - std::string targetName = this->Namespace; - - targetName += target->GetExportName(); - - // Set the import properties. - os << "# Import target \"" << targetName << "\" for configuration \"" - << config << "\"\n"; - os << "set_property(TARGET " << targetName - << " APPEND PROPERTY IMPORTED_CONFIGURATIONS "; - if (!config.empty()) { - os << cmSystemTools::UpperCase(config); - } else { - os << "NOCONFIG"; - } - os << ")\n"; - os << "set_target_properties(" << targetName << " PROPERTIES\n"; - std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix); - for (auto const& property : properties) { - if (importedXcFrameworkLocation.empty() || - property.first != importedLocationProp) { - os << " " << property.first << " " - << cmExportFileGeneratorEscape(property.second) << "\n"; - } - } - os << " )\n"; - if (!importedXcFrameworkLocation.empty()) { - auto importedLocationIt = properties.find(importedLocationProp); - if (importedLocationIt != properties.end()) { - os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY " - << cmExportFileGeneratorEscape(importedXcFrameworkLocation) - << ")\n" - " set_property(TARGET " - << targetName << " PROPERTY " << importedLocationProp << " " - << cmExportFileGeneratorEscape(importedXcFrameworkLocation) - << ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY " - << importedLocationProp << " " - << cmExportFileGeneratorEscape(importedLocationIt->second) - << ")\nendif()\n"; - } - } - os << "\n"; -} - -void cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os) -{ - os << "include(CMakeFindDependencyMacro)\n"; - std::map packageDependencies; - auto* exportSet = this->GetExportSet(); - if (exportSet) { - packageDependencies = exportSet->GetPackageDependencies(); - } - - for (cmGeneratorTarget const* gt : this->ExternalTargets) { - std::string findPackageName; - auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME"); - cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack(); - if (!exportFindPackageName.IsEmpty()) { - findPackageName = *exportFindPackageName; - } else { - if (!pkgStack.Empty()) { - cmFindPackageCall const& fpc = pkgStack.Top(); - findPackageName = fpc.Name; - } - } - if (!findPackageName.empty()) { - auto& dep = packageDependencies[findPackageName]; - if (!pkgStack.Empty()) { - dep.FindPackageIndex = pkgStack.Top().Index; - } - if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) { - dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On; - } - } - } - - std::vector> - packageDependenciesSorted(packageDependencies.begin(), - packageDependencies.end()); - std::sort( - packageDependenciesSorted.begin(), packageDependenciesSorted.end(), - [](const std::pair& lhs, - const std::pair& rhs) - -> bool { - if (lhs.second.SpecifiedIndex) { - if (rhs.second.SpecifiedIndex) { - return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex; - } - assert(rhs.second.FindPackageIndex); - return true; - } - assert(lhs.second.FindPackageIndex); - if (rhs.second.SpecifiedIndex) { - return false; - } - assert(rhs.second.FindPackageIndex); - return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex; - }); - - for (auto const& it : packageDependenciesSorted) { - if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) { - os << "find_dependency(" << it.first; - for (auto const& arg : it.second.ExtraArguments) { - os << " " << cmOutputConverter::EscapeForCMake(arg); - } - os << ")\n"; - } - } - os << "\n\n"; -} - -void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os) -{ - if (this->MissingTargets.empty()) { - /* clang-format off */ - os << "# This file does not depend on other imported targets which have\n" - "# been exported from the same project but in a separate " - "export set.\n\n"; - /* clang-format on */ - return; - } - /* clang-format off */ - os << "# Make sure the targets which have been exported in some other\n" - "# export set exist.\n" - "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" - "foreach(_target "; - /* clang-format on */ - std::set emitted; - for (std::string const& missingTarget : this->MissingTargets) { - if (emitted.insert(missingTarget).second) { - os << "\"" << missingTarget << "\" "; - } - } - /* clang-format off */ - os << ")\n" - " if(NOT TARGET \"${_target}\" )\n" - " set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \"" - "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")" - "\n" - " endif()\n" - "endforeach()\n" - "\n" - "if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" - " if(CMAKE_FIND_PACKAGE_NAME)\n" - " set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n" - " set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE " - "\"The following imported targets are " - "referenced, but are missing: " - "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n" - " else()\n" - " message(FATAL_ERROR \"The following imported targets are " - "referenced, but are missing: " - "${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n" - " endif()\n" - "endif()\n" - "unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n" - "\n"; - /* clang-format on */ -} - -void cmExportFileGenerator::GenerateImportedFileCheckLoop(std::ostream& os) -{ - // Add code which verifies at cmake time that the file which is being - // imported actually exists on disk. This should in theory always be theory - // case, but still when packages are split into normal and development - // packages this might get broken (e.g. the Config.cmake could be part of - // the non-development package, something similar happened to me without - // on SUSE with a mysql pkg-config file, which claimed everything is fine, - // but the development package was not installed.). - /* clang-format off */ - os << "# Loop over all imported files and verify that they actually exist\n" - "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n" - " if(CMAKE_VERSION VERSION_LESS \"3.28\"\n" - " OR NOT DEFINED " - "_cmake_import_check_xcframework_for_${_cmake_target}\n" - " OR NOT IS_DIRECTORY " - "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n" - " foreach(_cmake_file IN LISTS " - "\"_cmake_import_check_files_for_${_cmake_target}\")\n" - " if(NOT EXISTS \"${_cmake_file}\")\n" - " message(FATAL_ERROR \"The imported target " - "\\\"${_cmake_target}\\\" references the file\n" - " \\\"${_cmake_file}\\\"\n" - "but this file does not exist. Possible reasons include:\n" - "* The file was deleted, renamed, or moved to another location.\n" - "* An install or uninstall procedure did not complete successfully.\n" - "* The installation package was faulty and contained\n" - " \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n" - "but not all the files it references.\n" - "\")\n" - " endif()\n" - " endforeach()\n" - " endif()\n" - " unset(_cmake_file)\n" - " unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n" - "endforeach()\n" - "unset(_cmake_target)\n" - "unset(_cmake_import_check_targets)\n" - "\n"; - /* clang-format on */ -} - -void cmExportFileGenerator::GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set& importedLocations, - const std::string& importedXcFrameworkLocation) -{ - // Construct the imported target name. - std::string targetName = cmStrCat(this->Namespace, target->GetExportName()); - - os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n"; - if (!importedXcFrameworkLocation.empty()) { - os << "set(_cmake_import_check_xcframework_for_" << targetName << ' ' - << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n"; - } - os << "list(APPEND _cmake_import_check_files_for_" << targetName << " "; - - for (std::string const& li : importedLocations) { - auto pi = properties.find(li); - if (pi != properties.end()) { - os << cmExportFileGeneratorEscape(pi->second) << " "; - } - } - - os << ")\n\n"; -} +template void cmExportFileGenerator::SetImportLinkProperty( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); +namespace { enum class ExportWhen { Defined, @@ -1423,7 +653,6 @@ enum class PropertyType IncludePaths, }; -namespace { bool PropertyTypeIsForPaths(PropertyType pt) { switch (pt) { @@ -1437,18 +666,6 @@ bool PropertyTypeIsForPaths(PropertyType pt) } } -struct ModuleTargetPropertyTable -{ - cm::static_string_view Name; - ExportWhen Cond; -}; - -struct ModulePropertyTable -{ - cm::static_string_view Name; - PropertyType Type; -}; - bool cmExportFileGenerator::PopulateCxxModuleExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, cmGeneratorExpression::PreprocessContext ctx, @@ -1458,7 +675,13 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( return true; } - const ModuleTargetPropertyTable exportedDirectModuleProperties[] = { + struct ModuleTargetPropertyTable + { + cm::static_string_view Name; + ExportWhen Cond; + }; + + ModuleTargetPropertyTable const exportedDirectModuleProperties[] = { { "CXX_EXTENSIONS"_s, ExportWhen::Defined }, // Always define this property as it is an intrinsic property of the target // and should not be inherited from the in-scope `CMAKE_CXX_MODULE_STD` @@ -1484,7 +707,13 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } } - const ModulePropertyTable exportedModuleProperties[] = { + struct ModulePropertyTable + { + cm::static_string_view Name; + PropertyType Type; + }; + + ModulePropertyTable const exportedModuleProperties[] = { { "INCLUDE_DIRECTORIES"_s, PropertyType::IncludePaths }, { "COMPILE_DEFINITIONS"_s, PropertyType::Strings }, { "COMPILE_OPTIONS"_s, PropertyType::Strings }, @@ -1505,7 +734,7 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( if (ctx == cmGeneratorExpression::InstallInterface && PropertyTypeIsForPaths(propEntry.Type)) { this->ReplaceInstallPrefix(properties[exportedPropName]); - prefixItems(properties[exportedPropName]); + this->AddImportPrefix(properties[exportedPropName]); if (propEntry.Type == PropertyType::IncludePaths && !includesDestinationDirs.empty()) { if (!properties[exportedPropName].empty()) { @@ -1517,7 +746,7 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } } - const cm::static_string_view exportedLinkModuleProperties[] = { + cm::static_string_view const exportedLinkModuleProperties[] = { "LINK_LIBRARIES"_s, }; for (auto const& propName : exportedLinkModuleProperties) { @@ -1531,8 +760,8 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( auto const exportedPropName = cmStrCat("IMPORTED_CXX_MODULES_", propName); auto value = cmGeneratorExpression::Preprocess(*prop, ctx); - this->ResolveTargetsInGeneratorExpressions( - value, gte, cmExportFileGenerator::ReplaceFreeTargets); + this->ResolveTargetsInGeneratorExpressions(value, gte, + ReplaceFreeTargets); properties[exportedPropName] = value; } } @@ -1542,9 +771,9 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( bool cmExportFileGenerator::PopulateExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, - std::string& errorMessage) + std::string& errorMessage) const { - const auto& targetProperties = gte->Target->GetProperties(); + auto const& targetProperties = gte->Target->GetProperties(); if (cmValue exportProperties = targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) { for (auto& prop : cmList{ *exportProperties }) { @@ -1579,95 +808,3 @@ bool cmExportFileGenerator::PopulateExportProperties( } return true; } - -void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte, - std::ostream& os, - cmTargetExport* te) -{ - auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets(); - if (!interfaceFileSets.empty()) { - std::string targetName = cmStrCat(this->Namespace, gte->GetExportName()); - os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n" - " target_sources(" - << targetName << "\n"; - - for (auto const& name : interfaceFileSets) { - auto* fileSet = gte->Target->GetFileSet(name); - if (!fileSet) { - gte->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("File set \"", name, - "\" is listed in interface file sets of ", gte->GetName(), - " but has not been created")); - return; - } - - os << " INTERFACE" - << "\n FILE_SET " << cmOutputConverter::EscapeForCMake(name) - << "\n TYPE " - << cmOutputConverter::EscapeForCMake(fileSet->GetType()) - << "\n BASE_DIRS " - << this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES " - << this->GetFileSetFiles(gte, fileSet, te) << "\n"; - } - - os << " )\nelse()\n set_property(TARGET " << targetName - << "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES"; - for (auto const& name : interfaceFileSets) { - auto* fileSet = gte->Target->GetFileSet(name); - if (!fileSet) { - gte->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("File set \"", name, - "\" is listed in interface file sets of ", gte->GetName(), - " but has not been created")); - return; - } - - if (fileSet->GetType() == "HEADERS"_s) { - os << "\n " << this->GetFileSetDirectories(gte, fileSet, te); - } - } - os << "\n )\nendif()\n\n"; - } -} - -void cmExportFileGenerator::GenerateCxxModuleInformation( - std::string const& name, 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-" << name << ".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-", name, ".cmake"); - - // Include all configuration-specific include files. - cmGeneratedFileStream ap(trampoline_path, true); - ap.SetCopyIfDifferent(true); - - this->GenerateCxxModuleConfigInformation(name, ap); -} - -void cmExportFileGenerator::SetRequiredCMakeVersion(unsigned int major, - unsigned int minor, - unsigned int patch) -{ - if (CMake_VERSION_ENCODE(major, minor, patch) > - CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor, - this->RequiredCMakeVersionMinor, - this->RequiredCMakeVersionPatch)) { - this->RequiredCMakeVersionMajor = major; - this->RequiredCMakeVersionMinor = minor; - this->RequiredCMakeVersionPatch = patch; - } -} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f275a1247..b1c9ce3b1 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -8,18 +8,16 @@ #include #include #include +#include #include +#include + #include "cmGeneratorExpression.h" -#include "cmStateTypes.h" -#include "cmVersion.h" -#include "cmVersionConfig.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; class cmLocalGenerator; -class cmTargetExport; #define STRINGIFY_HELPER(X) #X #define STRINGIFY(X) STRINGIFY_HELPER(X) @@ -32,12 +30,9 @@ class cmTargetExport; : #major "." #minor ".0") /** \class cmExportFileGenerator - * \brief Generate a file exporting targets from a build or install tree. + * \brief Generate files exporting targets from a build or install tree. * - * cmExportFileGenerator is the superclass for - * cmExportBuildFileGenerator and cmExportInstallFileGenerator. It - * contains common code generation routines for the two kinds of - * export implementations. + * cmExportFileGenerator is the interface class for generating export files. */ class cmExportFileGenerator { @@ -46,65 +41,28 @@ public: virtual ~cmExportFileGenerator() = default; /** Set the full path to the export file to generate. */ - void SetExportFile(const char* mainFile); - const std::string& GetMainExportFileName() const; + void SetExportFile(char const* mainFile); + std::string const& GetMainExportFileName() const; /** Set the namespace in which to place exported target names. */ - void SetNamespace(const std::string& ns) { this->Namespace = ns; } + void SetNamespace(std::string const& ns) { this->Namespace = ns; } std::string GetNamespace() const { return this->Namespace; } - void SetExportOld(bool exportOld) { this->ExportOld = exportOld; } - /** Add a configuration to be exported. */ - void AddConfiguration(const std::string& config); + void AddConfiguration(std::string const& config); - /** Actually generate the export file. Returns whether there was an - error. */ + /** Create and actually generate the export file. Returns whether there was + an error. */ bool GenerateImportFile(); - void SetExportPackageDependencies(bool exportPackageDependencies) - { - this->ExportPackageDependencies = exportPackageDependencies; - } - protected: using ImportPropertyMap = std::map; - // Generate per-configuration target information to the given output - // stream. - void GenerateImportConfig(std::ostream& os, const std::string& config); - - // Methods to implement export file code generation. - virtual void GeneratePolicyHeaderCode(std::ostream& os); - virtual void GeneratePolicyFooterCode(std::ostream& os); - virtual void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = ""); - virtual void GenerateImportFooterCode(std::ostream& os); - void GenerateImportVersionCode(std::ostream& os); - virtual void GenerateImportTargetCode(std::ostream& os, - cmGeneratorTarget const* target, - cmStateEnums::TargetType targetType); - virtual void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation); - virtual void GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set& importedLocations, - const std::string& importedXcFrameworkLocation); - virtual void GenerateImportedFileCheckLoop(std::ostream& os); - virtual void GenerateMissingTargetsCheckCode(std::ostream& os); - virtual void GenerateFindDependencyCalls(std::ostream& os); - - virtual void GenerateExpectedTargetsCode(std::ostream& os, - const std::string& expectedTargets); - // Collect properties with detailed information about targets beyond // their location on disk. - void SetImportDetailProperties(const std::string& config, + void SetImportDetailProperties(std::string const& config, std::string const& suffix, - cmGeneratorTarget* target, + cmGeneratorTarget const* target, ImportPropertyMap& properties); enum class ImportLinkPropertyTargetNames @@ -115,65 +73,71 @@ protected: template void SetImportLinkProperty(std::string const& suffix, cmGeneratorTarget const* target, - const std::string& propName, + std::string const& propName, std::vector const& entries, ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames); + /** Generate the export file to the given output stream. Returns whether + there was an error. */ + virtual bool GenerateImportFile(std::ostream& os) = 0; + /** Each subclass knows how to generate its kind of export file. */ virtual bool GenerateMainFile(std::ostream& os) = 0; + /** Generate per-configuration target information to the given output + stream. */ + virtual void GenerateImportConfig(std::ostream& os, + std::string const& config); + /** Each subclass knows where the target files are located. */ virtual void GenerateImportTargetsConfig(std::ostream& os, - const std::string& config, + std::string const& config, std::string const& suffix) = 0; + /** Record a target referenced by an exported target. */ + virtual bool NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget); + /** Each subclass knows how to deal with a target that is missing from an * export set. */ virtual void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) = 0; - void PopulateInterfaceProperty(const std::string&, + + /** Complain when a duplicate target is encountered. */ + virtual void ComplainAboutDuplicateTarget( + std::string const& targetName) const = 0; + + virtual cm::string_view GetImportPrefixWithSlash() const = 0; + + void AddImportPrefix(std::string& exportDirs) const; + + void PopulateInterfaceProperty(std::string const& propName, + cmGeneratorTarget const* target, + ImportPropertyMap& properties) const; + void PopulateInterfaceProperty(std::string const& propName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); bool PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); - void PopulateInterfaceProperty(const std::string& propName, - cmGeneratorTarget const* target, - ImportPropertyMap& properties); - void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, - ImportPropertyMap& properties); - void PopulateCustomTransitiveInterfaceProperties( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - virtual void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties); - void PopulateIncludeDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties, cmTargetExport const& te, - std::string& includesDestinationDirs); - void PopulateSourcesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - void PopulateLinkDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - void PopulateLinkDependsInterface( + + bool PopulateInterfaceProperties( cmGeneratorTarget const* target, + std::string const& includesDestinationDirs, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties); - void SetImportLinkInterface( - const std::string& config, std::string const& suffix, - cmGeneratorExpression::PreprocessContext preprocessRule, - cmGeneratorTarget const* target, ImportPropertyMap& properties); + virtual void ReportError(std::string const& errorMessage) const = 0; + + using ExportInfo = std::pair, std::string>; + + /** Find the set of export files and the unique namespace (if any) for a + * target. */ + virtual ExportInfo FindExportInfo(cmGeneratorTarget const* target) const = 0; enum FreeTargetsReplace { @@ -185,36 +149,26 @@ protected: std::string& input, cmGeneratorTarget const* target, FreeTargetsReplace replace = NoReplaceFreeTargets); - bool PopulateCxxModuleExportProperties( - cmGeneratorTarget const* gte, ImportPropertyMap& properties, - cmGeneratorExpression::PreprocessContext ctx, - std::string const& includesDestinationDirs, std::string& errorMessage); - bool PopulateExportProperties(cmGeneratorTarget const* gte, - ImportPropertyMap& properties, - std::string& errorMessage); + virtual cmExportSet* GetExportSet() const { return nullptr; } - void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, - cmTargetExport* te = nullptr); + virtual void ReplaceInstallPrefix(std::string& input) const; - void GenerateCxxModuleInformation(std::string const& name, std::ostream& os); + virtual std::string InstallNameDir(cmGeneratorTarget const* target, + std::string const& config) = 0; - virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* te) = 0; - virtual std::string GetFileSetFiles(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* te) = 0; + /** Get the temporary location of the config-agnostic C++ module file. */ + virtual std::string GetCxxModuleFile(std::string const& name) const = 0; - virtual cmExportSet* GetExportSet() const { return nullptr; } + virtual std::string GetCxxModulesDirectory() const = 0; + virtual void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream& os) const = 0; - void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, - unsigned int patch); + bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, + cmLocalGenerator const* lg); // The namespace in which the exports are placed in the generated file. std::string Namespace; - bool ExportOld; - // The set of configurations to export. std::vector Configurations; @@ -223,40 +177,47 @@ protected: std::string FileDir; std::string FileBase; std::string FileExt; - bool AppendMode; + bool AppendMode = false; // The set of targets included in the export. - std::set ExportedTargets; + std::set ExportedTargets; std::vector MissingTargets; std::set ExternalTargets; - unsigned int RequiredCMakeVersionMajor = 2; - unsigned int RequiredCMakeVersionMinor = 8; - unsigned int RequiredCMakeVersionPatch = 3; - - bool ExportPackageDependencies = false; - private: - void PopulateInterfaceProperty(const std::string&, const std::string&, + void PopulateInterfaceProperty(std::string const& propName, + std::string const& outputName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); - bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, - cmLocalGenerator const* lg); + void PopulateCompatibleInterfaceProperties( + cmGeneratorTarget const* target, ImportPropertyMap& properties) const; + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + bool PopulateCxxModuleExportProperties( + cmGeneratorTarget const* gte, ImportPropertyMap& properties, + cmGeneratorExpression::PreprocessContext ctx, + std::string const& includesDestinationDirs, std::string& errorMessage); + bool PopulateExportProperties(cmGeneratorTarget const* gte, + ImportPropertyMap& properties, + std::string& errorMessage) const; void ResolveTargetsInGeneratorExpression(std::string& input, cmGeneratorTarget const* target, cmLocalGenerator const* lg); +}; - virtual void ReplaceInstallPrefix(std::string& input); - - virtual std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) = 0; +extern template void cmExportFileGenerator::SetImportLinkProperty( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); - virtual std::string GetCxxModulesDirectory() const = 0; - virtual void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream& os) const = 0; -}; +extern template void cmExportFileGenerator::SetImportLinkProperty( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index eaa85f319..c9c2f4e52 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -4,14 +4,15 @@ #include #include -#include +#include #include -#include "cmExportBuildAndroidMKGenerator.h" #include "cmExportSet.h" +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmInstallExportGenerator.h" #include "cmInstallTargetGenerator.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -24,8 +25,55 @@ cmExportInstallAndroidMKGenerator::cmExportInstallAndroidMKGenerator( { } +void cmExportInstallAndroidMKGenerator::ReportDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "install(EXPORT_ANDROID_MK \"" << this->GetExportSet()->GetName() + << "\" ...) " + << "includes target \"" << targetName + << "\" more than once in the export set."; + this->ReportError(e.str()); +} + +bool cmExportInstallAndroidMKGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector allTargets; + { + auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; + + if (!this->CollectExports(visitor)) { + return false; + } + } + + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget const* gt = te->Target; + + this->GenerateImportTargetCode(os, gt, this->GetExportTargetType(te)); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties); + } + + this->GenerateInterfaceProperties(gt, os, properties); + } + + return true; +} + void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( - std::ostream& os, const std::string&) + std::ostream& os, std::string const&) { std::string installDir = this->IEGen->GetDestination(); os << "LOCAL_PATH := $(call my-dir)\n"; @@ -53,10 +101,6 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( } } -void cmExportInstallAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) -{ -} - void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) @@ -73,61 +117,3 @@ void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( } os << target->GetFullName(config) << "\n"; } - -void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode( - std::ostream&, const std::string&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, const std::string&, - cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateMissingTargetsCheckCode( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) -{ - std::string config; - if (!this->Configurations.empty()) { - config = this->Configurations[0]; - } - cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - target, os, properties, cmExportBuildAndroidMKGenerator::INSTALL, config); -} - -void cmExportInstallAndroidMKGenerator::LoadConfigFiles(std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode( - std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&, - const std::set&, const std::string&) -{ -} - -bool cmExportInstallAndroidMKGenerator::GenerateImportFileConfig( - const std::string&) -{ - return true; -} diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index b1778ef58..1e1a5a81d 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -5,9 +5,10 @@ #include "cmConfigure.h" // IWYU pragma: keep #include -#include #include +#include +#include "cmExportAndroidMKGenerator.h" #include "cmExportInstallFileGenerator.h" #include "cmStateTypes.h" @@ -15,52 +16,51 @@ class cmGeneratorTarget; class cmInstallExportGenerator; /** \class cmExportInstallAndroidMKGenerator - * \brief Generate a file exporting targets from an install tree. + * \brief Generate files exporting targets from an install tree. * * cmExportInstallAndroidMKGenerator generates files exporting targets from - * install an installation tree. The files are placed in a temporary - * location for installation by cmInstallExportGenerator. The file format - * is for the ndk build system and is a makefile fragment specifying prebuilt - * libraries to the ndk build system. + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is for the ndk + * build system and is a makefile fragment specifying prebuilt libraries to the + * ndk build system. * * This is used to implement the INSTALL(EXPORT_ANDROID_MK) command. */ -class cmExportInstallAndroidMKGenerator : public cmExportInstallFileGenerator +class cmExportInstallAndroidMKGenerator + : public cmExportAndroidMKGenerator + , public cmExportInstallFileGenerator { public: /** Construct with the export installer that will install the files. */ cmExportInstallAndroidMKGenerator(cmInstallExportGenerator* iegen); + std::string GetConfigImportFileGlob() const override { return {}; } + protected: + GenerateType GetGenerateType() const override { return INSTALL; } + + char GetConfigFileNameSeparator() const override { return '-'; } + // Implement virtual methods from the superclass. - void GeneratePolicyHeaderCode(std::ostream&) override {} - void GeneratePolicyFooterCode(std::ostream&) override {} + void ReportDuplicateTarget(std::string const& targetName) const; + bool GenerateMainFile(std::ostream& os) override; void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = "") override; - void GenerateImportFooterCode(std::ostream& os) override; + std::string const& config = "") override; void GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) override; - void GenerateExpectedTargetsCode( - std::ostream& os, const std::string& expectedTargets) override; - void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation) override; - void GenerateMissingTargetsCheckCode(std::ostream& os) override; - void GenerateFindDependencyCalls(std::ostream&) override {} - void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) override; - void GenerateImportPrefix(std::ostream& os) override; - void LoadConfigFiles(std::ostream&) override; - void CleanupTemporaryVariables(std::ostream&) override; - void GenerateImportedFileCheckLoop(std::ostream& os) override; - void GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set& importedLocations, - const std::string& importedXcFrameworkLocation) override; - bool GenerateImportFileConfig(const std::string& config) override; + + void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, + cmGeneratorTarget const* dependee, + std::vector const& namespaces); + + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override + { + this->cmExportAndroidMKGenerator::GenerateImportTargetsConfig(os, config, + suffix); + } + + std::string GetCxxModulesDirectory() const override { return {}; } }; diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx new file mode 100644 index 000000000..e1b22854d --- /dev/null +++ b/Source/cmExportInstallCMakeConfigGenerator.cxx @@ -0,0 +1,477 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportInstallCMakeConfigGenerator.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmExportFileGenerator.h" +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmPolicies.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTargetExport.h" +#include "cmValue.h" + +cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator( + cmInstallExportGenerator* iegen) + : cmExportInstallFileGenerator(iegen) +{ +} + +std::string cmExportInstallCMakeConfigGenerator::GetConfigImportFileGlob() + const +{ + std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt); + return glob; +} + +bool cmExportInstallCMakeConfigGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector allTargets; + { + std::string expectedTargets; + std::string sep; + auto visitor = [&](cmTargetExport const* te) { + allTargets.push_back(te); + expectedTargets += sep + this->Namespace + te->Target->GetExportName(); + sep = " "; + }; + + if (!this->CollectExports(visitor)) { + return false; + } + + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + // Compute the relative import prefix for the file + this->GenerateImportPrefix(os); + + bool requiresConfigFiles = false; + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget* gt = te->Target; + cmStateEnums::TargetType targetType = this->GetExportTargetType(te); + + requiresConfigFiles = + requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY; + + this->GenerateImportTargetCode(os, gt, targetType); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + if (this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties) && + !this->ExportOld) { + this->SetRequiredCMakeVersion(2, 8, 12); + } + } + if (targetType == cmStateEnums::INTERFACE_LIBRARY) { + this->SetRequiredCMakeVersion(3, 0, 0); + } + if (gt->GetProperty("INTERFACE_SOURCES")) { + // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 + // can consume them. + this->SetRequiredCMakeVersion(3, 1, 0); + } + + this->GenerateInterfaceProperties(gt, os, properties); + + this->GenerateTargetFileSets(gt, os, te); + } + + this->LoadConfigFiles(os); + + bool result = true; + + std::string cxx_modules_name = this->GetExportSet()->GetName(); + this->GenerateCxxModuleInformation(cxx_modules_name, os); + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, + c)) { + result = false; + } + } + } + + this->CleanupTemporaryVariables(os); + this->GenerateImportedFileCheckLoop(os); + + // Generate an import file for each configuration. + // Don't do this if we only export INTERFACE_LIBRARY targets. + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportFileConfig(c)) { + result = false; + } + } + } + + this->GenerateMissingTargetsCheckCode(os); + + return result; +} + +void cmExportInstallCMakeConfigGenerator::GenerateImportPrefix( + std::ostream& os) +{ + // Set an _IMPORT_PREFIX variable for import location properties + // to reference if they are relative to the install prefix. + std::string installPrefix = + this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( + "CMAKE_INSTALL_PREFIX"); + std::string const& expDest = this->IEGen->GetDestination(); + if (cmSystemTools::FileIsFullPath(expDest)) { + // The export file is being installed to an absolute path so the + // package is not relocatable. Use the configured install prefix. + /* clang-format off */ + os << + "# The installation prefix configured by this project.\n" + "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n" + "\n"; + /* clang-format on */ + } else { + // Add code to compute the installation prefix relative to the + // import file location. + std::string absDest = installPrefix + "/" + expDest; + std::string absDestS = absDest + "/"; + os << "# Compute the installation prefix relative to this file.\n" + << "get_filename_component(_IMPORT_PREFIX" + << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"; + if (cmHasLiteralPrefix(absDestS, "/lib/") || + cmHasLiteralPrefix(absDestS, "/lib64/") || + cmHasLiteralPrefix(absDestS, "/libx32/") || + cmHasLiteralPrefix(absDestS, "/usr/lib/") || + cmHasLiteralPrefix(absDestS, "/usr/lib64/") || + cmHasLiteralPrefix(absDestS, "/usr/libx32/")) { + // Handle "/usr move" symlinks created by some Linux distros. + /* clang-format off */ + os << + "# Use original install prefix when loaded through a\n" + "# cross-prefix symbolic link such as /lib -> /usr/lib.\n" + "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n" + "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n" + "if(_realCurr STREQUAL _realOrig)\n" + " set(_IMPORT_PREFIX \"" << absDest << "\")\n" + "endif()\n" + "unset(_realOrig)\n" + "unset(_realCurr)\n"; + /* clang-format on */ + } + std::string dest = expDest; + while (!dest.empty()) { + os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" " + "PATH)\n"; + dest = cmSystemTools::GetFilenamePath(dest); + } + os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n" + << " set(_IMPORT_PREFIX \"\")\n" + << "endif()\n" + << "\n"; + } +} + +void cmExportInstallCMakeConfigGenerator::CleanupTemporaryVariables( + std::ostream& os) +{ + /* clang-format off */ + os << "# Cleanup temporary variables.\n" + << "set(_IMPORT_PREFIX)\n" + << "\n"; + /* clang-format on */ +} + +void cmExportInstallCMakeConfigGenerator::LoadConfigFiles(std::ostream& os) +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/" + << this->GetConfigImportFileGlob() << "\")\n" + << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n" + << " include(\"${_cmake_config_file}\")\n" + << "endforeach()\n" + << "unset(_cmake_config_file)\n" + << "unset(_cmake_config_files)\n" + << "\n"; + /* clang-format on */ +} + +void cmExportInstallCMakeConfigGenerator::GenerateImportConfig( + std::ostream& os, std::string const& config) +{ + // Start with the import file header. + this->GenerateImportHeaderCode(os, config); + + // Generate the per-config target information. + this->cmExportFileGenerator::GenerateImportConfig(os, config); + + // End with the import file footer. + this->GenerateImportFooterCode(os); +} + +void cmExportInstallCMakeConfigGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + // Add each target in the set to the export. + for (std::unique_ptr const& te : + this->GetExportSet()->GetTargetExports()) { + // Collect import properties for this target. + if (this->GetExportTargetType(te.get()) == + cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + ImportPropertyMap properties; + std::set importedLocations; + + this->PopulateImportProperties(config, suffix, te.get(), properties, + importedLocations); + + // If any file location was set for the target add it to the + // import file. + if (!properties.empty()) { + cmGeneratorTarget const* const gtgt = te->Target; + std::string const importedXcFrameworkLocation = + this->GetImportXcFrameworkLocation(config, te.get()); + + this->SetImportLinkInterface(config, suffix, + cmGeneratorExpression::InstallInterface, + gtgt, properties); + + this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties, + importedXcFrameworkLocation); + this->GenerateImportedFileChecksCode( + os, gtgt, properties, importedLocations, importedXcFrameworkLocation); + } + } +} + +namespace { +bool EntryIsContextSensitive( + std::unique_ptr const& cge) +{ + return cge->GetHadContextSensitiveCondition(); +} +} + +std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) +{ + std::vector resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); + auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + + for (auto const& config : configs) { + auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); + auto dest = cmOutputConverter::EscapeForCMake( + unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); + if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + } + + 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) { + 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("\"$<$:", dest, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', dest, '"')); + break; + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) +{ + std::vector resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + auto fileEntries = fileSet->CompileFileEntries(); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance()); + auto destCge = + destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + std::map> files; + for (auto const& entry : fileEntries) { + fileSet->EvaluateFileEntry(directories, files, entry, + gte->LocalGenerator, config, gte); + } + auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte); + auto dest = + cmStrCat(cmOutputConverter::EscapeForCMake( + unescapedDest, cmOutputConverter::WrapQuotes::NoWrap), + '/'); + if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + } + + bool const contextSensitive = destCge->GetHadContextSensitiveCondition() || + std::any_of(directoryEntries.begin(), directoryEntries.end(), + EntryIsContextSensitive) || + 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) { + 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) { + auto relFile = + cmStrCat(prefix, cmSystemTools::GetFilenameName(filename)); + auto escapedFile = + cmStrCat(dest, + cmOutputConverter::EscapeForCMake( + relFile, cmOutputConverter::WrapQuotes::NoWrap)); + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$:", escapedFile, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + } + } + } + + if (!(contextSensitive && configs.size() != 1)) { + break; + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportInstallCMakeConfigGenerator::GetCxxModulesDirectory() const +{ + return IEGen->GetCxxModuleDirectory(); +} + +void cmExportInstallCMakeConfigGenerator::GenerateCxxModuleConfigInformation( + std::string const& name, 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-" << name << "-*.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 cmExportInstallCMakeConfigGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, + 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-", name, '-', 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) { + // Only targets with C++ module sources will have a + // collator-generated install script. + if (!tgt->HaveCxx20ModuleSources()) { + continue; + } + + auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(), + '-', filename_config, ".cmake"); + prop_files.emplace_back(cmStrCat(dest, prop_filename)); + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + } + + return true; +} diff --git a/Source/cmExportInstallCMakeConfigGenerator.h b/Source/cmExportInstallCMakeConfigGenerator.h new file mode 100644 index 000000000..e56836b0b --- /dev/null +++ b/Source/cmExportInstallCMakeConfigGenerator.h @@ -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. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include +#include + +#include "cmExportCMakeConfigGenerator.h" +#include "cmExportInstallFileGenerator.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmInstallExportGenerator; +class cmTargetExport; + +/** \class cmExportInstallCMakeConfigGenerator + * \brief Generate files exporting targets from an install tree. + * + * cmExportInstallCMakeConfigGenerator generates files exporting targets from + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is CMake's native + * package configuration format. + * + * One main file is generated that creates the imported targets and loads + * per-configuration files. Target locations and settings for each + * configuration are written to these per-configuration files. After + * installation the main file loads the configurations that have been + * installed. + * + * This is used to implement the INSTALL(EXPORT) command. + */ +class cmExportInstallCMakeConfigGenerator + : public cmExportCMakeConfigGenerator + , public cmExportInstallFileGenerator +{ +public: + /** Construct with the export installer that will install the + files. */ + cmExportInstallCMakeConfigGenerator(cmInstallExportGenerator* iegen); + + /** Compute the globbing expression used to load per-config import + files from the main file. */ + std::string GetConfigImportFileGlob() const override; + +protected: + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + void GenerateImportConfig(std::ostream& os, + std::string const& config) override; + + char GetConfigFileNameSeparator() const override { return '-'; } + + /** Generate the relative import prefix. */ + virtual void GenerateImportPrefix(std::ostream&); + + /** Generate the relative import prefix. */ + virtual void LoadConfigFiles(std::ostream&); + + virtual void CleanupTemporaryVariables(std::ostream&); + + std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, + std::string const&); +}; diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index f5f22efa8..7b62bb4bc 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -3,29 +3,23 @@ #include "cmExportInstallFileGenerator.h" #include +#include +#include #include +#include #include #include -#include -#include - #include "cmExportSet.h" -#include "cmFileSet.h" #include "cmGeneratedFileStream.h" -#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmInstallExportGenerator.h" -#include "cmInstallFileSetGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -38,259 +32,76 @@ cmExportInstallFileGenerator::cmExportInstallFileGenerator( { } -std::string cmExportInstallFileGenerator::GetConfigImportFileGlob() +void cmExportInstallFileGenerator::ReplaceInstallPrefix( + std::string& input) const { - std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt); - return glob; + cmGeneratorExpression::ReplaceInstallPrefix(input, this->GetInstallPrefix()); } -bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) +void cmExportInstallFileGenerator::PopulateImportProperties( + std::string const& config, std::string const& suffix, + cmTargetExport const* targetExport, ImportPropertyMap& properties, + std::set& importedLocations) { - std::vector allTargets; - { - std::string expectedTargets; - std::string sep; - for (std::unique_ptr const& te : - this->IEGen->GetExportSet()->GetTargetExports()) { - if (te->NamelinkOnly) { - continue; - } - expectedTargets += sep + this->Namespace + te->Target->GetExportName(); - sep = " "; - if (this->ExportedTargets.insert(te->Target).second) { - allTargets.push_back(te.get()); - } else { - std::ostringstream e; - e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName() - << "\" ...) " - << "includes target \"" << te->Target->GetName() - << "\" more than once in the export set."; - cmSystemTools::Error(e.str()); - return false; - } - } - - this->GenerateExpectedTargetsCode(os, expectedTargets); + this->SetImportLocationProperty(config, suffix, + targetExport->ArchiveGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->LibraryGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->RuntimeGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->ObjectsGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->FrameworkGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->BundleGenerator, properties, + importedLocations); + + // If any file location was set for the target add it to the + // import file. + if (!properties.empty()) { + // Get the rest of the target details. + cmGeneratorTarget const* const gtgt = targetExport->Target; + this->SetImportDetailProperties(config, suffix, gtgt, properties); + + // TODO: PUBLIC_HEADER_LOCATION + // This should wait until the build feature propagation stuff is done. + // Then this can be a propagated include directory. + // this->GenerateImportProperty(config, te->HeaderGenerator, properties); } - - // Compute the relative import prefix for the file - this->GenerateImportPrefix(os); - - bool requiresConfigFiles = false; - // Create all the imported targets. - for (cmTargetExport* te : allTargets) { - cmGeneratorTarget* gt = te->Target; - cmStateEnums::TargetType targetType = this->GetExportTargetType(te); - - requiresConfigFiles = - requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY; - - this->GenerateImportTargetCode(os, gt, targetType); - - ImportPropertyMap properties; - - std::string includesDestinationDirs; - this->PopulateIncludeDirectoriesInterface( - gt, cmGeneratorExpression::InstallInterface, properties, *te, - includesDestinationDirs); - this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateLinkDirectoriesInterface( - gt, cmGeneratorExpression::InstallInterface, properties); - this->PopulateLinkDependsInterface( - gt, cmGeneratorExpression::InstallInterface, properties); - - std::string errorMessage; - if (!this->PopulateCxxModuleExportProperties( - gt, properties, cmGeneratorExpression::InstallInterface, - includesDestinationDirs, errorMessage)) { - cmSystemTools::Error(errorMessage); - return false; - } - - if (!this->PopulateExportProperties(gt, properties, errorMessage)) { - cmSystemTools::Error(errorMessage); - return false; - } - - const bool newCMP0022Behavior = - gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && - gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (newCMP0022Behavior) { - if (this->PopulateInterfaceLinkLibrariesProperty( - gt, cmGeneratorExpression::InstallInterface, properties) && - !this->ExportOld) { - this->SetRequiredCMakeVersion(2, 8, 12); - } - } - if (targetType == cmStateEnums::INTERFACE_LIBRARY) { - this->SetRequiredCMakeVersion(3, 0, 0); - } - if (gt->GetProperty("INTERFACE_SOURCES")) { - // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 - // can consume them. - this->SetRequiredCMakeVersion(3, 1, 0); - } - - this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt, - properties); - - this->PopulateCompatibleInterfaceProperties(gt, properties); - this->PopulateCustomTransitiveInterfaceProperties( - gt, cmGeneratorExpression::InstallInterface, properties); - - this->GenerateInterfaceProperties(gt, os, properties); - - this->GenerateTargetFileSets(gt, os, te); - } - - this->LoadConfigFiles(os); - - bool result = true; - - std::string cxx_modules_name = this->IEGen->GetExportSet()->GetName(); - this->GenerateCxxModuleInformation(cxx_modules_name, os); - if (requiresConfigFiles) { - for (std::string const& c : this->Configurations) { - if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, - c)) { - result = false; - } - } - } - - this->CleanupTemporaryVariables(os); - this->GenerateImportedFileCheckLoop(os); - - // Generate an import file for each configuration. - // Don't do this if we only export INTERFACE_LIBRARY targets. - if (requiresConfigFiles) { - for (std::string const& c : this->Configurations) { - if (!this->GenerateImportFileConfig(c)) { - result = false; - } - } - } - - this->GenerateMissingTargetsCheckCode(os); - - return result; } -void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os) +std::string cmExportInstallFileGenerator::GetImportXcFrameworkLocation( + std::string const& config, cmTargetExport const* targetExport) const { - // Set an _IMPORT_PREFIX variable for import location properties - // to reference if they are relative to the install prefix. - std::string installPrefix = - this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( - "CMAKE_INSTALL_PREFIX"); - std::string const& expDest = this->IEGen->GetDestination(); - if (cmSystemTools::FileIsFullPath(expDest)) { - // The export file is being installed to an absolute path so the - // package is not relocatable. Use the configured install prefix. - /* clang-format off */ - os << - "# The installation prefix configured by this project.\n" - "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n" - "\n"; - /* clang-format on */ - } else { - // Add code to compute the installation prefix relative to the - // import file location. - std::string absDest = installPrefix + "/" + expDest; - std::string absDestS = absDest + "/"; - os << "# Compute the installation prefix relative to this file.\n" - << "get_filename_component(_IMPORT_PREFIX" - << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"; - if (cmHasLiteralPrefix(absDestS, "/lib/") || - cmHasLiteralPrefix(absDestS, "/lib64/") || - cmHasLiteralPrefix(absDestS, "/libx32/") || - cmHasLiteralPrefix(absDestS, "/usr/lib/") || - cmHasLiteralPrefix(absDestS, "/usr/lib64/") || - cmHasLiteralPrefix(absDestS, "/usr/libx32/")) { - // Handle "/usr move" symlinks created by some Linux distros. - /* clang-format off */ - os << - "# Use original install prefix when loaded through a\n" - "# cross-prefix symbolic link such as /lib -> /usr/lib.\n" - "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n" - "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n" - "if(_realCurr STREQUAL _realOrig)\n" - " set(_IMPORT_PREFIX \"" << absDest << "\")\n" - "endif()\n" - "unset(_realOrig)\n" - "unset(_realCurr)\n"; - /* clang-format on */ - } - std::string dest = expDest; - while (!dest.empty()) { - os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" " - "PATH)\n"; - dest = cmSystemTools::GetFilenamePath(dest); + std::string importedXcFrameworkLocation = targetExport->XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::InstallInterface, + this->GetImportPrefixWithSlash()); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, targetExport->Target->GetLocalGenerator(), + config, targetExport->Target, nullptr, targetExport->Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) && + !cmHasPrefix(importedXcFrameworkLocation, + this->GetImportPrefixWithSlash())) { + return cmStrCat(this->GetImportPrefixWithSlash(), + importedXcFrameworkLocation); } - os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n" - << " set(_IMPORT_PREFIX \"\")\n" - << "endif()\n" - << "\n"; } -} -void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os) -{ - /* clang-format off */ - os << "# Cleanup temporary variables.\n" - << "set(_IMPORT_PREFIX)\n" - << "\n"; - /* clang-format on */ -} - -void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os) -{ - // Now load per-configuration properties for them. - /* clang-format off */ - os << "# Load information for each installed configuration.\n" - << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/" - << this->GetConfigImportFileGlob() << "\")\n" - << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n" - << " include(\"${_cmake_config_file}\")\n" - << "endforeach()\n" - << "unset(_cmake_config_file)\n" - << "unset(_cmake_config_files)\n" - << "\n"; - /* clang-format on */ -} - -void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input) -{ - cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}"); + return importedXcFrameworkLocation; } bool cmExportInstallFileGenerator::GenerateImportFileConfig( - const std::string& config) + std::string const& config) { // Skip configurations not enabled for this export. if (!this->IEGen->InstallsForConfig(config)) { @@ -298,7 +109,8 @@ bool cmExportInstallFileGenerator::GenerateImportFileConfig( } // Construct the name of the file to generate. - std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-'); + std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, + this->GetConfigFileNameSeparator()); if (!config.empty()) { fileName += cmSystemTools::LowerCase(config); } else { @@ -318,93 +130,17 @@ bool cmExportInstallFileGenerator::GenerateImportFileConfig( exportFileStream.SetCopyIfDifferent(true); std::ostream& os = exportFileStream; - // Start with the import file header. - this->GenerateImportHeaderCode(os, config); - // Generate the per-config target information. this->GenerateImportConfig(os, config); - // End with the import file footer. - this->GenerateImportFooterCode(os); - // Record this per-config import file. this->ConfigImportFiles[config] = fileName; return true; } -void cmExportInstallFileGenerator::GenerateImportTargetsConfig( - std::ostream& os, const std::string& config, std::string const& suffix) -{ - // Add each target in the set to the export. - for (std::unique_ptr const& te : - this->IEGen->GetExportSet()->GetTargetExports()) { - // Collect import properties for this target. - if (this->GetExportTargetType(te.get()) == - cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - - ImportPropertyMap properties; - std::set importedLocations; - - this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->LibraryGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->BundleGenerator, - properties, importedLocations); - - // If any file location was set for the target add it to the - // import file. - if (!properties.empty()) { - // Get the rest of the target details. - cmGeneratorTarget* gtgt = te->Target; - this->SetImportDetailProperties(config, suffix, gtgt, properties); - - this->SetImportLinkInterface(config, suffix, - cmGeneratorExpression::InstallInterface, - gtgt, properties); - - // TODO: PUBLIC_HEADER_LOCATION - // This should wait until the build feature propagation stuff - // is done. Then this can be a propagated include directory. - // this->GenerateImportProperty(config, te->HeaderGenerator, - // properties); - - // Generate code in the export file. - std::string importedXcFrameworkLocation = te->XcFrameworkLocation; - if (!importedXcFrameworkLocation.empty()) { - importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( - importedXcFrameworkLocation, - cmGeneratorExpression::PreprocessContext::InstallInterface, true); - importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( - importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config, - te->Target, nullptr, te->Target); - if (!importedXcFrameworkLocation.empty() && - !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) && - !cmHasLiteralPrefix(importedXcFrameworkLocation, - "${_IMPORT_PREFIX}/")) { - importedXcFrameworkLocation = - cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation); - } - } - this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties, - importedXcFrameworkLocation); - this->GenerateImportedFileChecksCode( - os, gtgt, properties, importedLocations, importedXcFrameworkLocation); - } - } -} - void cmExportInstallFileGenerator::SetImportLocationProperty( - const std::string& config, std::string const& suffix, + std::string const& config, std::string const& suffix, cmInstallTargetGenerator* itgen, ImportPropertyMap& properties, std::set& importedLocations) { @@ -421,7 +157,7 @@ void cmExportInstallFileGenerator::SetImportLocationProperty( std::string value; if (!cmSystemTools::FileIsFullPath(dest)) { // The target is installed relative to the installation prefix. - value = "${_IMPORT_PREFIX}/"; + value = std::string{ this->GetImportPrefixWithSlash() }; } value += dest; value += "/"; @@ -495,20 +231,24 @@ cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType( // An OBJECT library installed with no OBJECTS DESTINATION // is transformed to an INTERFACE library. if (targetType == cmStateEnums::OBJECT_LIBRARY && - targetExport->ObjectsGenerator == nullptr) { + !targetExport->ObjectsGenerator) { targetType = cmStateEnums::INTERFACE_LIBRARY; } return targetType; } +std::string const& cmExportInstallFileGenerator::GetExportName() const +{ + return this->GetExportSet()->GetName(); +} + void cmExportInstallFileGenerator::HandleMissingTarget( std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) { - const std::string name = dependee->GetName(); - cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator(); - auto exportInfo = this->FindNamespaces(gg, name); - std::vector const& exportFiles = exportInfo.first; + auto const& exportInfo = this->FindExportInfo(dependee); + auto const& exportFiles = exportInfo.first; + if (exportFiles.size() == 1) { std::string missingTarget = exportInfo.second; @@ -522,26 +262,24 @@ void cmExportInstallFileGenerator::HandleMissingTarget( } } -std::pair, std::string> -cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg, - const std::string& name) +cmExportFileGenerator::ExportInfo cmExportInstallFileGenerator::FindExportInfo( + cmGeneratorTarget const* target) const { std::vector exportFiles; std::string ns; - const cmExportSetMap& exportSets = gg->GetExportSets(); - for (auto const& expIt : exportSets) { - const cmExportSet& exportSet = expIt.second; + auto const& name = target->GetName(); + auto& exportSets = + target->GetLocalGenerator()->GetGlobalGenerator()->GetExportSets(); - bool containsTarget = false; - for (auto const& target : exportSet.GetTargetExports()) { - if (name == target->TargetName) { - containsTarget = true; - break; - } - } + for (auto const& exp : exportSets) { + auto const& exportSet = exp.second; + auto const& targets = exportSet.GetTargetExports(); - if (containsTarget) { + if (std::any_of(targets.begin(), targets.end(), + [&name](std::unique_ptr const& te) { + return te->TargetName == name; + })) { std::vector const* installs = exportSet.GetInstallations(); for (cmInstallExportGenerator const* install : *installs) { @@ -551,16 +289,16 @@ cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg, } } - return { exportFiles, ns }; + return { exportFiles, exportFiles.size() == 1 ? ns : std::string{} }; } void cmExportInstallFileGenerator::ComplainAboutMissingTarget( cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, - std::vector const& exportFiles) + std::vector const& exportFiles) const { std::ostringstream e; - e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName() - << "\" ...) " + e << "install(" << this->IEGen->InstallSubcommand() << " \"" + << this->GetExportName() << "\" ...) " << "includes target \"" << depender->GetName() << "\" which requires target \"" << dependee->GetName() << "\" "; if (exportFiles.empty()) { @@ -573,220 +311,371 @@ void cmExportInstallFileGenerator::ComplainAboutMissingTarget( "\"" << dependee->GetName() << "\" target to a single export."; } - cmSystemTools::Error(e.str()); + this->ReportError(e.str()); +} + +void cmExportInstallFileGenerator::ComplainAboutDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "install(" << this->IEGen->InstallSubcommand() << " \"" + << this->GetExportName() << "\" ...) " + << "includes target \"" << targetName + << "\" more than once in the export set."; + this->ReportError(e.str()); +} + +void cmExportInstallFileGenerator::ReportError( + std::string const& errorMessage) const +{ + cmSystemTools::Error(errorMessage); } std::string cmExportInstallFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; cmMakefile* mf = target->Target->GetMakefile(); if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { - install_name_dir = - target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}"); + auto const& prefix = this->GetInstallPrefix(); + install_name_dir = target->GetInstallNameDirForInstallTree(config, prefix); } return install_name_dir; } -namespace { -bool EntryIsContextSensitive( - const std::unique_ptr& cge) +std::string cmExportInstallFileGenerator::GetCxxModuleFile() const { - return cge->GetHadContextSensitiveCondition(); -} + return this->GetCxxModuleFile(this->GetExportSet()->GetName()); } -std::string cmExportInstallFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) +bool cmExportInstallFileGenerator::CollectExports( + std::function const& visitor) { - std::vector resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); - - for (auto const& config : configs) { - auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { - dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + auto pred = [&](std::unique_ptr const& te) -> bool { + if (te->NamelinkOnly) { + return true; } - - 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) { - 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 (this->ExportedTargets.insert(te->Target).second) { + visitor(te.get()); + return true; } - if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$:", dest, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', dest, '"')); - break; - } - } + this->ComplainAboutDuplicateTarget(te->Target->GetName()); + return false; + }; - return cmJoin(resultVector, " "); + auto const& targets = this->GetExportSet()->GetTargetExports(); + return std::all_of(targets.begin(), targets.end(), pred); } -std::string cmExportInstallFileGenerator::GetFileSetFiles( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) +bool cmExportInstallFileGenerator::PopulateInterfaceProperties( + cmTargetExport const* targetExport, ImportPropertyMap& properties) +{ + cmGeneratorTarget const* const gt = targetExport->Target; + + std::string includesDestinationDirs; + this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt, + cmGeneratorExpression::InstallInterface, + properties); + this->PopulateIncludeDirectoriesInterface( + gt, cmGeneratorExpression::InstallInterface, properties, *targetExport, + includesDestinationDirs); + this->PopulateLinkDirectoriesInterface( + gt, cmGeneratorExpression::InstallInterface, properties); + this->PopulateLinkDependsInterface( + gt, cmGeneratorExpression::InstallInterface, properties); + this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface, + properties); + + return this->PopulateInterfaceProperties( + gt, includesDestinationDirs, cmGeneratorExpression::InstallInterface, + properties); +} + +namespace { +bool isSubDirectory(std::string const& a, std::string const& b) { - std::vector resultVector; + return (cmSystemTools::ComparePath(a, b) || + cmSystemTools::IsSubDirectory(a, b)); +} +} - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); +bool cmExportInstallFileGenerator::CheckInterfaceDirs( + std::string const& prepro, cmGeneratorTarget const* target, + std::string const& prop) const +{ + std::string const& installDir = + target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); + std::string const& topSourceDir = + target->GetLocalGenerator()->GetSourceDirectory(); + std::string const& topBinaryDir = + target->GetLocalGenerator()->GetBinaryDirectory(); - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); + std::vector parts; + cmGeneratorExpression::Split(prepro, parts); - cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance()); - auto destCge = - destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + bool const inSourceBuild = topSourceDir == topBinaryDir; - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); + bool hadFatalError = false; - std::map> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, - gte->LocalGenerator, config, gte); + for (std::string const& li : parts) { + size_t genexPos = cmGeneratorExpression::Find(li); + if (genexPos == 0) { + continue; + } + if (cmHasPrefix(li, this->GetImportPrefixWithSlash())) { + continue; } - auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = - cmStrCat(cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap), - '/'); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { - dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + MessageType messageType = MessageType::FATAL_ERROR; + std::ostringstream e; + if (genexPos != std::string::npos) { + if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { + switch (target->GetPolicyStatusCMP0041()) { + case cmPolicies::WARN: + messageType = MessageType::WARNING; + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0041) << "\n"; + break; + case cmPolicies::OLD: + continue; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + hadFatalError = true; + break; // Issue fatal message. + } + } else { + hadFatalError = true; + } } - - bool const contextSensitive = destCge->GetHadContextSensitiveCondition() || - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - 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) { - 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 (!cmSystemTools::FileIsFullPath(li)) { + /* clang-format off */ + e << "Target \"" << target->GetName() << "\" " << prop << + " property contains relative path:\n" + " \"" << li << "\""; + /* clang-format on */ + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); } - - for (auto const& it : files) { - auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); - for (auto const& filename : it.second) { - auto relFile = - cmStrCat(prefix, cmSystemTools::GetFilenameName(filename)); - auto escapedFile = - cmStrCat(dest, - cmOutputConverter::EscapeForCMake( - relFile, cmOutputConverter::WrapQuotes::NoWrap)); - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$:", escapedFile, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + bool inBinary = isSubDirectory(li, topBinaryDir); + bool inSource = isSubDirectory(li, topSourceDir); + if (isSubDirectory(li, installDir)) { + // The include directory is inside the install tree. If the + // install tree is not inside the source tree or build tree then + // fall through to the checks below that the include directory is not + // also inside the source tree or build tree. + bool shouldContinue = + (!inBinary || isSubDirectory(installDir, topBinaryDir)) && + (!inSource || isSubDirectory(installDir, topSourceDir)); + + if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { + if (!shouldContinue) { + switch (target->GetPolicyStatusCMP0052()) { + case cmPolicies::WARN: { + std::ostringstream s; + s << cmPolicies::GetPolicyWarning(cmPolicies::CMP0052) << "\n"; + s << "Directory:\n \"" << li + << "\"\nin " + "INTERFACE_INCLUDE_DIRECTORIES of target \"" + << target->GetName() + << "\" is a subdirectory of the install " + "directory:\n \"" + << installDir + << "\"\nhowever it is also " + "a subdirectory of the " + << (inBinary ? "build" : "source") << " tree:\n \"" + << (inBinary ? topBinaryDir : topSourceDir) << "\"\n"; + target->GetLocalGenerator()->IssueMessage( + MessageType::AUTHOR_WARNING, s.str()); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + shouldContinue = true; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + break; + } } } + if (shouldContinue) { + continue; + } } - - if (!(contextSensitive && configs.size() != 1)) { - break; + if (inBinary) { + /* clang-format off */ + e << "Target \"" << target->GetName() << "\" " << prop << + " property contains path:\n" + " \"" << li << "\"\nwhich is prefixed in the build directory."; + /* clang-format on */ + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); + } + if (!inSourceBuild) { + if (inSource) { + e << "Target \"" << target->GetName() << "\" " << prop + << " property contains path:\n" + " \"" + << li << "\"\nwhich is prefixed in the source directory."; + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); + } } } - - return cmJoin(resultVector, " "); + return !hadFatalError; } -std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const +void cmExportInstallFileGenerator::PopulateSourcesInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) { - return IEGen->GetCxxModuleDirectory(); + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + char const* const propName = "INTERFACE_SOURCES"; + cmValue input = gt->GetProperty(propName); + + if (!input) { + return; + } + + if (input->empty()) { + properties[propName].clear(); + return; + } + + std::string prepro = cmGeneratorExpression::Preprocess( + *input, preprocessRule, this->GetImportPrefixWithSlash()); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); + + if (!this->CheckInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } } -void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( - std::string const& name, std::ostream& os) const +void cmExportInstallFileGenerator::PopulateIncludeDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, cmTargetExport const& te, + std::string& includesDestinationDirs) { - // 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-" << name << "-*.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 */ + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + includesDestinationDirs.clear(); + + char const* const propName = "INTERFACE_INCLUDE_DIRECTORIES"; + cmValue input = target->GetProperty(propName); + + cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance()); + + std::string dirs = cmGeneratorExpression::Preprocess( + cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)), + preprocessRule, this->GetImportPrefixWithSlash()); + this->ReplaceInstallPrefix(dirs); + std::unique_ptr cge = ge.Parse(dirs); + std::string exportDirs = + cge->Evaluate(target->GetLocalGenerator(), "", target); + + if (cge->GetHadContextSensitiveCondition()) { + cmLocalGenerator* lg = target->GetLocalGenerator(); + std::ostringstream e; + e << "Target \"" << target->GetName() + << "\" is installed with " + "INCLUDES DESTINATION set to a context sensitive path. Paths which " + "depend on the configuration, policy values or the link interface " + "are " + "not supported. Consider using target_include_directories instead."; + lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return; + } + + if (!input && exportDirs.empty()) { + return; + } + if ((input && input->empty()) && exportDirs.empty()) { + // Set to empty + properties[propName].clear(); + return; + } + + this->AddImportPrefix(exportDirs); + includesDestinationDirs = exportDirs; + + std::string includes = (input ? *input : ""); + char const* const sep = input ? ";" : ""; + includes += sep + exportDirs; + std::string prepro = cmGeneratorExpression::Preprocess( + includes, preprocessRule, this->GetImportPrefixWithSlash()); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + + if (!this->CheckInterfaceDirs(prepro, target, propName)) { + return; + } + properties[propName] = prepro; + } } -bool cmExportInstallFileGenerator:: - GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, - std::string const& config) +void cmExportInstallFileGenerator::PopulateLinkDependsInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) { - auto cxx_modules_dirname = this->GetCxxModulesDirectory(); - if (cxx_modules_dirname.empty()) { - return true; + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + char const* const propName = "INTERFACE_LINK_DEPENDS"; + cmValue input = gt->GetProperty(propName); + + if (!input) { + return; } - std::string filename_config = config; - if (filename_config.empty()) { - filename_config = "noconfig"; + if (input->empty()) { + properties[propName].clear(); + return; } - std::string const dest = - cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); - std::string fileName = - cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake"); + std::string prepro = cmGeneratorExpression::Preprocess( + *input, preprocessRule, this->GetImportPrefixWithSlash()); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); - 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; + if (!this->CheckInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; } - os.SetCopyIfDifferent(true); +} - // Record this per-config import file. - this->ConfigCxxModuleFiles[config] = fileName; +void cmExportInstallFileGenerator::PopulateLinkDirectoriesInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + assert(preprocessRule == cmGeneratorExpression::InstallInterface); - auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; - for (auto const* tgt : this->ExportedTargets) { - // Only targets with C++ module sources will have a - // collator-generated install script. - if (!tgt->HaveCxx20ModuleSources()) { - continue; - } + char const* const propName = "INTERFACE_LINK_DIRECTORIES"; + cmValue input = gt->GetProperty(propName); - auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(), - '-', filename_config, ".cmake"); - prop_files.emplace_back(cmStrCat(dest, prop_filename)); - os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + if (!input) { + return; } - return true; + if (input->empty()) { + properties[propName].clear(); + return; + } + + std::string prepro = cmGeneratorExpression::Preprocess( + *input, preprocessRule, this->GetImportPrefixWithSlash()); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); + + if (!this->CheckInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } } diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 7a7258410..f33ecc112 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -4,39 +4,31 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include +#include #include #include #include -#include #include +#include + #include "cmExportFileGenerator.h" +#include "cmGeneratorExpression.h" #include "cmInstallExportGenerator.h" #include "cmStateTypes.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; -class cmGlobalGenerator; class cmInstallTargetGenerator; class cmTargetExport; /** \class cmExportInstallFileGenerator * \brief Generate a file exporting targets from an install tree. * - * cmExportInstallFileGenerator generates files exporting targets from - * install an installation tree. The files are placed in a temporary - * location for installation by cmInstallExportGenerator. One main - * file is generated that creates the imported targets and loads - * per-configuration files. Target locations and settings for each - * configuration are written to these per-configuration files. After - * installation the main file loads the configurations that have been - * installed. - * - * This is used to implement the INSTALL(EXPORT) command. + * cmExportInstallFileGenerator is the generic interface class for generating + * export files for an install tree. */ -class cmExportInstallFileGenerator : public cmExportFileGenerator +class cmExportInstallFileGenerator : virtual public cmExportFileGenerator { public: /** Construct with the export installer that will install the @@ -51,6 +43,9 @@ public: return this->ConfigImportFiles; } + /** Get the temporary location of the config-agnostic C++ module file. */ + std::string GetCxxModuleFile() const; + /** Get the per-config C++ module file generated for each configuration. This maps from the configuration name to the file temporary location for installation. */ @@ -70,65 +65,76 @@ public: /** Compute the globbing expression used to load per-config import files from the main file. */ - std::string GetConfigImportFileGlob(); + virtual std::string GetConfigImportFileGlob() const = 0; protected: - // Implement virtual methods from the superclass. - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream& os, const std::string& config, - std::string const& suffix) override; cmStateEnums::TargetType GetExportTargetType( cmTargetExport const* targetExport) const; + + virtual std::string const& GetExportName() const; + + std::string GetInstallPrefix() const + { + cm::string_view const& prefixWithSlash = this->GetImportPrefixWithSlash(); + return std::string(prefixWithSlash.data(), prefixWithSlash.length() - 1); + } + virtual char GetConfigFileNameSeparator() const = 0; + void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) override; - void ReplaceInstallPrefix(std::string& input) override; - - void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - std::vector const& exportFiles); + void ReplaceInstallPrefix(std::string& input) const override; - std::pair, std::string> FindNamespaces( - cmGlobalGenerator* gg, const std::string& name); + void ComplainAboutMissingTarget( + cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, + std::vector const& exportFiles) const; - /** Generate the relative import prefix. */ - virtual void GenerateImportPrefix(std::ostream&); + void ComplainAboutDuplicateTarget( + std::string const& targetName) const override; - /** Generate the relative import prefix. */ - virtual void LoadConfigFiles(std::ostream&); + ExportInfo FindExportInfo(cmGeneratorTarget const* target) const override; - virtual void CleanupTemporaryVariables(std::ostream&); + void ReportError(std::string const& errorMessage) const override; /** Generate a per-configuration file for the targets. */ - virtual bool GenerateImportFileConfig(const std::string& config); + virtual bool GenerateImportFileConfig(std::string const& config); /** Fill in properties indicating installed file locations. */ - void SetImportLocationProperty(const std::string& config, + void SetImportLocationProperty(std::string const& config, std::string const& suffix, cmInstallTargetGenerator* itgen, ImportPropertyMap& properties, std::set& importedLocations); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; + using cmExportFileGenerator::GetCxxModuleFile; - std::string GetCxxModulesDirectory() const override; - void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream&) const override; - bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, - std::string const&); + /** Walk the list of targets to be exported. Returns true iff no duplicates + are found. */ + bool CollectExports( + std::function const& visitor); cmExportSet* GetExportSet() const override { return this->IEGen->GetExportSet(); } + std::string GetImportXcFrameworkLocation( + std::string const& config, cmTargetExport const* targetExport) const; + + using cmExportFileGenerator::PopulateInterfaceProperties; + bool PopulateInterfaceProperties(cmTargetExport const* targetExport, + ImportPropertyMap& properties); + + void PopulateImportProperties(std::string const& config, + std::string const& suffix, + cmTargetExport const* targetExport, + ImportPropertyMap& properties, + std::set& importedLocations); + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. @@ -137,4 +143,32 @@ protected: std::map ConfigCxxModuleFiles; // The C++ module property target files generated for each configuration. std::map> ConfigCxxModuleTargetFiles; + +private: + bool CheckInterfaceDirs(std::string const& prepro, + cmGeneratorTarget const* target, + std::string const& prop) const; + void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, + ImportPropertyMap& properties); + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateIncludeDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, cmTargetExport const& te, + std::string& includesDestinationDirs); + void PopulateSourcesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateLinkDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateLinkDependsInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); }; diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx new file mode 100644 index 000000000..b9b715edb --- /dev/null +++ b/Source/cmExportInstallPackageInfoGenerator.cxx @@ -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 "cmExportInstallPackageInfoGenerator.h" + +#include +#include +#include +#include + +#include + +#include "cmExportSet.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmInstallExportGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetExport.h" + +cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator( + cmInstallExportGenerator* iegen, std::string packageName, + std::string version, std::string versionCompat, std::string versionSchema, + std::vector defaultTargets, + std::vector defaultConfigurations) + : cmExportPackageInfoGenerator( + std::move(packageName), std::move(version), std::move(versionCompat), + std::move(versionSchema), std::move(defaultTargets), + std::move(defaultConfigurations)) + , cmExportInstallFileGenerator(iegen) +{ +} + +std::string cmExportInstallPackageInfoGenerator::GetConfigImportFileGlob() + const +{ + std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt); + return glob; +} + +std::string const& cmExportInstallPackageInfoGenerator::GetExportName() const +{ + return this->GetPackageName(); +} + +bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector allTargets; + { + auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; + + if (!this->CollectExports(visitor)) { + return false; + } + } + + if (!this->CheckDefaultTargets()) { + return false; + } + + Json::Value root = this->GeneratePackageInfo(); + Json::Value& components = root["components"]; + + // Compute the relative import prefix for the file + std::string const& packagePath = this->GenerateImportPrefix(); + if (packagePath.empty()) { + return false; + } + root["cps_path"] = packagePath; + + bool requiresConfigFiles = false; + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget* gt = te->Target; + cmStateEnums::TargetType targetType = this->GetExportTargetType(te); + + Json::Value* const component = + this->GenerateImportTarget(components, gt, targetType); + if (!component) { + return false; + } + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties); + + if (targetType != cmStateEnums::INTERFACE_LIBRARY) { + requiresConfigFiles = true; + } + + // Set configuration-agnostic properties for component. + this->GenerateInterfaceProperties(*component, gt, properties); + } + + this->GeneratePackageRequires(root); + + // Write the primary packing information file. + this->WritePackageInfo(root, os); + + bool result = true; + + // Generate an import file for each configuration. + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportFileConfig(c)) { + result = false; + } + } + } + + return result; +} + +void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + Json::Value root; + root["name"] = this->GetPackageName(); + root["configuration"] = config; + + Json::Value& components = root["components"]; + + for (auto const& te : this->GetExportSet()->GetTargetExports()) { + // Collect import properties for this target. + if (this->GetExportTargetType(te.get()) == + cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + ImportPropertyMap properties; + std::set importedLocations; + + this->PopulateImportProperties(config, suffix, te.get(), properties, + importedLocations); + + this->GenerateInterfaceConfigProperties(components, te->Target, suffix, + properties); + } + + this->WritePackageInfo(root, os); +} + +std::string cmExportInstallPackageInfoGenerator::GenerateImportPrefix() const +{ + std::string expDest = this->IEGen->GetDestination(); + if (cmSystemTools::FileIsFullPath(expDest)) { + std::string const& installPrefix = + this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( + "CMAKE_INSTALL_PREFIX"); + if (cmHasPrefix(expDest, installPrefix)) { + auto n = installPrefix.length(); + while (n < expDest.length() && expDest[n] == '/') { + ++n; + } + expDest = expDest.substr(n); + } else { + this->ReportError( + cmStrCat("install(PACKAGE_INFO \"", this->GetExportName(), + "\" ...) specifies DESTINATION \"", expDest, + "\" which is not a subdirectory of the install prefix.")); + return {}; + } + } + + if (expDest.empty()) { + return this->GetInstallPrefix(); + } + return cmStrCat(this->GetImportPrefixWithSlash(), expDest); +} + +std::string cmExportInstallPackageInfoGenerator::InstallNameDir( + cmGeneratorTarget const* target, std::string const& config) +{ + std::string install_name_dir; + + cmMakefile* mf = target->Target->GetMakefile(); + if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { + install_name_dir = + target->GetInstallNameDirForInstallTree(config, "@prefix@"); + } + + return install_name_dir; +} + +std::string cmExportInstallPackageInfoGenerator::GetCxxModulesDirectory() const +{ + // TODO: Implement a not-CMake-specific mechanism for providing module + // information. + // return IEGen->GetCxxModuleDirectory(); + return {}; +} diff --git a/Source/cmExportInstallPackageInfoGenerator.h b/Source/cmExportInstallPackageInfoGenerator.h new file mode 100644 index 000000000..5861b050d --- /dev/null +++ b/Source/cmExportInstallPackageInfoGenerator.h @@ -0,0 +1,66 @@ +/* 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 +#include +#include + +#include "cmExportInstallFileGenerator.h" +#include "cmExportPackageInfoGenerator.h" + +class cmGeneratorTarget; +class cmInstallExportGenerator; + +/** \class cmExportInstallPackageInfoGenerator + * \brief Generate files exporting targets from an install tree. + * + * cmExportInstallPackageInfoGenerator generates files exporting targets from + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is the Common + * Package Specification (https://cps-org.github.io/cps/). + * + * One main file is generated that describes the imported targets. Additional, + * per-configuration files describe target locations and settings for each + * configuration. + * + * This is used to implement the INSTALL(PACKAGE_INFO) command. + */ +class cmExportInstallPackageInfoGenerator + : public cmExportPackageInfoGenerator + , public cmExportInstallFileGenerator +{ +public: + /** Construct with the export installer that will install the + files. */ + cmExportInstallPackageInfoGenerator( + cmInstallExportGenerator* iegen, std::string packageName, + std::string version, std::string versionCompat, std::string versionSchema, + std::vector defaultTargets, + std::vector defaultConfigurations); + + /** Compute the globbing expression used to load per-config import + files from the main file. */ + std::string GetConfigImportFileGlob() const override; + +protected: + std::string const& GetExportName() const override; + + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + + char GetConfigFileNameSeparator() const override { return '@'; } + + /** Generate the cps_path, which determines the import prefix. */ + std::string GenerateImportPrefix() const; + + std::string InstallNameDir(cmGeneratorTarget const* target, + std::string const& config) override; + + std::string GetCxxModulesDirectory() const override; + // TODO: Generate C++ module info in a not-CMake-specific format. +}; diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx new file mode 100644 index 000000000..3f6628b21 --- /dev/null +++ b/Source/cmExportPackageInfoGenerator.cxx @@ -0,0 +1,452 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportPackageInfoGenerator.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "cmExportSet.h" +#include "cmFindPackageStack.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmValue.h" + +static const std::string kCPS_VERSION_STR = "0.12.0"; + +cmExportPackageInfoGenerator::cmExportPackageInfoGenerator( + std::string packageName, std::string version, std::string versionCompat, + std::string versionSchema, std::vector defaultTargets, + std::vector defaultConfigurations) + : PackageName(std::move(packageName)) + , PackageVersion(std::move(version)) + , PackageVersionCompat(std::move(versionCompat)) + , PackageVersionSchema(std::move(versionSchema)) + , DefaultTargets(std::move(defaultTargets)) + , DefaultConfigurations(std::move(defaultConfigurations)) +{ +} + +cm::string_view cmExportPackageInfoGenerator::GetImportPrefixWithSlash() const +{ + return "@prefix@/"_s; +} + +bool cmExportPackageInfoGenerator::GenerateImportFile(std::ostream& os) +{ + return this->GenerateMainFile(os); +} + +void cmExportPackageInfoGenerator::WritePackageInfo( + Json::Value const& packageInfo, std::ostream& os) const +{ + Json::StreamWriterBuilder builder; + builder["indentation"] = " "; + builder["commentStyle"] = "None"; + std::unique_ptr const writer(builder.newStreamWriter()); + writer->write(packageInfo, &os); +} + +namespace { +template +void buildArray(Json::Value& object, std::string const& property, + T const& values) +{ + if (!values.empty()) { + Json::Value& array = object[property]; + for (auto const& item : values) { + array.append(item); + } + } +} +} + +bool cmExportPackageInfoGenerator::CheckDefaultTargets() const +{ + bool result = true; + std::set exportedTargetNames; + for (auto const* te : this->ExportedTargets) { + exportedTargetNames.emplace(te->GetExportName()); + } + + for (auto const& name : this->DefaultTargets) { + if (!cm::contains(exportedTargetNames, name)) { + this->ReportError( + cmStrCat("Package \"", this->GetPackageName(), + "\" specifies DEFAULT_TARGETS \"", name, + "\", which is not a target in the export set \"", + this->GetExportSet()->GetName(), "\".")); + result = false; + } + } + + return result; +} + +Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const +{ + Json::Value package; + + package["name"] = this->GetPackageName(); + package["cps_version"] = std::string(kCPS_VERSION_STR); + + if (!this->PackageVersion.empty()) { + package["version"] = this->PackageVersion; + if (!this->PackageVersionCompat.empty()) { + package["compat_version"] = this->PackageVersionCompat; + } + if (!this->PackageVersionSchema.empty()) { + package["version_schema"] = this->PackageVersionSchema; + } + } + + buildArray(package, "default_components", this->DefaultTargets); + buildArray(package, "configurations", this->DefaultConfigurations); + + // TODO: description, website, license + + return package; +} + +void cmExportPackageInfoGenerator::GeneratePackageRequires( + Json::Value& package) const +{ + if (!this->Requirements.empty()) { + Json::Value& requirements = package["requires"]; + for (auto const& requirement : this->Requirements) { + // TODO: version, hint + requirements[requirement] = Json::Value{}; + } + } +} + +Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget( + Json::Value& components, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) const +{ + auto const& name = target->GetExportName(); + if (name.empty()) { + return nullptr; + } + + Json::Value& component = components[name]; + Json::Value& type = component["type"]; + switch (targetType) { + case cmStateEnums::EXECUTABLE: + type = "executable"; + break; + case cmStateEnums::STATIC_LIBRARY: + type = "archive"; + break; + case cmStateEnums::SHARED_LIBRARY: + type = "dylib"; + break; + case cmStateEnums::MODULE_LIBRARY: + type = "module"; + break; + case cmStateEnums::INTERFACE_LIBRARY: + type = "interface"; + break; + default: + type = "unknown"; + break; + } + return &component; +} + +bool cmExportPackageInfoGenerator::GenerateInterfaceProperties( + Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + bool result = true; + + this->GenerateInterfaceLinkProperties(result, component, target, properties); + + this->GenerateInterfaceCompileFeatures(result, component, target, + properties); + this->GenerateInterfaceCompileDefines(result, component, target, properties); + + this->GenerateInterfaceListProperty(result, component, target, + "compile_flags", "COMPILE_OPTIONS"_s, + properties); + this->GenerateInterfaceListProperty(result, component, target, "link_flags", + "LINK_OPTIONS"_s, properties); + this->GenerateInterfaceListProperty(result, component, target, + "link_directories", "LINK_DIRECTORIES"_s, + properties); + this->GenerateInterfaceListProperty(result, component, target, "includes", + "INCLUDE_DIRECTORIES"_s, properties); + + // TODO: description, license + + return result; +} + +namespace { +bool forbidGeneratorExpressions(std::string const& propertyName, + std::string const& propertyValue, + cmGeneratorTarget const* target) +{ + std::string const& evaluatedValue = cmGeneratorExpression::Preprocess( + propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions); + if (evaluatedValue != propertyValue) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property \"", propertyName, "\" of target \"", + target->GetName(), + "\" contains a generator expression. This is not allowed.")); + return false; + } + return true; +} +} + +bool cmExportPackageInfoGenerator::NoteLinkedTarget( + cmGeneratorTarget const* target, std::string const& linkedName, + cmGeneratorTarget const* linkedTarget) +{ + if (cm::contains(this->ExportedTargets, linkedTarget)) { + // Target is internal to this package. + this->LinkTargets.emplace(linkedName, + cmStrCat(':', linkedTarget->GetExportName())); + return true; + } + + if (linkedTarget->IsImported()) { + // Target is imported from a found package. + auto pkgName = [linkedTarget]() -> std::string { + auto const& pkgStack = linkedTarget->Target->GetFindPackageStack(); + if (!pkgStack.Empty()) { + return pkgStack.Top().Name; + } + + return linkedTarget->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME"); + }(); + + if (pkgName.empty()) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), + "\" references imported target \"", linkedName, + "\" which does not come from any known package.")); + return false; + } + + auto const& prefix = cmStrCat(pkgName, "::"); + if (!cmHasPrefix(linkedName, prefix)) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, "\", which comes from the \"", pkgName, + "\" package, but does not belong to the package's " + "canonical namespace. This is not allowed.")); + return false; + } + + // TODO: Record package version, hint. + this->Requirements.emplace(pkgName); + this->LinkTargets.emplace( + linkedName, cmStrCat(pkgName, ':', linkedName.substr(prefix.length()))); + return true; + } + + // Target belongs to another export from this build. + auto const& exportInfo = this->FindExportInfo(linkedTarget); + if (exportInfo.first.size() == 1) { + auto const& linkNamespace = exportInfo.second; + if (!cmHasSuffix(linkNamespace, "::")) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", target->GetName(), "\" references target \"", + linkedName, + "\", which does not use the standard namespace separator. " + "This is not allowed.")); + return false; + } + + auto pkgName = + cm::string_view{ linkNamespace.data(), linkNamespace.size() - 2 }; + + if (pkgName == this->GetPackageName()) { + this->LinkTargets.emplace(linkedName, + cmStrCat(':', linkedTarget->GetExportName())); + } else { + this->Requirements.emplace(pkgName); + this->LinkTargets.emplace( + linkedName, cmStrCat(pkgName, ':', linkedTarget->GetExportName())); + } + return true; + } + + // cmExportFileGenerator::HandleMissingTarget should have complained about + // this already. (In fact, we probably shouldn't ever get here.) + return false; +} + +void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES"); + if (iter == properties.end()) { + return; + } + + // TODO: Support $. + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + std::vector buildRequires; + // std::vector linkRequires; TODO + std::vector linkLibraries; + + for (auto const& name : cmList{ iter->second }) { + auto const& ti = this->LinkTargets.find(name); + if (ti != this->LinkTargets.end()) { + if (ti->second.empty()) { + result = false; + } else { + buildRequires.emplace_back(ti->second); + } + } else { + linkLibraries.emplace_back(name); + } + } + + buildArray(component, "requires", buildRequires); + // buildArray(component, "link_requires", linkRequires); TODO + buildArray(component, "link_libraries", linkLibraries); +} + +void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES"); + if (iter == properties.end()) { + return; + } + + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + std::set features; + for (auto const& value : cmList{ iter->second }) { + if (cmHasLiteralPrefix(value, "c_std_")) { + auto suffix = cm::string_view{ value }.substr(6, 2); + features.emplace(cmStrCat("cxx", suffix)); + } else if (cmHasLiteralPrefix(value, "cxx_std_")) { + auto suffix = cm::string_view{ value }.substr(8, 2); + features.emplace(cmStrCat("c++", suffix)); + } + } + + buildArray(component, "compile_features", features); +} + +void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const +{ + auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS"); + if (iter == properties.end()) { + return; + } + + // TODO: Support language-specific defines. + if (!forbidGeneratorExpressions(iter->first, iter->second, target)) { + result = false; + return; + } + + Json::Value defines; + for (auto const& def : cmList{ iter->second }) { + auto const n = def.find('='); + if (n == std::string::npos) { + defines[def] = Json::Value{}; + } else { + defines[def.substr(0, n)] = def.substr(n + 1); + } + } + + if (!defines.empty()) { + component["compile_definitions"]["*"] = std::move(defines); + } +} + +void cmExportPackageInfoGenerator::GenerateInterfaceListProperty( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + std::string const& outName, cm::string_view inName, + ImportPropertyMap const& properties) const +{ + auto const& prop = cmStrCat("INTERFACE_", inName); + auto const& iter = properties.find(prop); + if (iter == properties.end()) { + return; + } + + if (!forbidGeneratorExpressions(prop, iter->second, target)) { + result = false; + return; + } + + Json::Value& array = component[outName]; + for (auto const& value : cmList{ iter->second }) { + array.append(value); + } +} + +void cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties( + Json::Value& components, cmGeneratorTarget const* target, + std::string const& suffix, ImportPropertyMap const& properties) const +{ + Json::Value component; + auto const suffixLength = suffix.length(); + + for (auto const& p : properties) { + if (!cmHasSuffix(p.first, suffix)) { + continue; + } + auto const n = p.first.length() - suffixLength - 9; + auto const prop = cm::string_view{ p.first }.substr(9, n); + + if (prop == "LOCATION") { + component["location"] = p.second; + } else if (prop == "IMPLIB") { + component["link_location"] = p.second; + } else if (prop == "LINK_INTERFACE_LANGUAGES") { + std::vector languages; + for (auto const& lang : cmList{ p.second }) { + auto ll = cmSystemTools::LowerCase(lang); + if (ll == "cxx") { + languages.emplace_back("cpp"); + } else { + languages.emplace_back(std::move(ll)); + } + } + buildArray(component, "link_languages", languages); + } + } + + if (!component.empty()) { + components[target->GetExportName()] = component; + } +} diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h new file mode 100644 index 000000000..6fe1e359d --- /dev/null +++ b/Source/cmExportPackageInfoGenerator.h @@ -0,0 +1,114 @@ +/* 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 +#include +#include +#include +#include + +#include + +#include "cmExportFileGenerator.h" +#include "cmStateTypes.h" + +class cmGeneratorTarget; +namespace Json { +class Value; +} + +/** \class cmExportPackageInfoGenerator + * \brief Generate Common Package Specification package information files + * exporting targets from a build or install tree. + * + * cmExportPackageInfoGenerator is the superclass for + * cmExportBuildPackageInfoGenerator and cmExportInstallPackageInfoGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +class cmExportPackageInfoGenerator : virtual public cmExportFileGenerator +{ +public: + cmExportPackageInfoGenerator(std::string packageName, std::string version, + std::string versionCompat, + std::string versionSchema, + std::vector defaultTargets, + std::vector defaultConfigurations); + + using cmExportFileGenerator::GenerateImportFile; + +protected: + std::string const& GetPackageName() const { return this->PackageName; } + + void WritePackageInfo(Json::Value const& packageInfo, + std::ostream& os) const; + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + + bool CheckDefaultTargets() const; + + Json::Value GeneratePackageInfo() const; + Json::Value* GenerateImportTarget(Json::Value& components, + cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) const; + + void GeneratePackageRequires(Json::Value& package) const; + + using ImportPropertyMap = std::map; + bool GenerateInterfaceProperties(Json::Value& component, + cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + void GenerateInterfaceConfigProperties( + Json::Value& components, cmGeneratorTarget const* target, + std::string const& suffix, ImportPropertyMap const& properties) const; + + cm::string_view GetImportPrefixWithSlash() const override; + + std::string GetCxxModuleFile(std::string const& /*name*/) const override + { + // TODO + return {}; + } + + void GenerateCxxModuleConfigInformation(std::string const& /*name*/, + std::ostream& /*os*/) const override + { + // TODO + } + + bool NoteLinkedTarget(cmGeneratorTarget const* target, + std::string const& linkedName, + cmGeneratorTarget const* linkedTarget) override; + +private: + void GenerateInterfaceLinkProperties( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceCompileFeatures( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceCompileDefines( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + ImportPropertyMap const& properties) const; + + void GenerateInterfaceListProperty( + bool& result, Json::Value& component, cmGeneratorTarget const* target, + std::string const& outName, cm::string_view inName, + ImportPropertyMap const& properties) const; + + std::string const PackageName; + std::string const PackageVersion; + std::string const PackageVersionCompat; + std::string const PackageVersionSchema; + std::vector DefaultTargets; + std::vector DefaultConfigurations; + + std::map LinkTargets; + std::set Requirements; +}; diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 7ce5cd99c..021e07115 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -19,19 +19,26 @@ #include "cmOutputConverter.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.h" class cmTargetExport; cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator( - cmGlobalGenerator* gg, const std::vector& targets, + cmGlobalGenerator* gg, std::vector const& targets, cmMakefile* mf, std::set const& langs) : Languages(langs.begin(), langs.end()) { gg->CreateImportedGenerationObjects(mf, targets, this->Exports); } +void cmExportTryCompileFileGenerator::ReportError( + std::string const& errorMessage) const +{ + cmSystemTools::Error(errorMessage); +} + bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) { std::set emitted; @@ -61,7 +68,7 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) } std::string cmExportTryCompileFileGenerator::FindTargets( - const std::string& propName, cmGeneratorTarget const* tgt, + std::string const& propName, cmGeneratorTarget const* tgt, std::string const& language, std::set& emitted) { cmValue prop = tgt->GetProperty(propName); @@ -94,7 +101,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets( std::string result = cge->Evaluate(tgt->GetLocalGenerator(), this->Config, &gDummyHead, &dagChecker, tgt, language); - const std::set& allTargets = + std::set const& allTargets = cge->GetAllTargetsSeen(); for (cmGeneratorTarget const* target : allTargets) { if (emitted.insert(target).second) { @@ -105,7 +112,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets( } void cmExportTryCompileFileGenerator::PopulateProperties( - const cmGeneratorTarget* target, ImportPropertyMap& properties, + cmGeneratorTarget const* target, ImportPropertyMap& properties, std::set& emitted) { // Look through all non-special properties. @@ -140,7 +147,7 @@ void cmExportTryCompileFileGenerator::PopulateProperties( } std::string cmExportTryCompileFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; @@ -153,14 +160,14 @@ std::string cmExportTryCompileFileGenerator::InstallNameDir( } std::string cmExportTryCompileFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/) + cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetDirectoryEntries())); } std::string cmExportTryCompileFileGenerator::GetFileSetFiles( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/) + cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetFileEntries())); diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 4c7d287c1..58d1f5ee4 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -9,7 +9,7 @@ #include #include -#include "cmExportFileGenerator.h" +#include "cmExportCMakeConfigGenerator.h" class cmFileSet; class cmGeneratorTarget; @@ -17,7 +17,7 @@ class cmGlobalGenerator; class cmMakefile; class cmTargetExport; -class cmExportTryCompileFileGenerator : public cmExportFileGenerator +class cmExportTryCompileFileGenerator : public cmExportCMakeConfigGenerator { public: cmExportTryCompileFileGenerator(cmGlobalGenerator* gg, @@ -26,13 +26,17 @@ public: std::set const& langs); /** Set the list of targets to export. */ - void SetConfig(const std::string& config) { this->Config = config; } + void SetConfig(std::string const& config) { this->Config = config; } protected: // Implement virtual methods from the superclass. + void ComplainAboutDuplicateTarget( + std::string const& /*targetName*/) const override{}; + void ReportError(std::string const& errorMessage) const override; + bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream&, const std::string&, + void GenerateImportTargetsConfig(std::ostream&, std::string const&, std::string const&) override { } @@ -41,19 +45,24 @@ protected: { } + ExportInfo FindExportInfo(cmGeneratorTarget const* /*target*/) const override + { + return { {}, {} }; + } + void PopulateProperties(cmGeneratorTarget const* target, ImportPropertyMap& properties, - std::set& emitted); + std::set& emitted); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; std::string GetFileSetDirectories(cmGeneratorTarget* target, cmFileSet* fileSet, - cmTargetExport* te) override; + cmTargetExport const* te) override; std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, - cmTargetExport* te) override; + cmTargetExport const* te) override; std::string GetCxxModulesDirectory() const override { return {}; } void GenerateCxxModuleConfigInformation(std::string const&, @@ -62,10 +71,10 @@ protected: } private: - std::string FindTargets(const std::string& prop, - const cmGeneratorTarget* tgt, + std::string FindTargets(std::string const& prop, + cmGeneratorTarget const* tgt, std::string const& language, - std::set& emitted); + std::set& emitted); std::vector Exports; std::string Config; diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index 8d7f33e7e..bc5844788 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -494,7 +494,7 @@ void cmExtraCodeBlocksGenerator::AppendTarget( xml.StartElement("Target"); xml.Attribute("title", targetName); - if (target != nullptr) { + if (target) { int cbTargetType = this->GetCBTargetType(target); std::string workingDir = lg->GetCurrentBinaryDirectory(); if (target->GetType() == cmStateEnums::EXECUTABLE) { diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx index 205a691f2..37c4a7cac 100644 --- a/Source/cmExtraSublimeTextGenerator.cxx +++ b/Source/cmExtraSublimeTextGenerator.cxx @@ -242,7 +242,7 @@ void cmExtraSublimeTextGenerator::AppendTarget( MapSourceFileFlags& sourceFileFlags, bool firstTarget) { - if (target != nullptr) { + if (target) { std::vector sourceFiles; target->GetSourceFiles(sourceFiles, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index d4a717586..1f1561221 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -7,9 +7,13 @@ #include #include #include +#include #include #include +#include +#include + #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" @@ -29,7 +33,12 @@ cmFileAPI::cmFileAPI(cmake* cm) : CMakeInstance(cm) { this->APIv1 = - this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1"; + cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/.cmake/api/v1"); + + if (cm::optional cmakeConfigDir = + cmSystemTools::GetCMakeConfigDirectory()) { + this->UserAPIv1 = cmStrCat(std::move(*cmakeConfigDir), "/api/v1"_s); + } Json::CharReaderBuilder rbuilder; rbuilder["collectComments"] = false; @@ -47,14 +56,24 @@ cmFileAPI::cmFileAPI(cmake* cm) void cmFileAPI::ReadQueries() { - std::string const query_dir = this->APIv1 + "/query"; + std::string const query_dir = cmStrCat(this->APIv1, "/query"); + std::string const user_query_dir = cmStrCat(this->UserAPIv1, "/query"); this->QueryExists = cmSystemTools::FileIsDirectory(query_dir); + if (!this->UserAPIv1.empty()) { + this->QueryExists = + this->QueryExists || cmSystemTools::FileIsDirectory(user_query_dir); + } if (!this->QueryExists) { return; } // Load queries at the top level. std::vector queries = cmFileAPI::LoadDir(query_dir); + if (!this->UserAPIv1.empty()) { + std::vector user_queries = cmFileAPI::LoadDir(user_query_dir); + std::move(user_queries.begin(), user_queries.end(), + std::back_inserter(queries)); + } // Read the queries and save for later. for (std::string& query : queries) { diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h index 1c13d7b6b..ace19ee98 100644 --- a/Source/cmFileAPI.h +++ b/Source/cmFileAPI.h @@ -61,6 +61,9 @@ private: /** The api/v1 directory location. */ std::string APIv1; + /** api/v1 directory in the user's shared CMake config directory. */ + std::string UserAPIv1; + /** The set of files we have just written to the reply directory. */ std::unordered_set ReplyFiles; diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 08baca060..92e6b3e81 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -881,6 +882,42 @@ bool HandleMakeDirectoryCommand(std::vector const& args, // Projects might pass a dynamically generated list of directories, and it // could be an empty list. We should not assume there is at least one. + cmRange::const_iterator> argsRange = + cmMakeRange(args).advance(1); // Get rid of subcommand + + struct Arguments : public ArgumentParser::ParseResult + { + std::string Result; + }; + Arguments arguments; + + auto resultPosItr = + std::find(cm::begin(argsRange), cm::end(argsRange), "RESULT"); + if (resultPosItr != cm::end(argsRange)) { + static auto const parser = + cmArgumentParser{}.Bind("RESULT"_s, &Arguments::Result); + std::vector unparsedArguments; + auto resultDistanceFromBegin = + std::distance(cm::begin(argsRange), resultPosItr); + arguments = + parser.Parse(cmMakeRange(argsRange).advance(resultDistanceFromBegin), + &unparsedArguments); + + if (!unparsedArguments.empty()) { + std::string unexpectedArgsStr = cmJoin( + cmMakeRange(cm::begin(unparsedArguments), cm::end(unparsedArguments)), + "\n"); + status.SetError("MAKE_DIRECTORY called with unexpected\n" + "arguments:\n" + + unexpectedArgsStr); + return false; + } + + auto resultDistanceFromEnd = + std::distance(cm::end(argsRange), resultPosItr); + argsRange = argsRange.retreat(-resultDistanceFromEnd); + } + std::string expr; for (std::string const& arg : cmMakeRange(args).advance(1)) // Get rid of subcommand @@ -892,20 +929,34 @@ bool HandleMakeDirectoryCommand(std::vector const& args, cdir = &expr; } if (!status.GetMakefile().CanIWriteThisFile(*cdir)) { - std::string e = "attempted to create a directory: " + *cdir + - " into a source directory."; - status.SetError(e); - cmSystemTools::SetFatalErrorOccurred(); - return false; + std::string e = cmStrCat("attempted to create a directory: ", *cdir, + " into a source directory."); + if (arguments.Result.empty()) { + status.SetError(e); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + status.GetMakefile().AddDefinition(arguments.Result, e); + return true; } cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir); if (!mkdirStatus) { - std::string error = cmStrCat("failed to create directory:\n ", *cdir, - "\nbecause: ", mkdirStatus.GetString()); - status.SetError(error); - return false; + if (arguments.Result.empty()) { + std::string errorOutput = + cmStrCat("failed to create directory:\n ", *cdir, + "\nbecause: ", mkdirStatus.GetString()); + status.SetError(errorOutput); + return false; + } + std::string errorResult = cmStrCat("Failed to create directory: ", *cdir, + " Error: ", mkdirStatus.GetString()); + status.GetMakefile().AddDefinition(arguments.Result, errorResult); + return true; } } + if (!arguments.Result.empty()) { + status.GetMakefile().AddDefinition(arguments.Result, "0"); + } return true; } @@ -1388,6 +1439,13 @@ bool HandleRealPathCommand(std::vector const& args, realPath = oldPolicyPath; } + if (!cmSystemTools::FileExists(realPath)) { + status.GetMakefile().IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("Given path:\n ", input, + "\ndoes not refer to an existing path on disk.")); + } + status.GetMakefile().AddDefinition(args[2], realPath); return true; @@ -1682,6 +1740,9 @@ bool HandleNativePathCommand(std::vector const& args, #if !defined(CMAKE_BOOTSTRAP) +const bool TLS_VERIFY_DEFAULT = true; +const std::string TLS_VERSION_DEFAULT = "1.2"; + // Stuff for curl download/upload using cmFileCommandVectorOfChar = std::vector; @@ -1874,8 +1935,8 @@ bool HandleDownloadCommand(std::vector const& args, long inactivity_timeout = 0; std::string logVar; std::string statusVar; - cm::optional tls_version; - cm::optional tls_verify; + cm::optional tlsVersionOpt; + cm::optional tlsVerifyOpt; cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string netrc_level = status.GetMakefile().GetSafeDefinition("CMAKE_NETRC"); @@ -1924,7 +1985,7 @@ bool HandleDownloadCommand(std::vector const& args, } else if (*i == "TLS_VERSION") { ++i; if (i != args.end()) { - tls_version = *i; + tlsVersionOpt = *i; } else { status.SetError("DOWNLOAD missing value for TLS_VERSION."); return false; @@ -1932,7 +1993,7 @@ bool HandleDownloadCommand(std::vector const& args, } else if (*i == "TLS_VERIFY") { ++i; if (i != args.end()) { - tls_verify = cmIsOn(*i); + tlsVerifyOpt = cmIsOn(*i); } else { status.SetError("DOWNLOAD missing bool value for TLS_VERIFY."); return false; @@ -2040,29 +2101,39 @@ bool HandleDownloadCommand(std::vector const& args, ++i; } - if (!tls_verify) { + if (!tlsVerifyOpt.has_value()) { if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) { - tls_verify = v.IsOn(); + tlsVerifyOpt = v.IsOn(); } } - if (!tls_verify) { + if (!tlsVerifyOpt.has_value()) { if (cm::optional v = cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) { - tls_verify = cmIsOn(*v); + tlsVerifyOpt = cmIsOn(*v); } } + bool tlsVerifyDefaulted = false; + if (!tlsVerifyOpt.has_value()) { + tlsVerifyOpt = TLS_VERIFY_DEFAULT; + tlsVerifyDefaulted = true; + } - if (!tls_version) { + if (!tlsVersionOpt.has_value()) { if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) { - tls_version = *v; + tlsVersionOpt = *v; } } - if (!tls_version) { + if (!tlsVersionOpt.has_value()) { if (cm::optional v = cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) { - tls_version = std::move(v); + tlsVersionOpt = std::move(v); } } + bool tlsVersionDefaulted = false; + if (!tlsVersionOpt.has_value()) { + tlsVersionOpt = TLS_VERSION_DEFAULT; + tlsVersionDefaulted = true; + } // Can't calculate hash if we don't save the file. // TODO Incrementally calculate hash in the write callback as the file is @@ -2144,21 +2215,24 @@ bool HandleDownloadCommand(std::vector const& args, cmFileCommandCurlDebugCallback); check_curl_result(res, "DOWNLOAD cannot set debug function: "); - if (tls_version) { - if (cm::optional v = cmCurlParseTLSVersion(*tls_version)) { + if (tlsVersionOpt.has_value()) { + if (cm::optional v = cmCurlParseTLSVersion(*tlsVersionOpt)) { res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v); - check_curl_result( - res, - cmStrCat("DOWNLOAD cannot set TLS/SSL version ", *tls_version, ": ")); + if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) { + res = CURLE_OK; + } + check_curl_result(res, + cmStrCat("DOWNLOAD cannot set TLS/SSL version ", + *tlsVersionOpt, ": ")); } else { status.SetError( - cmStrCat("DOWNLOAD given unknown TLS/SSL version ", *tls_version)); + cmStrCat("DOWNLOAD given unknown TLS/SSL version ", *tlsVersionOpt)); return false; } } // check to see if TLS verification is requested - if (tls_verify && *tls_verify) { + if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) { res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify on: "); } else { @@ -2259,9 +2333,17 @@ bool HandleDownloadCommand(std::vector const& args, ::curl_easy_cleanup(curl); if (!statusVar.empty()) { + std::string m = curl_easy_strerror(res); + if ((res == CURLE_SSL_CONNECT_ERROR || + res == CURLE_PEER_FAILED_VERIFICATION) && + tlsVerifyDefaulted) { + m = cmStrCat( + std::move(m), + ". If this is due to https certificate verification failure, one may " + "set environment variable CMAKE_TLS_VERIFY=0 to suppress it."); + } status.GetMakefile().AddDefinition( - statusVar, - cmStrCat(static_cast(res), ";\"", ::curl_easy_strerror(res), "\"")); + statusVar, cmStrCat(static_cast(res), ";\"", std::move(m), "\"")); } ::curl_global_cleanup(); @@ -2346,8 +2428,8 @@ bool HandleUploadCommand(std::vector const& args, std::string logVar; std::string statusVar; bool showProgress = false; - cm::optional tls_version; - cm::optional tls_verify; + cm::optional tlsVersionOpt; + cm::optional tlsVerifyOpt; cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string userpwd; std::string netrc_level = @@ -2393,7 +2475,7 @@ bool HandleUploadCommand(std::vector const& args, } else if (*i == "TLS_VERSION") { ++i; if (i != args.end()) { - tls_version = *i; + tlsVersionOpt = *i; } else { status.SetError("UPLOAD missing value for TLS_VERSION."); return false; @@ -2401,7 +2483,7 @@ bool HandleUploadCommand(std::vector const& args, } else if (*i == "TLS_VERIFY") { ++i; if (i != args.end()) { - tls_verify = cmIsOn(*i); + tlsVerifyOpt = cmIsOn(*i); } else { status.SetError("UPLOAD missing bool value for TLS_VERIFY."); return false; @@ -2453,29 +2535,39 @@ bool HandleUploadCommand(std::vector const& args, ++i; } - if (!tls_verify) { + if (!tlsVerifyOpt.has_value()) { if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) { - tls_verify = v.IsOn(); + tlsVerifyOpt = v.IsOn(); } } - if (!tls_verify) { + if (!tlsVerifyOpt.has_value()) { if (cm::optional v = cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) { - tls_verify = cmIsOn(*v); + tlsVerifyOpt = cmIsOn(*v); } } + bool tlsVerifyDefaulted = false; + if (!tlsVerifyOpt.has_value()) { + tlsVerifyOpt = TLS_VERIFY_DEFAULT; + tlsVerifyDefaulted = true; + } - if (!tls_version) { + if (!tlsVersionOpt.has_value()) { if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) { - tls_version = *v; + tlsVersionOpt = *v; } } - if (!tls_version) { + if (!tlsVersionOpt.has_value()) { if (cm::optional v = cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) { - tls_version = std::move(v); + tlsVersionOpt = std::move(v); } } + bool tlsVersionDefaulted = false; + if (!tlsVersionOpt.has_value()) { + tlsVersionOpt = TLS_VERSION_DEFAULT; + tlsVersionDefaulted = true; + } // Open file for reading: // @@ -2522,21 +2614,24 @@ bool HandleUploadCommand(std::vector const& args, cmFileCommandCurlDebugCallback); check_curl_result(res, "UPLOAD cannot set debug function: "); - if (tls_version) { - if (cm::optional v = cmCurlParseTLSVersion(*tls_version)) { + if (tlsVersionOpt.has_value()) { + if (cm::optional v = cmCurlParseTLSVersion(*tlsVersionOpt)) { res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v); + if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) { + res = CURLE_OK; + } check_curl_result( res, - cmStrCat("UPLOAD cannot set TLS/SSL version ", *tls_version, ": ")); + cmStrCat("UPLOAD cannot set TLS/SSL version ", *tlsVersionOpt, ": ")); } else { status.SetError( - cmStrCat("UPLOAD given unknown TLS/SSL version ", *tls_version)); + cmStrCat("UPLOAD given unknown TLS/SSL version ", *tlsVersionOpt)); return false; } } // check to see if TLS verification is requested - if (tls_verify && *tls_verify) { + if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) { res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify on: "); } else { @@ -2639,9 +2734,17 @@ bool HandleUploadCommand(std::vector const& args, ::curl_easy_cleanup(curl); if (!statusVar.empty()) { + std::string m = curl_easy_strerror(res); + if ((res == CURLE_SSL_CONNECT_ERROR || + res == CURLE_PEER_FAILED_VERIFICATION) && + tlsVerifyDefaulted) { + m = cmStrCat( + std::move(m), + ". If this is due to https certificate verification failure, one may " + "set environment variable CMAKE_TLS_VERIFY=0 to suppress it."); + } status.GetMakefile().AddDefinition( - statusVar, - cmStrCat(static_cast(res), ";\"", ::curl_easy_strerror(res), "\"")); + statusVar, cmStrCat(static_cast(res), ";\"", std::move(m), "\"")); } ::curl_global_cleanup(); @@ -3556,6 +3659,7 @@ bool HandleArchiveCreateCommand(std::vector const& args, // accepted without one and treated as if an empty value were given. // Fixing this would require a policy. ArgumentParser::Maybe MTime; + std::string WorkingDirectory; bool Verbose = false; // "PATHS" requires at least one value, but use a custom check below. ArgumentParser::MaybeEmpty> Paths; @@ -3568,6 +3672,7 @@ bool HandleArchiveCreateCommand(std::vector const& args, .Bind("COMPRESSION"_s, &Arguments::Compression) .Bind("COMPRESSION_LEVEL"_s, &Arguments::CompressionLevel) .Bind("MTIME"_s, &Arguments::MTime) + .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory) .Bind("VERBOSE"_s, &Arguments::Verbose) .Bind("PATHS"_s, &Arguments::Paths); @@ -3667,7 +3772,8 @@ bool HandleArchiveCreateCommand(std::vector const& args, return false; } - if (!cmSystemTools::CreateTar(parsedArgs.Output, parsedArgs.Paths, compress, + if (!cmSystemTools::CreateTar(parsedArgs.Output, parsedArgs.Paths, + parsedArgs.WorkingDirectory, compress, parsedArgs.Verbose, parsedArgs.MTime, parsedArgs.Format, compressionLevel)) { status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output)); diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 8840cdcbf..81e081c80 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -149,7 +148,7 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) return false; } auto command = this->Makefile->GetState()->GetCommand(args[j]); - if (command == nullptr) { + if (!command) { this->SetError(cmStrCat( "command specified for \"VALIDATOR\" is undefined: ", args[j], '.')); return false; diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index 31cc14bea..31243f63e 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -212,7 +212,8 @@ void cmFindCommon::SelectDefaultSearchModes() } } -void cmFindCommon::RerootPaths(std::vector& paths) +void cmFindCommon::RerootPaths(std::vector& paths, + std::string* debugBuffer) { #if 0 for(std::string const& p : paths) @@ -238,17 +239,40 @@ void cmFindCommon::RerootPaths(std::vector& paths) return; } + if (this->DebugMode && debugBuffer) { + *debugBuffer = cmStrCat( + *debugBuffer, "Prepending the following roots to each prefix:\n"); + } + + auto debugRoot = [this, debugBuffer](const std::string& name, + cmValue value) { + if (this->DebugMode && debugBuffer) { + *debugBuffer = cmStrCat(*debugBuffer, name, "\n"); + cmList roots{ value }; + if (roots.empty()) { + *debugBuffer = cmStrCat(*debugBuffer, " none\n"); + } + for (auto const& root : roots) { + *debugBuffer = cmStrCat(*debugBuffer, " ", root, "\n"); + } + } + }; + // Construct the list of path roots with no trailing slashes. cmList roots; + debugRoot("CMAKE_FIND_ROOT_PATH", rootPath); if (rootPath) { roots.assign(*rootPath); } + debugRoot("CMAKE_SYSROOT_COMPILE", sysrootCompile); if (sysrootCompile) { roots.emplace_back(*sysrootCompile); } + debugRoot("CMAKE_SYSROOT_LINK", sysrootLink); if (sysrootLink) { roots.emplace_back(*sysrootLink); } + debugRoot("CMAKE_SYSROOT", sysroot); if (sysroot) { roots.emplace_back(*sysroot); } @@ -411,7 +435,8 @@ static void AddTrailingSlash(std::string& s) s += '/'; } } -void cmFindCommon::ComputeFinalPaths(IgnorePaths ignorePaths) +void cmFindCommon::ComputeFinalPaths(IgnorePaths ignorePaths, + std::string* debugBuffer) { // Filter out ignored paths from the prefix list std::set ignoredPaths; @@ -430,7 +455,7 @@ void cmFindCommon::ComputeFinalPaths(IgnorePaths ignorePaths) } // Expand list of paths inside all search roots. - this->RerootPaths(this->SearchPaths); + this->RerootPaths(this->SearchPaths, debugBuffer); // Add a trailing slash to all paths to aid the search process. std::for_each(this->SearchPaths.begin(), this->SearchPaths.end(), diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h index 41de797ee..1abf56726 100644 --- a/Source/cmFindCommon.h +++ b/Source/cmFindCommon.h @@ -81,7 +81,8 @@ protected: void InitializeSearchPathGroups(); /** Place a set of search paths under the search roots. */ - void RerootPaths(std::vector& paths); + void RerootPaths(std::vector& paths, + std::string* debugBuffer = nullptr); /** Get ignored paths from CMAKE_[SYSTEM_]IGNORE_PATH variables. */ void GetIgnoredPaths(std::vector& ignore); @@ -97,7 +98,8 @@ protected: No, Yes, }; - void ComputeFinalPaths(IgnorePaths ignorePaths); + void ComputeFinalPaths(IgnorePaths ignorePaths, + std::string* debugBuffer = nullptr); /** Compute the current default root path mode. */ void SelectDefaultRootPathMode(); diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 344c2506c..cc150fda0 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -106,7 +106,7 @@ public: bool isDirentryToIgnore(const char* const fname) { - assert(fname != nullptr); + assert(fname); assert(fname[0] != 0); return fname[0] == '.' && (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); @@ -2013,7 +2013,7 @@ void cmFindPackageCommand::ComputePrefixes() } this->FillPrefixesUserGuess(); - this->ComputeFinalPaths(IgnorePaths::No); + this->ComputeFinalPaths(IgnorePaths::No, &this->DebugBuffer); } void cmFindPackageCommand::FillPrefixesPackageRedirect() diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index 8a2a69e42..dd22b4188 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -327,7 +327,7 @@ std::string cmFindProgramCommand::GetBundleExecutable( // returned executableURL is relative to /Contents/MacOS/ CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle); - if (executableURL != nullptr) { + if (executableURL) { const int MAX_OSX_PATH_SIZE = 1024; UInt8 buffer[MAX_OSX_PATH_SIZE]; diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 8e590fa8c..553ea1fb9 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -7,6 +7,8 @@ #include #include +#include + #include "cmsys/RegularExpression.hxx" #include "cmGeneratorExpressionContext.h" @@ -193,7 +195,7 @@ static std::string stripAllGeneratorExpressions(const std::string& input) } static void prefixItems(const std::string& content, std::string& result, - const std::string& prefix) + const cm::string_view& prefix) { std::vector entries; cmGeneratorExpression::Split(content, entries); @@ -211,7 +213,7 @@ static void prefixItems(const std::string& content, std::string& result, static std::string stripExportInterface( const std::string& input, cmGeneratorExpression::PreprocessContext context, - bool resolveRelative) + cm::string_view importPrefix) { std::string result; @@ -268,8 +270,8 @@ static std::string stripExportInterface( } else if (context == cmGeneratorExpression::InstallInterface && foundGenex == FoundGenex::InstallInterface) { const std::string content = input.substr(pos, c - cStart); - if (resolveRelative) { - prefixItems(content, result, "${_IMPORT_PREFIX}/"); + if (!importPrefix.empty()) { + prefixItems(content, result, importPrefix); } else { result += content; } @@ -359,13 +361,13 @@ void cmGeneratorExpression::Split(const std::string& input, std::string cmGeneratorExpression::Preprocess(const std::string& input, PreprocessContext context, - bool resolveRelative) + cm::string_view importPrefix) { if (context == StripAllGeneratorExpressions) { return stripAllGeneratorExpressions(input); } if (context == BuildInterface || context == InstallInterface) { - return stripExportInterface(input, context, resolveRelative); + return stripExportInterface(input, context, importPrefix); } assert(false && @@ -373,14 +375,15 @@ std::string cmGeneratorExpression::Preprocess(const std::string& input, return std::string(); } -std::string::size_type cmGeneratorExpression::Find(const std::string& input) +cm::string_view::size_type cmGeneratorExpression::Find( + const cm::string_view& input) { - const std::string::size_type openpos = input.find("$<"); - if (openpos != std::string::npos && - input.find('>', openpos) != std::string::npos) { + const cm::string_view::size_type openpos = input.find("$<"); + if (openpos != cm::string_view::npos && + input.find('>', openpos) != cm::string_view::npos) { return openpos; } - return std::string::npos; + return cm::string_view::npos; } bool cmGeneratorExpression::IsValidTargetName(const std::string& input) diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 71855c9f6..134930804 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -11,6 +11,8 @@ #include #include +#include + #include "cmListFileCache.h" #include "cmLocalGenerator.h" @@ -59,12 +61,12 @@ public: static std::string Preprocess(const std::string& input, PreprocessContext context, - bool resolveRelative = false); + cm::string_view importPrefix = {}); static void Split(const std::string& input, std::vector& output); - static std::string::size_type Find(const std::string& input); + static cm::string_view::size_type Find(const cm::string_view& input); static bool IsValidTargetName(const std::string& input); @@ -76,7 +78,7 @@ public: } static inline bool StartsWithGeneratorExpression(const char* input) { - return input != nullptr && input[0] == '$' && input[1] == '<'; + return input && input[0] == '$' && input[1] == '<'; } static void ReplaceInstallPrefix(std::string& input, diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index aad25f032..92a76dc70 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -24,7 +24,7 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( std::string const& contextConfig) : cmGeneratorExpressionDAGChecker(cmListFileBacktrace(), target, std::move(property), content, parent, - contextLG, contextConfig) + contextLG, contextConfig, INHERIT) { } @@ -33,8 +33,20 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( std::string property, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, std::string const& contextConfig) + : cmGeneratorExpressionDAGChecker(std::move(backtrace), target, + std::move(property), content, parent, + contextLG, contextConfig, INHERIT) +{ +} + +cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( + cmListFileBacktrace backtrace, cmGeneratorTarget const* target, + std::string property, const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, + std::string const& contextConfig, TransitiveClosure closure) : Parent(parent) , Top(parent ? parent->Top : this) + , Closure((closure == SUBGRAPH || !parent) ? this : parent->Closure) , Target(target) , Property(std::move(property)) , Content(content) @@ -53,16 +65,16 @@ cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker( this->CheckResult = this->CheckGraph(); if (this->CheckResult == DAG && this->EvaluatingTransitiveProperty()) { - const auto* top = this->Top; - auto it = top->Seen.find(this->Target); - if (it != top->Seen.end()) { + const auto* transitiveClosure = this->Closure; + auto it = transitiveClosure->Seen.find(this->Target); + if (it != transitiveClosure->Seen.end()) { const std::set& propSet = it->second; if (propSet.find(this->Property) != propSet.end()) { this->CheckResult = ALREADY_SEEN; return; } } - top->Seen[this->Target].insert(this->Property); + transitiveClosure->Seen[this->Target].insert(this->Property); } } diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 8b0eea76b..03f6b399a 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -17,6 +17,17 @@ class cmLocalGenerator; struct cmGeneratorExpressionDAGChecker { + enum TransitiveClosure + { + INHERIT, + SUBGRAPH, + }; + + cmGeneratorExpressionDAGChecker( + cmListFileBacktrace backtrace, cmGeneratorTarget const* target, + std::string property, const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* parent, cmLocalGenerator const* contextLG, + std::string const& contextConfig, TransitiveClosure closure); cmGeneratorExpressionDAGChecker(cmListFileBacktrace backtrace, cmGeneratorTarget const* target, std::string property, @@ -76,6 +87,7 @@ private: const cmGeneratorExpressionDAGChecker* const Parent; const cmGeneratorExpressionDAGChecker* const Top; + const cmGeneratorExpressionDAGChecker* const Closure; cmGeneratorTarget const* Target; const std::string Property; mutable std::map> Seen; diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 84521cadd..7f4e4ac77 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -2983,8 +2983,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode if (isInterfaceProperty) { return cmGeneratorExpression::StripEmptyListElements( - target->EvaluateInterfaceProperty(propertyName, context, - dagCheckerParent, usage)); + target->EvaluateInterfaceProperty( + propertyName, context, dagCheckerParent, usage, + cmGeneratorTarget::TransitiveClosure::Subgraph)); } cmGeneratorExpressionDAGChecker dagChecker( @@ -4468,8 +4469,8 @@ static const struct ShellPathNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - cmList listIn{ parameters.front() }; - if (listIn.empty()) { + cmList list_in{ parameters.front() }; + if (list_in.empty()) { reportError(context, content->GetOriginalExpression(), "\"\" is not an absolute path."); return std::string(); @@ -4477,17 +4478,17 @@ static const struct ShellPathNode : public cmGeneratorExpressionNode cmStateSnapshot snapshot = context->LG->GetStateSnapshot(); cmOutputConverter converter(snapshot); const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":"; - std::vector listOut; - listOut.reserve(listIn.size()); - for (auto const& in : listIn) { + std::vector list_out; + list_out.reserve(list_in.size()); + for (auto const& in : list_in) { if (!cmSystemTools::FileIsFullPath(in)) { reportError(context, content->GetOriginalExpression(), "\"" + in + "\" is not an absolute path."); return std::string(); } - listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); + list_out.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); } - return cmJoin(listOut, separator); + return cmJoin(list_out, separator); } } shellPathNode; diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 727d452f9..fb92771b0 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -37,6 +37,7 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPropertyMap.h" +#include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" @@ -138,6 +139,20 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) } else { this->LinkerLanguage = this->Target->GetSafeProperty("LINKER_LANGUAGE"); } + + auto configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + std::string build_db_languages[] = { "CXX" }; + for (auto const& language : build_db_languages) { + for (auto const& config : configs) { + auto bdb_path = this->BuildDatabasePath(language, config); + if (!bdb_path.empty()) { + this->Makefile->GetOrCreateGeneratedSource(bdb_path); + this->GetGlobalGenerator()->AddBuildDatabaseFile(language, config, + bdb_path); + } + } + } } cmGeneratorTarget::~cmGeneratorTarget() = default; @@ -173,6 +188,20 @@ const std::string& cmGeneratorTarget::GetName() const return this->Target->GetName(); } +std::string cmGeneratorTarget::GetFamilyName() const +{ + if (!this->IsImported() && !this->IsSynthetic()) { + return this->Target->GetTemplateName(); + } + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); + constexpr size_t HASH_TRUNCATION = 12; + auto dirhash = + hasher.HashString(this->GetLocalGenerator()->GetCurrentBinaryDirectory()); + auto targetIdent = hasher.HashString(cmStrCat("@d_", dirhash)); + return cmStrCat(this->Target->GetTemplateName(), '@', + targetIdent.substr(0, HASH_TRUNCATION)); +} + std::string cmGeneratorTarget::GetExportName() const { cmValue exportName = this->GetProperty("EXPORT_NAME"); @@ -585,6 +614,10 @@ void cmGeneratorTarget::AddSystemIncludeDirectory(std::string const& inc, } auto const& key = cmStrCat(config_upper, "/", lang); this->Target->AddSystemIncludeDirectories({ inc_with_config }); + if (this->SystemIncludesCache.find(key) == + this->SystemIncludesCache.end()) { + this->AddSystemIncludeCacheKey(key, config, lang); + } this->SystemIncludesCache[key].emplace_back(inc_with_config); // SystemIncludesCache should be sorted so that binary search can be used @@ -1039,7 +1072,7 @@ void cmGeneratorTarget::AppendCustomCommandSideEffects( sideEffects.insert(this); } else { for (auto const& source : this->GetAllConfigSources()) { - if (source.Source->GetCustomCommand() != nullptr) { + if (source.Source->GetCustomCommand()) { sideEffects.insert(this); break; } @@ -1163,6 +1196,51 @@ const std::string& cmGeneratorTarget::GetLocationForBuild() const return location; } +void cmGeneratorTarget::AddSystemIncludeCacheKey( + const std::string& key, const std::string& config, + const std::string& language) const +{ + cmGeneratorExpressionDAGChecker dagChecker( + this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr, this->LocalGenerator, + config); + + bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED"); + + cmList result; + for (std::string const& it : this->Target->GetSystemIncludeDirectories()) { + result.append(cmGeneratorExpression::Evaluate( + it, this->LocalGenerator, config, this, &dagChecker, nullptr, language)); + } + + std::vector const& deps = + this->GetLinkImplementationClosure(config, UseTo::Compile); + for (cmGeneratorTarget const* dep : deps) { + handleSystemIncludesDep(this->LocalGenerator, dep, config, this, + &dagChecker, result, excludeImported, language); + } + + cmLinkImplementation const* impl = + this->GetLinkImplementation(config, UseTo::Compile); + if (impl) { + auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language); + if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) { + for (auto const& lib : runtimeEntries->second) { + if (lib.Target) { + handleSystemIncludesDep(this->LocalGenerator, lib.Target, config, + this, &dagChecker, result, excludeImported, + language); + } + } + } + } + + std::for_each(result.begin(), result.end(), + cmSystemTools::ConvertToUnixSlashes); + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + SystemIncludesCache.emplace(key, result); +} + bool cmGeneratorTarget::IsSystemIncludeDirectory( const std::string& dir, const std::string& config, const std::string& language) const @@ -1176,47 +1254,8 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory( auto iter = this->SystemIncludesCache.find(key); if (iter == this->SystemIncludesCache.end()) { - cmGeneratorExpressionDAGChecker dagChecker( - this, "SYSTEM_INCLUDE_DIRECTORIES", nullptr, nullptr, - this->LocalGenerator, config); - - bool excludeImported = this->GetPropertyAsBool("NO_SYSTEM_FROM_IMPORTED"); - - cmList result; - for (std::string const& it : this->Target->GetSystemIncludeDirectories()) { - result.append(cmGeneratorExpression::Evaluate(it, this->LocalGenerator, - config, this, &dagChecker, - nullptr, language)); - } - - std::vector const& deps = - this->GetLinkImplementationClosure(config, UseTo::Compile); - for (cmGeneratorTarget const* dep : deps) { - handleSystemIncludesDep(this->LocalGenerator, dep, config, this, - &dagChecker, result, excludeImported, language); - } - - cmLinkImplementation const* impl = - this->GetLinkImplementation(config, UseTo::Compile); - if (impl != nullptr) { - auto runtimeEntries = impl->LanguageRuntimeLibraries.find(language); - if (runtimeEntries != impl->LanguageRuntimeLibraries.end()) { - for (auto const& lib : runtimeEntries->second) { - if (lib.Target) { - handleSystemIncludesDep(this->LocalGenerator, lib.Target, config, - this, &dagChecker, result, excludeImported, - language); - } - } - } - } - - std::for_each(result.begin(), result.end(), - cmSystemTools::ConvertToUnixSlashes); - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - - iter = this->SystemIncludesCache.emplace(key, result).first; + this->AddSystemIncludeCacheKey(key, config, language); + iter = this->SystemIncludesCache.find(key); } return std::binary_search(iter->second.begin(), iter->second.end(), dir); @@ -1270,7 +1309,8 @@ bool cmGeneratorTarget::HasSOName(const std::string& config) const // and then only when the platform supports an soname flag. return ((this->GetType() == cmStateEnums::SHARED_LIBRARY) && !this->GetPropertyAsBool("NO_SONAME") && - this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config))); + (this->Makefile->GetSONameFlag(this->GetLinkerLanguage(config)) || + this->IsArchivedAIXSharedLibrary())); } bool cmGeneratorTarget::NeedRelinkBeforeInstall( @@ -2000,6 +2040,425 @@ std::vector cmGeneratorTarget::GetAppleArchs( return std::move(archList.data()); } +namespace { + +bool IsSupportedClassifiedFlagsLanguage(std::string const& lang) +{ + return lang == "CXX"_s; +} + +bool CanUseCompilerLauncher(std::string const& lang) +{ + // Also found in `cmCommonTargetGenerator::GetCompilerLauncher`. + return lang == "C"_s || lang == "CXX"_s || lang == "Fortran"_s || + lang == "CUDA"_s || lang == "HIP"_s || lang == "ISPC"_s || + lang == "OBJC"_s || lang == "OBJCXX"_s; +} + +// FIXME: return a vector of `cm::string_view` instead to avoid lots of tiny +// allocations. +std::vector SplitFlags(std::string const& flags) +{ + std::vector options; + +#ifdef _WIN32 + cmSystemTools::ParseWindowsCommandLine(flags.c_str(), options); +#else + cmSystemTools::ParseUnixCommandLine(flags.c_str(), options); +#endif + + return options; +} + +} + +cmGeneratorTarget::ClassifiedFlags +cmGeneratorTarget::GetClassifiedFlagsForSource(cmSourceFile const* sf, + std::string const& config) +{ + auto& sourceFlagsCache = this->Configs[config].SourceFlags; + auto cacheEntry = sourceFlagsCache.lower_bound(sf); + if (cacheEntry != sourceFlagsCache.end() && cacheEntry->first == sf) { + return cacheEntry->second; + } + + ClassifiedFlags flags; + std::string const& lang = sf->GetLanguage(); + + if (!IsSupportedClassifiedFlagsLanguage(lang)) { + return flags; + } + + auto* const lg = this->GetLocalGenerator(); + auto const* const mf = this->Makefile; + + // Compute the compiler launcher flags. + if (CanUseCompilerLauncher(lang)) { + // Compiler launchers are all execution flags and should not be relevant to + // the actual compilation. + FlagClassification cls = FlagClassification::ExecutionFlag; + FlagKind kind = FlagKind::NotAFlag; + + std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER"); + cmValue clauncher = this->GetProperty(clauncher_prop); + std::string const evaluatedClauncher = cmGeneratorExpression::Evaluate( + *clauncher, lg, config, this, nullptr, this, lang); + + for (auto const& flag : SplitFlags(evaluatedClauncher)) { + flags.emplace_back(cls, kind, flag); + } + } + + ClassifiedFlags define_flags; + ClassifiedFlags include_flags; + ClassifiedFlags compile_flags; + + SourceVariables sfVars = this->GetSourceVariables(sf, config); + + // Compute language flags. + { + FlagClassification cls = FlagClassification::BaselineFlag; + FlagKind kind = FlagKind::Compile; + + std::string mfFlags; + // Explicitly add the explicit language flag before any other flag + // so user flags can override it. + this->AddExplicitLanguageFlags(mfFlags, *sf); + + for (auto const& flag : SplitFlags(mfFlags)) { + flags.emplace_back(cls, kind, flag); + } + } + + std::unordered_map pchSources; + std::string filterArch; + + { + std::vector pchArchs = this->GetPchArchs(config, lang); + + for (const std::string& arch : pchArchs) { + const std::string pchSource = this->GetPchSource(config, lang, arch); + if (pchSource == sf->GetFullPath()) { + filterArch = arch; + } + if (!pchSource.empty()) { + pchSources.insert(std::make_pair(pchSource, arch)); + } + } + } + + // Compute target-wide flags. + { + FlagClassification cls = FlagClassification::BaselineFlag; + + // Compile flags + { + FlagKind kind = FlagKind::Compile; + std::string targetFlags; + + lg->GetTargetCompileFlags(this, config, lang, targetFlags, filterArch); + + for (auto&& flag : SplitFlags(targetFlags)) { + compile_flags.emplace_back(cls, kind, std::move(flag)); + } + } + + // Define flags + { + FlagKind kind = FlagKind::Definition; + std::set defines; + + lg->GetTargetDefines(this, config, lang, defines); + + std::string defineFlags; + lg->JoinDefines(defines, defineFlags, lang); + + for (auto&& flag : SplitFlags(defineFlags)) { + define_flags.emplace_back(cls, kind, std::move(flag)); + } + } + + // Include flags + { + FlagKind kind = FlagKind::Include; + std::vector includes; + + lg->GetIncludeDirectories(includes, this, lang, config); + auto includeFlags = + lg->GetIncludeFlags(includes, this, lang, config, false); + + for (auto&& flag : SplitFlags(includeFlags)) { + include_flags.emplace_back(cls, kind, std::move(flag)); + } + } + } + + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + + cmGeneratorExpressionInterpreter genexInterpreter(lg, config, this, lang); + + // Source-specific flags. + { + FlagClassification cls = FlagClassification::PrivateFlag; + FlagKind kind = FlagKind::Compile; + + std::string sourceFlags; + + if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) { + lg->AppendFlags(sourceFlags, + genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); + } + + if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + sourceFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); + } + + for (auto&& flag : SplitFlags(sourceFlags)) { + compile_flags.emplace_back(cls, kind, std::move(flag)); + } + + // Dependency tracking flags. + { + if (!sfVars.DependencyFlags.empty()) { + cmRulePlaceholderExpander::RuleVariables vars; + auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + + vars.DependencyFile = sfVars.DependencyFile.c_str(); + vars.DependencyTarget = sfVars.DependencyTarget.c_str(); + + std::string depfileFlags = sfVars.DependencyFlags; + rulePlaceholderExpander->ExpandRuleVariables(lg, depfileFlags, vars); + for (auto&& flag : SplitFlags(depfileFlags)) { + compile_flags.emplace_back(FlagClassification::LocationFlag, + FlagKind::BuildSystem, std::move(flag)); + } + } + } + } + + // Precompiled headers. + { + FlagClassification cls = FlagClassification::PrivateFlag; + FlagKind kind = FlagKind::Compile; + + std::string pchFlags; + + // Add precompile headers compile options. + if (!sf->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + if (!pchSources.empty()) { + std::string pchOptions; + auto pchIt = pchSources.find(sf->GetFullPath()); + if (pchIt != pchSources.end()) { + pchOptions = + this->GetPchCreateCompileOptions(config, lang, pchIt->second); + } else { + pchOptions = this->GetPchUseCompileOptions(config, lang); + } + + this->LocalGenerator->AppendCompileOptions( + pchFlags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); + } + } + + for (auto&& flag : SplitFlags(pchFlags)) { + compile_flags.emplace_back(cls, kind, std::move(flag)); + } + } + + // C++ module flags. + if (lang == "CXX"_s) { + FlagClassification cls = FlagClassification::LocationFlag; + FlagKind kind = FlagKind::BuildSystem; + + std::string bmiFlags; + + auto const* fs = this->GetFileSetForSource(config, sf); + if (fs && fs->GetType() == "CXX_MODULES"_s) { + if (lang != "CXX"_s) { + mf->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", this->Target->GetName(), "\" contains the source\n ", + sf->GetFullPath(), "\nin a file set of type \"", fs->GetType(), + R"(" but the source is not classified as a "CXX" source.)")); + } + + if (!this->Target->IsNormal()) { + auto flag = mf->GetSafeDefinition("CMAKE_CXX_MODULE_BMI_ONLY_FLAG"); + cmRulePlaceholderExpander::RuleVariables compileObjectVars; + compileObjectVars.Object = sfVars.ObjectFileDir.c_str(); + auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + rulePlaceholderExpander->ExpandRuleVariables(lg, flag, + compileObjectVars); + lg->AppendCompileOptions(bmiFlags, flag); + } + } + + for (auto&& flag : SplitFlags(bmiFlags)) { + compile_flags.emplace_back(cls, kind, std::move(flag)); + } + } + + cmRulePlaceholderExpander::RuleVariables vars; + vars.CMTargetName = this->GetName().c_str(); + vars.CMTargetType = cmState::GetTargetTypeName(this->GetType()).c_str(); + vars.Language = lang.c_str(); + auto const sfPath = this->LocalGenerator->ConvertToOutputFormat( + sf->GetFullPath(), cmOutputConverter::SHELL); + + // Compute the base compiler command line. We'll find placeholders and + // replace them with arguments later in this function. + { + FlagClassification cls = FlagClassification::ExecutionFlag; + FlagKind kind = FlagKind::NotAFlag; + + std::string const cmdVar = cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT"); + std::string const& compileCmd = mf->GetRequiredDefinition(cmdVar); + cmList compileCmds(compileCmd); // FIXME: which command to use? + std::string& cmd = compileCmds[0]; + auto rulePlaceholderExpander = lg->CreateRulePlaceholderExpander(); + + static std::string const PlaceholderDefines = "__CMAKE_DEFINES"; + static std::string const PlaceholderIncludes = "__CMAKE_INCLUDES"; + static std::string const PlaceholderFlags = "__CMAKE_FLAGS"; + static std::string const PlaceholderSource = "__CMAKE_SOURCE"; + + vars.Defines = PlaceholderDefines.c_str(); + vars.Includes = PlaceholderIncludes.c_str(); + vars.TargetPDB = sfVars.TargetPDB.c_str(); + vars.TargetCompilePDB = sfVars.TargetCompilePDB.c_str(); + vars.Object = sfVars.ObjectFileDir.c_str(); + vars.ObjectDir = sfVars.ObjectDir.c_str(); + vars.ObjectFileDir = sfVars.ObjectFileDir.c_str(); + vars.Flags = PlaceholderFlags.c_str(); + vars.DependencyFile = sfVars.DependencyFile.c_str(); + vars.DependencyTarget = sfVars.DependencyTarget.c_str(); + vars.Source = PlaceholderSource.c_str(); + + rulePlaceholderExpander->ExpandRuleVariables(lg, cmd, vars); + for (auto&& flag : SplitFlags(cmd)) { + if (flag == PlaceholderDefines) { + flags.insert(flags.end(), define_flags.begin(), define_flags.end()); + } else if (flag == PlaceholderIncludes) { + flags.insert(flags.end(), include_flags.begin(), include_flags.end()); + } else if (flag == PlaceholderFlags) { + flags.insert(flags.end(), compile_flags.begin(), compile_flags.end()); + } else if (flag == PlaceholderSource) { + flags.emplace_back(FlagClassification::LocationFlag, + FlagKind::BuildSystem, sfPath); + } else { + flags.emplace_back(cls, kind, std::move(flag)); + // All remaining flags here are build system flags. + kind = FlagKind::BuildSystem; + } + } + } + + cacheEntry = sourceFlagsCache.emplace_hint(cacheEntry, sf, std::move(flags)); + return cacheEntry->second; +} + +cmGeneratorTarget::SourceVariables cmGeneratorTarget::GetSourceVariables( + cmSourceFile const* sf, std::string const& config) +{ + SourceVariables vars; + auto const language = sf->GetLanguage(); + auto const targetType = this->GetType(); + auto const* const lg = this->GetLocalGenerator(); + auto const* const gg = this->GetGlobalGenerator(); + auto const* const mf = this->Makefile; + + // PDB settings. + { + if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID") || + mf->GetDefinition("MSVC_CUDA_ARCHITECTURE_ID")) { + std::string pdbPath; + std::string compilePdbPath; + if (targetType <= cmStateEnums::OBJECT_LIBRARY) { + compilePdbPath = this->GetCompilePDBPath(config); + if (compilePdbPath.empty()) { + // Match VS default: `$(IntDir)vc$(PlatformToolsetVersion).pdb`. + // A trailing slash tells the toolchain to add its default file name. + compilePdbPath = this->GetSupportDirectory(); + if (gg->IsMultiConfig()) { + compilePdbPath = cmStrCat(compilePdbPath, '/', config); + } + compilePdbPath += '/'; + if (targetType == cmStateEnums::STATIC_LIBRARY) { + // Match VS default for static libs: `$(IntDir)$(ProjectName).pdb`. + compilePdbPath = cmStrCat(compilePdbPath, this->GetName(), ".pdb"); + } + } + } + + if (targetType == cmStateEnums::EXECUTABLE || + targetType == cmStateEnums::STATIC_LIBRARY || + targetType == cmStateEnums::SHARED_LIBRARY || + targetType == cmStateEnums::MODULE_LIBRARY) { + pdbPath = cmStrCat(this->GetPDBDirectory(config), '/', + this->GetPDBName(config)); + } + + vars.TargetPDB = lg->ConvertToOutputFormat( + gg->ConvertToOutputPath(std::move(pdbPath)), cmOutputConverter::SHELL); + vars.TargetCompilePDB = lg->ConvertToOutputFormat( + gg->ConvertToOutputPath(std::move(compilePdbPath)), + cmOutputConverter::SHELL); + } + } + + // Object settings. + { + std::string const objectDir = gg->ConvertToOutputPath( + cmStrCat(this->GetSupportDirectory(), gg->GetConfigDirectory(config))); + std::string const objectFileName = this->GetObjectName(sf); + std::string const objectFilePath = + cmStrCat(objectDir, '/', objectFileName); + + vars.ObjectDir = + lg->ConvertToOutputFormat(objectDir, cmOutputConverter::SHELL); + vars.ObjectFileDir = + lg->ConvertToOutputFormat(objectFilePath, cmOutputConverter::SHELL); + + // Dependency settings. + { + std::string const depfileFormatName = + cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT"); + std::string const depfileFormat = + mf->GetSafeDefinition(depfileFormatName); + if (depfileFormat != "msvc"_s) { + std::string const flagsName = + cmStrCat("CMAKE_DEPFILE_FLAGS_", language); + std::string const depfileFlags = mf->GetSafeDefinition(flagsName); + if (!depfileFlags.empty()) { + bool replaceExt = false; + if (!language.empty()) { + std::string const repVar = + cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); + replaceExt = mf->IsOn(repVar); + } + std::string const depfilePath = cmStrCat( + objectDir, '/', + replaceExt + ? cmSystemTools::GetFilenameWithoutLastExtension(objectFileName) + : objectFileName, + ".d"); + + vars.DependencyFlags = depfileFlags; + vars.DependencyTarget = vars.ObjectFileDir; + vars.DependencyFile = + lg->ConvertToOutputFormat(depfilePath, cmOutputConverter::SHELL); + } + } + } + } + + return vars; +} + void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags, cmSourceFile const& sf) const { @@ -2294,6 +2753,9 @@ std::string cmGeneratorTarget::GetCreateRuleVariable( return this->GetFeatureSpecificLinkRuleVariable(var, lang, config); } case cmStateEnums::SHARED_LIBRARY: + if (this->IsArchivedAIXSharedLibrary()) { + return "CMAKE_" + lang + "_CREATE_SHARED_LIBRARY_ARCHIVE"; + } return "CMAKE_" + lang + "_CREATE_SHARED_LIBRARY"; case cmStateEnums::MODULE_LIBRARY: return "CMAKE_" + lang + "_CREATE_SHARED_MODULE"; @@ -2833,8 +3295,7 @@ bool cmGeneratorTarget::ComputeCompileFeatures( } // Custom updates for the CUDA standard. - if (generatorTargetLanguageStandard != nullptr && - (language.first == "CUDA")) { + if (generatorTargetLanguageStandard && (language.first == "CUDA")) { if (generatorTargetLanguageStandard->Value == "98") { this->LanguageStandardMap[key].Value = "03"; } @@ -2971,16 +3432,24 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( } targetNames.Real += cmStrCat(targetNames.Base, components.suffix); targetNames.SharedObject = targetNames.Real; + } else if (this->IsArchivedAIXSharedLibrary()) { + targetNames.SharedObject = + cmStrCat(components.prefix, targetNames.Base, ".so"); + if (soversion) { + targetNames.SharedObject += "."; + targetNames.SharedObject += *soversion; + } + targetNames.Real = targetNames.Output; } else { // The library's soname. - this->ComputeVersionedName(targetNames.SharedObject, components.prefix, - targetNames.Base, components.suffix, - targetNames.Output, soversion); + targetNames.SharedObject = this->ComputeVersionedName( + components.prefix, targetNames.Base, components.suffix, + targetNames.Output, soversion); // The library's real name on disk. - this->ComputeVersionedName(targetNames.Real, components.prefix, - targetNames.Base, components.suffix, - targetNames.Output, version); + targetNames.Real = this->ComputeVersionedName( + components.prefix, targetNames.Base, components.suffix, + targetNames.Output, version); } // The import library names. @@ -3003,14 +3472,13 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( targetNames.ImportLibrary = targetNames.ImportOutput; } else { // The import library's soname. - this->ComputeVersionedName( - targetNames.ImportLibrary, importComponents.prefix, - importComponents.base, importComponents.suffix, - targetNames.ImportOutput, soversion); + targetNames.ImportLibrary = this->ComputeVersionedName( + importComponents.prefix, importComponents.base, + importComponents.suffix, targetNames.ImportOutput, soversion); // The import library's real name on disk. - this->ComputeVersionedName( - targetNames.ImportReal, importComponents.prefix, importComponents.base, + targetNames.ImportReal = this->ComputeVersionedName( + importComponents.prefix, importComponents.base, importComponents.suffix, targetNames.ImportOutput, version); } } @@ -3054,8 +3522,13 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames( // The executable name. targetNames.Base = components.base; - targetNames.Output = - components.prefix + targetNames.Base + components.suffix; + + if (this->IsArchivedAIXSharedLibrary()) { + targetNames.Output = components.prefix + targetNames.Base; + } else { + targetNames.Output = + components.prefix + targetNames.Base + components.suffix; + } // The executable's real name on disk. #if defined(__CYGWIN__) @@ -3685,16 +4158,19 @@ std::string cmGeneratorTarget::GetFrameworkVersion() const return "A"; } -void cmGeneratorTarget::ComputeVersionedName( - std::string& vName, std::string const& prefix, std::string const& base, - std::string const& suffix, std::string const& name, cmValue version) const +std::string cmGeneratorTarget::ComputeVersionedName(std::string const& prefix, + std::string const& base, + std::string const& suffix, + std::string const& name, + cmValue version) const { - vName = this->IsApple() ? (prefix + base) : name; + std::string vName = this->IsApple() ? (prefix + base) : name; if (version) { vName += "."; vName += *version; } vName += this->IsApple() ? suffix : std::string(); + return vName; } std::vector cmGeneratorTarget::GetPropertyKeys() const @@ -4801,6 +5277,11 @@ bool cmGeneratorTarget::IsFrameworkOnApple() const return this->Target->IsFrameworkOnApple(); } +bool cmGeneratorTarget::IsArchivedAIXSharedLibrary() const +{ + return this->Target->IsArchivedAIXSharedLibrary(); +} + bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple( const std::string& config) const { @@ -5459,6 +5940,31 @@ cmGeneratorTarget::CxxModuleSupport cmGeneratorTarget::NeedCxxDyndep( return policyAnswer; } +std::string cmGeneratorTarget::BuildDatabasePath( + std::string const& lang, std::string const& config) const +{ + // Check to see if the target wants it. + if (!this->GetPropertyAsBool("EXPORT_BUILD_DATABASE")) { + return {}; + } + if (!cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::ExportBuildDatabase)) { + return {}; + } + // Check to see if the generator supports it. + if (!this->GetGlobalGenerator()->SupportsBuildDatabase()) { + return {}; + } + + if (this->GetGlobalGenerator()->IsMultiConfig()) { + return cmStrCat(this->GetSupportDirectory(), '/', config, '/', lang, + "_build_database.json"); + } + + return cmStrCat(this->GetSupportDirectory(), '/', lang, + "_build_database.json"); +} + void cmGeneratorTarget::BuildFileSetInfoCache(std::string const& config) const { auto& per_config = this->Configs[config]; diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 1e6ff782a..8ae98e8ea 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -108,6 +108,7 @@ public: cmStateEnums::TargetType GetType() const; const std::string& GetName() const; + std::string GetFamilyName() const; std::string GetExportName() const; std::string GetFilesystemExportName() const; @@ -516,6 +517,64 @@ public: std::vector GetAppleArchs(std::string const& config, cm::optional lang) const; + // The classification of the flag. + enum class FlagClassification + { + // The flag is for the execution of the tool (e.g., the compiler itself, + // any launchers, etc.). + ExecutionFlag, + // The flag is "baseline" and should be apply to TUs which may interact + // with this compilation (e.g., imported modules). + BaselineFlag, + // The flag is "private" and doesn't need to apply to interacting TUs. + PrivateFlag, + // Flags for the TU itself (e.g., output paths, dependency scanning, etc.). + LocationFlag, + }; + enum class FlagKind + { + // Not a flag (executable or other entries). + NotAFlag, + // Flags for support of the build system. + BuildSystem, + // A compilation flag. + Compile, + // An include flag. + Include, + // A compile definition. + Definition, + }; + struct ClassifiedFlag + { + ClassifiedFlag(FlagClassification cls, FlagKind kind, std::string flag) + : Classification(cls) + , Kind(kind) + , Flag(std::move(flag)) + { + } + + FlagClassification Classification; + FlagKind Kind; + std::string Flag; + }; + using ClassifiedFlags = std::vector; + ClassifiedFlags GetClassifiedFlagsForSource(cmSourceFile const* sf, + std::string const& config); + struct SourceVariables + { + std::string TargetPDB; + std::string TargetCompilePDB; + std::string ObjectDir; + std::string ObjectFileDir; + std::string DependencyFile; + std::string DependencyTarget; + + // Dependency flags (if used) + std::string DependencyFlags; + }; + SourceVariables GetSourceVariables(cmSourceFile const* sf, + std::string const& config); + void AddExplicitLanguageFlags(std::string& flags, cmSourceFile const& sf) const; @@ -656,6 +715,10 @@ public: const std::string& config, const std::string& language) const; + void AddSystemIncludeCacheKey(const std::string& key, + const std::string& config, + const std::string& language) const; + /** Add the target output files to the global generator manifest. */ void ComputeTargetManifest(const std::string& config) const; @@ -891,6 +954,9 @@ public: /** Return whether this target is a CFBundle (plugin) on Apple. */ bool IsCFBundleOnApple() const; + /** Return whether this target is a shared library on AIX. */ + bool IsArchivedAIXSharedLibrary() const; + /** Assembly types. The order of the values of this enum is relevant because of smaller/larger comparison operations! */ enum ManagedType @@ -911,11 +977,23 @@ public: const std::string& report, const std::string& compatibilityType) const; + /** Configures TransitiveClosureOptimization. Re-evaluation of a + transitive property will only be optimized within a subgraph. */ + enum class TransitiveClosure + { + Inherit, // node is in the same subgraph as its' parent + Subgraph, // the current node spans a new subgraph + }; + class TargetPropertyEntry; std::string EvaluateInterfaceProperty( std::string const& prop, cmGeneratorExpressionContext* context, cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const; + std::string EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage, + TransitiveClosure closure) const; struct TransitiveProperty { @@ -1104,9 +1182,11 @@ private: // Returns ARCHIVE, LIBRARY, or RUNTIME based on platform and type. const char* GetOutputTargetType(cmStateEnums::ArtifactType artifact) const; - void ComputeVersionedName(std::string& vName, std::string const& prefix, - std::string const& base, std::string const& suffix, - std::string const& name, cmValue version) const; + std::string ComputeVersionedName(std::string const& prefix, + std::string const& base, + std::string const& suffix, + std::string const& name, + cmValue version) const; mutable std::map CustomTransitiveBuildPropertiesMap; @@ -1436,6 +1516,9 @@ public: }; CxxModuleSupport NeedCxxDyndep(std::string const& config) const; + std::string BuildDatabasePath(std::string const& lang, + std::string const& config) const; + private: void BuildFileSetInfoCache(std::string const& config) const; struct InfoByConfig @@ -1444,6 +1527,7 @@ private: std::map FileSetCache; std::map> SyntheticDeps; + std::map SourceFlags; }; mutable std::map Configs; }; diff --git a/Source/cmGeneratorTarget_CompatibleInterface.cxx b/Source/cmGeneratorTarget_CompatibleInterface.cxx index 3af205e6a..d710aab46 100644 --- a/Source/cmGeneratorTarget_CompatibleInterface.cxx +++ b/Source/cmGeneratorTarget_CompatibleInterface.cxx @@ -393,7 +393,7 @@ bool getTypedProperty(cmGeneratorTarget const* tgt, const std::string& prop, cmGeneratorExpressionInterpreter* genexInterpreter) { - if (genexInterpreter == nullptr) { + if (!genexInterpreter) { return tgt->GetPropertyAsBool(prop); } @@ -408,7 +408,7 @@ const char* getTypedProperty( { cmValue value = tgt->GetProperty(prop); - if (genexInterpreter == nullptr) { + if (!genexInterpreter) { return value.GetCStr(); } @@ -422,7 +422,7 @@ std::string getTypedProperty( { cmValue value = tgt->GetProperty(prop); - if (genexInterpreter == nullptr) { + if (!genexInterpreter) { return valueAsString(value); } diff --git a/Source/cmGeneratorTarget_IncludeDirectories.cxx b/Source/cmGeneratorTarget_IncludeDirectories.cxx index 6db467f6a..a05b1a298 100644 --- a/Source/cmGeneratorTarget_IncludeDirectories.cxx +++ b/Source/cmGeneratorTarget_IncludeDirectories.cxx @@ -309,7 +309,7 @@ std::vector> cmGeneratorTarget::GetIncludeDirectories( this->GetLinkImplementationLibraries(config, UseTo::Compile)) { for (cmLinkImplItem const& lib : impl->Libraries) { std::string libDir; - if (lib.Target == nullptr) { + if (!lib.Target) { libDir = cmSystemTools::CollapseFullPath( lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); } else if (lib.Target->Target->IsFrameworkOnApple() || diff --git a/Source/cmGeneratorTarget_Options.cxx b/Source/cmGeneratorTarget_Options.cxx index f77ef7290..d8b3eb32d 100644 --- a/Source/cmGeneratorTarget_Options.cxx +++ b/Source/cmGeneratorTarget_Options.cxx @@ -7,6 +7,7 @@ #include "cmConfigure.h" #include +#include #include #include #include @@ -91,10 +92,16 @@ void processOptions(cmGeneratorTarget const* tgt, } } +enum class NestedLinkerFlags +{ + PreserveAsSpelled, + Normalize, +}; + std::vector> wrapOptions( std::vector& options, const cmListFileBacktrace& bt, const std::vector& wrapperFlag, const std::string& wrapperSep, - bool concatFlagAndArgs) + bool concatFlagAndArgs, NestedLinkerFlags nestedLinkerFlags) { std::vector> result; @@ -111,6 +118,48 @@ std::vector> wrapOptions( return result; } + auto insertWrapped = [&](std::vector& opts) { + if (!wrapperSep.empty()) { + if (concatFlagAndArgs) { + // insert flag elements except last one + for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { + result.emplace_back(*i, bt); + } + // concatenate last flag element and all list values + // in one option + result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); + } else { + for (std::string const& i : wrapperFlag) { + result.emplace_back(i, bt); + } + // concatenate all list values in one option + result.emplace_back(cmJoin(opts, wrapperSep), bt); + } + } else { + // prefix each element of list with wrapper + if (concatFlagAndArgs) { + std::transform(opts.begin(), opts.end(), opts.begin(), + [&wrapperFlag](std::string const& o) -> std::string { + return wrapperFlag.back() + o; + }); + } + for (std::string& o : opts) { + for (auto i = wrapperFlag.begin(), + e = concatFlagAndArgs ? wrapperFlag.end() - 1 + : wrapperFlag.end(); + i != e; ++i) { + result.emplace_back(*i, bt); + } + result.emplace_back(std::move(o), bt); + } + } + }; + + if (nestedLinkerFlags == NestedLinkerFlags::PreserveAsSpelled) { + insertWrapped(options); + return result; + } + for (std::vector::size_type index = 0; index < options.size(); index++) { if (cmHasLiteralPrefix(options[index], "LINKER:")) { @@ -154,40 +203,7 @@ std::vector> wrapOptions( continue; } - if (!wrapperSep.empty()) { - if (concatFlagAndArgs) { - // insert flag elements except last one - for (auto i = wrapperFlag.begin(); i != wrapperFlag.end() - 1; ++i) { - result.emplace_back(*i, bt); - } - // concatenate last flag element and all list values - // in one option - result.emplace_back(wrapperFlag.back() + cmJoin(opts, wrapperSep), bt); - } else { - for (std::string const& i : wrapperFlag) { - result.emplace_back(i, bt); - } - // concatenate all list values in one option - result.emplace_back(cmJoin(opts, wrapperSep), bt); - } - } else { - // prefix each element of list with wrapper - if (concatFlagAndArgs) { - std::transform(opts.begin(), opts.end(), opts.begin(), - [&wrapperFlag](std::string const& o) -> std::string { - return wrapperFlag.back() + o; - }); - } - for (std::string& o : opts) { - for (auto i = wrapperFlag.begin(), - e = concatFlagAndArgs ? wrapperFlag.end() - 1 - : wrapperFlag.end(); - i != e; ++i) { - result.emplace_back(*i, bt); - } - result.emplace_back(std::move(o), bt); - } - } + insertWrapped(opts); } return result; } @@ -484,8 +500,9 @@ std::vector> cmGeneratorTarget::GetLinkOptions( // host link options must be wrapped std::vector options; cmSystemTools::ParseUnixCommandLine(it->Value.c_str(), options); - auto hostOptions = wrapOptions(options, it->Backtrace, wrapperFlag, - wrapperSep, concatFlagAndArgs); + auto hostOptions = + wrapOptions(options, it->Backtrace, wrapperFlag, wrapperSep, + concatFlagAndArgs, NestedLinkerFlags::Normalize); it = result.erase(it); // some compilers (like gcc 4.8 or Intel 19.0 or XLC 16) do not respect // C++11 standard: 'std::vector::insert()' do not returns an iterator, @@ -534,12 +551,11 @@ std::vector>& cmGeneratorTarget::ResolveLinkerWrapper( const std::string SHELL{ "SHELL:" }; const std::string LINKER_SHELL = LINKER + SHELL; - std::vector>::iterator entry; - while ((entry = std::find_if(result.begin(), result.end(), - [&LINKER](BT const& item) -> bool { - return item.Value.compare(0, LINKER.length(), - LINKER) == 0; - })) != result.end()) { + for (auto entry = result.begin(); entry != result.end(); ++entry) { + if (entry->Value.compare(0, LINKER.length(), LINKER) != 0) { + continue; + } + std::string value = std::move(entry->Value); cmListFileBacktrace bt = std::move(entry->Backtrace); entry = result.erase(entry); @@ -569,15 +585,19 @@ std::vector>& cmGeneratorTarget::ResolveLinkerWrapper( return result; } - std::vector> options = wrapOptions( - linkerOptions, bt, wrapperFlag, wrapperSep, concatFlagAndArgs); + // Very old versions of the C++ standard library return void for insert, so + // can't use it to get the new iterator + const auto index = entry - result.begin(); + std::vector> options = + wrapOptions(linkerOptions, bt, wrapperFlag, wrapperSep, + concatFlagAndArgs, NestedLinkerFlags::PreserveAsSpelled); if (joinItems) { - result.insert(entry, - cmJoin(cmRange( - options.cbegin(), options.cend()), - " "_s)); + result.insert( + entry, cmJoin(cmMakeRange(options.begin(), options.end()), " "_s)); + entry = std::next(result.begin(), index); } else { result.insert(entry, options.begin(), options.end()); + entry = std::next(result.begin(), index + options.size() - 1); } } return result; diff --git a/Source/cmGeneratorTarget_TransitiveProperty.cxx b/Source/cmGeneratorTarget_TransitiveProperty.cxx index ac929ebc6..8c2b8a96b 100644 --- a/Source/cmGeneratorTarget_TransitiveProperty.cxx +++ b/Source/cmGeneratorTarget_TransitiveProperty.cxx @@ -98,6 +98,15 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty( std::string cmGeneratorTarget::EvaluateInterfaceProperty( std::string const& prop, cmGeneratorExpressionContext* context, cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage) const +{ + return EvaluateInterfaceProperty(prop, context, dagCheckerParent, usage, + TransitiveClosure::Inherit); +} + +std::string cmGeneratorTarget::EvaluateInterfaceProperty( + std::string const& prop, cmGeneratorExpressionContext* context, + cmGeneratorExpressionDAGChecker* dagCheckerParent, UseTo usage, + TransitiveClosure closure) const { std::string result; @@ -106,12 +115,15 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty( return result; } + auto const dagClosure = closure == TransitiveClosure::Inherit + ? cmGeneratorExpressionDAGChecker::INHERIT + : cmGeneratorExpressionDAGChecker::SUBGRAPH; // Evaluate $ as if it were compiled. This is // a subset of TargetPropertyNode::Evaluate without stringify/parse steps // but sufficient for transitive interface properties. cmGeneratorExpressionDAGChecker dagChecker( context->Backtrace, this, prop, nullptr, dagCheckerParent, - this->LocalGenerator, context->Config); + this->LocalGenerator, context->Config, dagClosure); switch (dagChecker.Check()) { case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: dagChecker.ReportError( diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 019271bca..e3a38b016 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -32,7 +32,9 @@ #include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" +#include "cmCustomCommandTypes.h" #include "cmDuration.h" +#include "cmExperimental.h" #include "cmExportBuildFileGenerator.h" #include "cmExternalMakefileProjectGenerator.h" #include "cmGeneratedFileStream.h" @@ -68,6 +70,8 @@ # include "cmQtAutoGenGlobalInitializer.h" #endif +class cmListFileBacktrace; + const std::string kCMAKE_PLATFORM_INFO_INITIALIZED = "CMAKE_PLATFORM_INFO_INITIALIZED"; @@ -135,6 +139,13 @@ cmGlobalGenerator::cmGlobalGenerator(cmake* cm) cm->GetState()->SetWatcomWMake(false); cm->GetState()->SetWindowsShell(false); cm->GetState()->SetWindowsVSIDE(false); + +#if !defined(CMAKE_BOOTSTRAP) + Json::StreamWriterBuilder wbuilder; + wbuilder["indentation"] = "\t"; + this->JsonWriter = + std::unique_ptr(wbuilder.newStreamWriter()); +#endif } cmGlobalGenerator::~cmGlobalGenerator() @@ -524,6 +535,9 @@ bool cmGlobalGenerator::CheckLanguages( // - Make sure the compiler works with a try compile if // CMakeDetermine(LANG) was loaded // +// CMake(LANG)LinkerInformation.cmake +// - loads Platform/Linker/${CMAKE_SYSTEM_NAME}-${LINKER}.cmake +// // Now load a few files that can override values set in any of the above // (PROJECTNAME)Compatibility.cmake // - load any backwards compatibility stuff for current project @@ -948,6 +962,28 @@ void cmGlobalGenerator::EnableLanguage( } } // end if in try compile } // end need test language + + // load linker configuration, if required + if (mf->GetDefinition(cmStrCat("CMAKE_", lang, "_USE_LINKER_INFORMATION")) + .IsOn()) { + std::string langLinkerLoadedVar = + cmStrCat("CMAKE_", lang, "_LINKER_INFORMATION_LOADED"); + if (!mf->GetDefinition(langLinkerLoadedVar)) { + fpath = cmStrCat("CMake", lang, "LinkerInformation.cmake"); + std::string informationFile = mf->GetModulesFile(fpath); + if (informationFile.empty()) { + informationFile = mf->GetModulesFile(cmStrCat("Internal/", fpath)); + } + if (informationFile.empty()) { + cmSystemTools::Error( + cmStrCat("Could not find cmake module file: ", fpath)); + } else if (!mf->ReadListFile(informationFile)) { + cmSystemTools::Error(cmStrCat( + "Could not process cmake module file: ", informationFile)); + } + } + } + // Store the shared library flags so that we can satisfy CMP0018 std::string sharedLibFlagsVar = cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS"); @@ -1398,6 +1434,8 @@ void cmGlobalGenerator::Configure() } } + this->ReserveGlobalTargetCodegen(); + // update the cache entry for the number of local generators, this is used // for progress this->GetCMakeInstance()->AddCacheEntry( @@ -1541,6 +1579,10 @@ bool cmGlobalGenerator::Compute() } this->FinalizeTargetConfiguration(); + if (!this->AddBuildDatabaseTargets()) { + return false; + } + this->CreateGenerationObjects(); // at this point this->LocalGenerators has been filled, @@ -1599,6 +1641,13 @@ bool cmGlobalGenerator::Compute() return false; } + // Perform after-generator-target generator actions. These involve collecting + // information gathered during the construction of generator targets. + for (unsigned int i = 0; i < this->Makefiles.size(); ++i) { + this->Makefiles[i]->GenerateAfterGeneratorTargets( + *this->LocalGenerators[i]); + } + // Add generator specific helper commands for (const auto& localGen : this->LocalGenerators) { localGen->AddHelperCommands(); @@ -1756,6 +1805,30 @@ void cmGlobalGenerator::Generate() } } +#if !defined(CMAKE_BOOTSTRAP) +void cmGlobalGenerator::WriteJsonContent(const std::string& path, + const Json::Value& value) const +{ + cmsys::ofstream ftmp(path.c_str()); + this->JsonWriter->write(value, &ftmp); + ftmp << '\n'; + ftmp.close(); +} + +void cmGlobalGenerator::WriteInstallJson() const +{ + if (this->GetCMakeInstance()->GetState()->GetGlobalPropertyAsBool( + "INSTALL_PARALLEL")) { + Json::Value index(Json::objectValue); + index["InstallScripts"] = Json::arrayValue; + for (const auto& file : this->InstallScripts) { + index["InstallScripts"].append(file); + } + this->WriteJsonContent("CMakeFiles/InstallScripts.json", index); + } +} +#endif + bool cmGlobalGenerator::ComputeTargetDepends() { cmComputeTargetDepends ctd(this); @@ -2775,6 +2848,20 @@ bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, reason); } +bool cmGlobalGenerator::CheckCMP0037Prefix(std::string const& targetPrefix, + std::string const& reason) const +{ + bool ret = true; + for (auto const& tgtPair : this->TargetSearchIndex) { + if (cmHasPrefix(tgtPair.first, targetPrefix) && + !RaiseCMP0037Message(this->GetCMakeInstance(), tgtPair.second, + tgtPair.first, reason)) { + ret = false; + } + } + return ret; +} + void cmGlobalGenerator::CreateDefaultGlobalTargets( std::vector& targets) { @@ -2914,6 +3001,53 @@ void cmGlobalGenerator::AddGlobalTarget_Test( targets.push_back(std::move(gti)); } +void cmGlobalGenerator::ReserveGlobalTargetCodegen() +{ + // Read the policy value at the end of the top-level CMakeLists.txt file + // since it's a global policy that affects the whole project. + auto& mf = this->Makefiles[0]; + const auto policyStatus = mf->GetPolicyStatus(cmPolicies::CMP0171); + + this->AllowGlobalTargetCodegen = (policyStatus == cmPolicies::NEW); + + cmTarget* tgt = this->FindTarget("codegen"); + if (!tgt) { + return; + } + + MessageType messageType = MessageType::AUTHOR_WARNING; + std::ostringstream e; + bool issueMessage = false; + switch (policyStatus) { + case cmPolicies::WARN: + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0171) << "\n"; + issueMessage = true; + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + issueMessage = true; + messageType = MessageType::FATAL_ERROR; + break; + } + if (issueMessage) { + e << "The target name \"codegen\" is reserved."; + this->GetCMakeInstance()->IssueMessage(messageType, e.str(), + tgt->GetBacktrace()); + if (messageType == MessageType::FATAL_ERROR) { + cmSystemTools::SetFatalErrorOccurred(); + return; + } + } +} + +bool cmGlobalGenerator::CheckCMP0171() const +{ + return this->AllowGlobalTargetCodegen; +} + void cmGlobalGenerator::AddGlobalTarget_EditCache( std::vector& targets) const { @@ -3064,7 +3198,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( // install_strip const char* install_strip = this->GetInstallStripTargetName(); - if ((install_strip != nullptr) && (mf->IsSet("CMAKE_STRIP"))) { + if (install_strip && mf->IsSet("CMAKE_STRIP")) { gti.Name = install_strip; gti.Message = "Installing the project stripped..."; gti.UsesTerminal = true; @@ -3080,6 +3214,205 @@ void cmGlobalGenerator::AddGlobalTarget_Install( } } +class ModuleCompilationDatabaseCommandAction +{ +public: + ModuleCompilationDatabaseCommandAction( + std::string output, std::function()> inputs) + : Output(std::move(output)) + , Inputs(std::move(inputs)) + { + } + void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr cc); + +private: + std::string const Output; + std::function()> const Inputs; +}; + +void ModuleCompilationDatabaseCommandAction::operator()( + cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr cc) +{ + auto inputs = this->Inputs(); + + cmCustomCommandLines command_lines; + cmCustomCommandLine command_line; + { + command_line.emplace_back(cmSystemTools::GetCMakeCommand()); + command_line.emplace_back("-E"); + command_line.emplace_back("cmake_module_compile_db"); + command_line.emplace_back("merge"); + command_line.emplace_back("-o"); + command_line.emplace_back(this->Output); + for (auto const& input : inputs) { + command_line.emplace_back(input); + } + } + command_lines.emplace_back(std::move(command_line)); + + cc->SetBacktrace(lfbt); + cc->SetCommandLines(command_lines); + cc->SetWorkingDirectory(lg.GetBinaryDirectory().c_str()); + cc->SetDependsExplicitOnly(true); + cc->SetOutputs(this->Output); + if (!inputs.empty()) { + cc->SetMainDependency(inputs[0]); + } + cc->SetDepends(inputs); + detail::AddCustomCommandToOutput(lg, cmCommandOrigin::Generator, + std::move(cc), false); +} + +class ModuleCompilationDatabaseTargetAction +{ +public: + ModuleCompilationDatabaseTargetAction(std::string output, cmTarget* target) + : Output(std::move(output)) + , Target(target) + { + } + void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr cc); + +private: + std::string const Output; + cmTarget* const Target; +}; + +void ModuleCompilationDatabaseTargetAction::operator()( + cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + std::unique_ptr cc) +{ + cc->SetBacktrace(lfbt); + cc->SetWorkingDirectory(lg.GetBinaryDirectory().c_str()); + std::vector target_inputs; + target_inputs.emplace_back(this->Output); + cc->SetDepends(target_inputs); + detail::AddUtilityCommand(lg, cmCommandOrigin::Generator, this->Target, + std::move(cc)); +} + +void cmGlobalGenerator::AddBuildDatabaseFile(std::string const& lang, + std::string const& config, + std::string const& path) +{ + if (!config.empty()) { + this->PerConfigModuleDbs[config][lang].push_back(path); + } + this->PerLanguageModuleDbs[lang].push_back(path); +} + +bool cmGlobalGenerator::AddBuildDatabaseTargets() +{ + auto& mf = this->Makefiles[0]; + if (!mf->IsOn("CMAKE_EXPORT_BUILD_DATABASE")) { + return true; + } + if (!cmExperimental::HasSupportEnabled( + *mf.get(), cmExperimental::Feature::ExportBuildDatabase)) { + return {}; + } + + static const auto reservedTargets = { "cmake_build_database" }; + for (auto const& target : reservedTargets) { + if (!this->CheckCMP0037(target, + "when exporting build databases are enabled")) { + return false; + } + } + static const auto reservedPrefixes = { "cmake_build_database-" }; + for (auto const& prefix : reservedPrefixes) { + if (!this->CheckCMP0037Prefix( + prefix, "when exporting build databases are enabled")) { + return false; + } + } + + if (!this->SupportsBuildDatabase()) { + return true; + } + + auto configs = mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + + static cm::static_string_view TargetPrefix = "cmake_build_database"_s; + auto AddMergeTarget = + [&mf](std::string const& name, const char* comment, + std::string const& output, + std::function()> inputs) { + // Add the custom command. + { + ModuleCompilationDatabaseCommandAction action{ output, + std::move(inputs) }; + auto cc = cm::make_unique(); + cc->SetComment(comment); + mf->AddGeneratorAction( + std::move(cc), action, + cmMakefile::GeneratorActionWhen::AfterGeneratorTargets); + } + + // Add a custom target with the given name. + { + cmTarget* target = mf->AddNewUtilityTarget(name, true); + ModuleCompilationDatabaseTargetAction action{ output, target }; + auto cc = cm::make_unique(); + mf->AddGeneratorAction(std::move(cc), action); + } + }; + + std::string module_languages[] = { "CXX" }; + + // Add per-configuration targets. + for (auto const& config : configs) { + // Add per-language targets. + std::vector all_config_paths; + for (auto const& lang : module_languages) { + auto comment = cmStrCat("Combining module command databases for ", lang, + " and ", config); + auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_", + lang, '_', config, ".json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget(cmStrCat(TargetPrefix, '-', lang, '-', config), + comment.c_str(), output, [this, config, lang]() { + return this->PerConfigModuleDbs[config][lang]; + }); + all_config_paths.emplace_back(std::move(output)); + } + + // Add the overall target. + auto comment = cmStrCat("Combining module command databases for ", config); + auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_", + config, ".json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget(cmStrCat(TargetPrefix, '-', config), comment.c_str(), + output, [all_config_paths]() { return all_config_paths; }); + } + + // NMC considerations + // Add per-language targets. + std::vector all_config_paths; + for (auto const& lang : module_languages) { + auto comment = cmStrCat("Combining module command databases for ", lang); + auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database_", + lang, ".json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget( + cmStrCat(TargetPrefix, '-', lang), comment.c_str(), output, + [this, lang]() { return this->PerLanguageModuleDbs[lang]; }); + all_config_paths.emplace_back(std::move(output)); + } + + // Add the overall target. + auto const* comment = "Combining all module command databases"; + auto output = cmStrCat(mf->GetHomeOutputDirectory(), "/build_database.json"); + mf->GetOrCreateGeneratedSource(output); + AddMergeTarget(std::string(TargetPrefix), comment, output, + [all_config_paths]() { return all_config_paths; }); + + return true; +} + std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const { cmValue prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty( @@ -3683,3 +4016,8 @@ cmGlobalGenerator::StripCommandStyle cmGlobalGenerator::GetStripCommandStyle( return StripCommandStyle::Default; #endif } + +void cmGlobalGenerator::AddInstallScript(std::string const& file) +{ + this->InstallScripts.push_back(file); +} diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 1ca02d9a8..a865adbe3 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -91,6 +91,9 @@ struct GeneratedMakeCommand bool RequiresOutputForward = false; }; } +namespace Json { +class StreamWriter; +} /** \class cmGlobalGenerator * \brief Responsible for overseeing the generation process for the entire tree @@ -168,6 +171,11 @@ public: return false; } + virtual bool SupportsBuildDatabase() const { return false; } + bool AddBuildDatabaseTargets(); + void AddBuildDatabaseFile(std::string const& lang, std::string const& config, + std::string const& path); + virtual bool IsGNUMakeJobServerAware() const { return false; } bool Compute(); @@ -591,6 +599,18 @@ public: virtual bool SupportsCrossConfigs() const { return false; } virtual bool SupportsDefaultConfigs() const { return false; } + virtual std::string ConvertToOutputPath(std::string path) const + { + return path; + } + virtual std::string GetConfigDirectory(std::string const& config) const + { + if (!this->IsMultiConfig() || config.empty()) { + return {}; + } + return cmStrCat('/', config); + } + static std::string EscapeJSON(const std::string& s); void ProcessEvaluationFiles(); @@ -653,6 +673,10 @@ public: virtual std::string& EncodeLiteral(std::string& lit) { return lit; } + bool CheckCMP0171() const; + + void AddInstallScript(std::string const& file); + protected: // for a project collect all its targets by following depend // information, and also collect all the targets @@ -672,6 +696,12 @@ protected: virtual bool ComputeTargetDepends(); +#if !defined(CMAKE_BOOTSTRAP) + void WriteJsonContent(const std::string& fname, + const Json::Value& value) const; + void WriteInstallJson() const; +#endif + virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; bool ApplyCXXStdTargets(); @@ -719,6 +749,8 @@ protected: void AddGlobalTarget_Install(std::vector& targets); void CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf); + void ReserveGlobalTargetCodegen(); + std::string FindMakeProgramFile; std::string ConfiguredFilesPath; cmake* CMakeInstance; @@ -786,6 +818,10 @@ private: std::map LanguageToLinkerPreference; std::map LanguageToOriginalSharedLibFlags; +#if !defined(CMAKE_BOOTSTRAP) + std::unique_ptr JsonWriter; +#endif + #ifdef __APPLE__ std::map StripCommandStyleMap; #endif @@ -840,6 +876,8 @@ private: bool CheckCMP0037(std::string const& targetName, std::string const& reason) const; + bool CheckCMP0037Prefix(std::string const& targetPrefix, + std::string const& reason) const; void IndexMakefile(cmMakefile* mf); void IndexLocalGenerator(cmLocalGenerator* lg); @@ -878,11 +916,20 @@ private: std::map RuntimeDependencySetsByName; + std::vector InstallScripts; + #if !defined(CMAKE_BOOTSTRAP) // Pool of file locks cmFileLockPool FileLockPool; #endif + using PerLanguageModuleDatabases = + std::map>; + using PerConfigModuleDatabases = + std::map; + PerConfigModuleDatabases PerConfigModuleDbs; + PerLanguageModuleDatabases PerLanguageModuleDbs; + protected: float FirstTimeProgress; bool NeedSymbolicMark; @@ -891,4 +938,5 @@ protected: bool ToolSupportsColor; bool InstallTargetEnabled; bool ConfigureDoneCMP0026AndCMP0024; + bool AllowGlobalTargetCodegen; }; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 96c8f25af..17e9c440a 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -25,6 +25,7 @@ #include "cmsys/FStream.hxx" +#include "cmCustomCommand.h" #include "cmCxxModuleMapper.h" #include "cmDyndepCollation.h" #include "cmFortranParser.h" @@ -43,6 +44,7 @@ #include "cmOutputConverter.h" #include "cmRange.h" #include "cmScanDepFormat.h" +#include "cmSourceFile.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -571,13 +573,7 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) cm->GetState()->SetWindowsShell(true); // Attempt to use full path to COMSPEC, default "cmd.exe" - std::string comspec; - if (cmSystemTools::GetEnv("COMSPEC", comspec) && - cmSystemTools::FileIsFullPath(comspec)) { - this->Comspec = comspec; - } else { - this->Comspec = "cmd.exe"; - } + this->Comspec = cmSystemTools::GetComspec(); #endif cm->GetState()->SetNinja(true); this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; @@ -1627,6 +1623,90 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) std::map dirTargets = this->ComputeDirectoryTargets(); + // Codegen target + if (this->CheckCMP0171()) { + for (auto const& it : dirTargets) { + cmNinjaBuild build("phony"); + cmGlobalNinjaGenerator::WriteDivider(os); + std::string const& currentBinaryDir = it.first; + DirectoryTarget const& dt = it.second; + std::vector configs = + dt.LG->GetMakefile()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); + + // Setup target + cmNinjaDeps configDeps; + build.Comment = cmStrCat("Folder: ", currentBinaryDir); + build.Outputs.emplace_back(); + std::string const buildDirAllTarget = + this->ConvertToNinjaPath(cmStrCat(currentBinaryDir, "/codegen")); + + cmNinjaDeps& explicitDeps = build.ExplicitDeps; + + for (auto const& config : configs) { + explicitDeps.clear(); + + for (DirectoryTarget::Target const& t : dt.Targets) { + if (this->IsExcludedFromAllInConfig(t, config)) { + continue; + } + + std::vector customCommandSources; + t.GT->GetCustomCommands(customCommandSources, config); + for (cmSourceFile const* sf : customCommandSources) { + cmCustomCommand const* cc = sf->GetCustomCommand(); + if (cc->GetCodegen()) { + auto const& outputs = cc->GetOutputs(); + + std::transform(outputs.begin(), outputs.end(), + std::back_inserter(explicitDeps), + this->MapToNinjaPath()); + } + } + } + + build.Outputs.front() = this->BuildAlias(buildDirAllTarget, config); + // Write target + this->WriteBuild(this->EnableCrossConfigBuild() && + this->CrossConfigs.count(config) + ? os + : *this->GetImplFileStream(config), + build); + } + + // Add shortcut target + if (this->IsMultiConfig()) { + for (auto const& config : configs) { + build.ExplicitDeps = { this->BuildAlias(buildDirAllTarget, config) }; + build.Outputs.front() = buildDirAllTarget; + this->WriteBuild(*this->GetConfigFileStream(config), build); + } + + if (!this->DefaultFileConfig.empty()) { + build.ExplicitDeps.clear(); + for (auto const& config : this->DefaultConfigs) { + build.ExplicitDeps.push_back( + this->BuildAlias(buildDirAllTarget, config)); + } + build.Outputs.front() = buildDirAllTarget; + this->WriteBuild(*this->GetDefaultFileStream(), build); + } + } + + // Add target for all configs + if (this->EnableCrossConfigBuild()) { + build.ExplicitDeps.clear(); + for (auto const& config : this->CrossConfigs) { + build.ExplicitDeps.push_back( + this->BuildAlias(buildDirAllTarget, config)); + } + build.Outputs.front() = this->BuildAlias(buildDirAllTarget, "codegen"); + this->WriteBuild(os, build); + } + } + } + + // All target for (auto const& it : dirTargets) { cmNinjaBuild build("phony"); cmGlobalNinjaGenerator::WriteDivider(os); @@ -1988,7 +2068,7 @@ std::string cmGlobalNinjaGenerator::CMakeCmd() const std::string cmGlobalNinjaGenerator::NinjaCmd() const { const auto& lgen = this->LocalGenerators[0]; - if (lgen != nullptr) { + if (lgen) { return lgen->ConvertToOutputFormat(this->NinjaCommand, cmOutputConverter::SHELL); } @@ -3069,6 +3149,11 @@ bool cmGlobalNinjaGenerator::IsSingleConfigUtility( !this->PerConfigUtilityTargets.count(target->GetName()); } +std::string cmGlobalNinjaGenerator::ConvertToOutputPath(std::string path) const +{ + return this->ConvertToNinjaPath(path); +} + const char* cmGlobalNinjaMultiGenerator::NINJA_COMMON_FILE = "CMakeFiles/common.ninja"; const char* cmGlobalNinjaMultiGenerator::NINJA_FILE_EXTENSION = ".ninja"; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 6ad38fbc3..69b2361dc 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -483,6 +483,9 @@ public: bool IsSingleConfigUtility(cmGeneratorTarget const* target) const; bool CheckCxxModuleSupport(CxxModuleSupportQuery query) override; + bool SupportsBuildDatabase() const override { return true; } + + std::string ConvertToOutputPath(std::string path) const override; protected: std::vector const& GetConfigNames() const; diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 56748a529..38cc4f662 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -23,6 +23,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmTargetDepend.h" #include "cmValue.h" #include "cmake.h" @@ -438,6 +439,10 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2( // Write directory-level rules for "all". this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "all", true, false); + // Write directory-level rules for "codegen". + this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "codegen", true, + false); + // Write directory-level rules for "preinstall". this->WriteDirectoryRule2(ruleFileStream, rootLG, dt, "preinstall", true, true); @@ -765,6 +770,17 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( depends, commands, true); } + // add the codegen rule + localName = lg.GetRelativeTargetDirectory(gtarget.get()); + depends.clear(); + commands.clear(); + makeTargetName = cmStrCat(localName, "/codegen"); + commands.push_back( + lg.GetRecursiveMakeCall(makefileName, makeTargetName)); + this->AppendCodegenTargetDepends(depends, gtarget.get()); + rootLG.WriteMakeRule(ruleFileStream, "codegen rule for target.", + makeTargetName, depends, commands, true); + // add the clean rule localName = lg.GetRelativeTargetDirectory(gtarget.get()); makeTargetName = cmStrCat(localName, "/clean"); @@ -893,6 +909,29 @@ void cmGlobalUnixMakefileGenerator3::AppendGlobalTargetDepends( } } +void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends( + std::vector& depends, cmGeneratorTarget* target) +{ + const std::set& codegen_depends = + target->Target->GetCodegenDeps(); + + for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) { + // Create the target-level dependency. + cmGeneratorTarget const* dep = i; + if (!dep->IsInBuildSystem()) { + continue; + } + if (codegen_depends.find(dep->GetName()) != codegen_depends.end()) { + cmLocalUnixMakefileGenerator3* lg3 = + static_cast(dep->GetLocalGenerator()); + std::string tgtName = cmStrCat( + lg3->GetRelativeTargetDirectory(const_cast(dep)), + "/all"); + depends.push_back(tgtName); + } + } +} + void cmGlobalUnixMakefileGenerator3::WriteHelpRule( std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg) { diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index ee783513b..d4fcf88a1 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -223,6 +223,9 @@ protected: void AppendGlobalTargetDepends(std::vector& depends, cmGeneratorTarget* target); + void AppendCodegenTargetDepends(std::vector& depends, + cmGeneratorTarget* target); + // Target name hooks for superclass. const char* GetAllTargetName() const override { return "all"; } const char* GetInstallTargetName() const override { return "install"; } diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 51d6d8aa7..f9abe29c1 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -516,13 +516,6 @@ bool cmGlobalVisualStudio10Generator::InitializeWindowsCE(cmMakefile* mf) this->DefaultPlatformToolset = this->SelectWindowsCEToolset(); - if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS12) { - // VS 12 .NET CF defaults to .NET framework 3.9 for Windows CE. - this->DefaultTargetFrameworkVersion = "v3.9"; - this->DefaultTargetFrameworkIdentifier = "WindowsEmbeddedCompact"; - this->DefaultTargetFrameworkTargetsVersion = "v8.0"; - } - return true; } @@ -1273,8 +1266,6 @@ std::string cmGlobalVisualStudio10Generator::Encoding() const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return "12.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "14.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx index 1f1a2c3e5..e5406dd42 100644 --- a/Source/cmGlobalVisualStudio12Generator.cxx +++ b/Source/cmGlobalVisualStudio12Generator.cxx @@ -9,131 +9,17 @@ #include #include "cmGlobalGenerator.h" -#include "cmGlobalGeneratorFactory.h" #include "cmGlobalVisualStudioGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -static const char vs12generatorName[] = "Visual Studio 12 2013"; - -// Map generator name without year to name with year. -static const char* cmVS12GenName(const std::string& name, std::string& genName) -{ - if (strncmp(name.c_str(), vs12generatorName, - sizeof(vs12generatorName) - 6) != 0) { - return nullptr; - } - const char* p = name.c_str() + sizeof(vs12generatorName) - 6; - if (cmHasLiteralPrefix(p, " 2013")) { - p += 5; - } - genName = std::string(vs12generatorName) + p; - return p; -} - -class cmGlobalVisualStudio12Generator::Factory - : public cmGlobalGeneratorFactory -{ -public: - std::unique_ptr CreateGlobalGenerator( - const std::string& name, bool allowArch, cmake* cm) const override - { - std::string genName; - const char* p = cmVS12GenName(name, genName); - if (!p) { - return std::unique_ptr(); - } - if (!*p) { - return std::unique_ptr( - new cmGlobalVisualStudio12Generator(cm, genName, "")); - } - if (!allowArch || *p++ != ' ') { - return std::unique_ptr(); - } - if (strcmp(p, "Win64") == 0) { - return std::unique_ptr( - new cmGlobalVisualStudio12Generator(cm, genName, "x64")); - } - if (strcmp(p, "ARM") == 0) { - return std::unique_ptr( - new cmGlobalVisualStudio12Generator(cm, genName, "ARM")); - } - return std::unique_ptr(); - } - - cmDocumentationEntry GetDocumentation() const override - { - return { cmStrCat(vs12generatorName, " [arch]"), - "Deprecated. Generates Visual Studio 2013 project files. " - "Optional [arch] can be \"Win64\" or \"ARM\"." }; - } - - std::vector GetGeneratorNames() const override - { - std::vector names; - names.push_back(vs12generatorName); - return names; - } - - std::vector GetGeneratorNamesWithPlatform() const override - { - std::vector names; - names.emplace_back(cmStrCat(vs12generatorName, " ARM")); - names.emplace_back(cmStrCat(vs12generatorName, " Win64")); - return names; - } - - bool SupportsToolset() const override { return true; } - bool SupportsPlatform() const override { return true; } - - std::vector GetKnownPlatforms() const override - { - std::vector platforms; - platforms.emplace_back("x64"); - platforms.emplace_back("Win32"); - platforms.emplace_back("ARM"); - return platforms; - } - - std::string GetDefaultPlatformName() const override { return "Win32"; } -}; - -std::unique_ptr -cmGlobalVisualStudio12Generator::NewFactory() -{ - return std::unique_ptr(new Factory); -} - cmGlobalVisualStudio12Generator::cmGlobalVisualStudio12Generator( cmake* cm, const std::string& name, std::string const& platformInGeneratorName) : cmGlobalVisualStudio11Generator(cm, name, platformInGeneratorName) { - std::string vc12Express; - this->ExpressEdition = cmSystemTools::ReadRegistryValue( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\12.0\\Setup\\VC;" - "ProductDir", - vc12Express, cmSystemTools::KeyWOW64_32); - this->DefaultPlatformToolset = "v120"; - this->DefaultCLFlagTableName = "v12"; - this->DefaultCSharpFlagTableName = "v12"; - this->DefaultLibFlagTableName = "v12"; - this->DefaultLinkFlagTableName = "v12"; - this->DefaultMasmFlagTableName = "v12"; - this->DefaultRCFlagTableName = "v12"; - this->Version = VSVersion::VS12; -} - -bool cmGlobalVisualStudio12Generator::MatchesGeneratorName( - const std::string& name) const -{ - std::string genName; - if (cmVS12GenName(name, genName)) { - return genName == this->GetName(); - } - return false; } bool cmGlobalVisualStudio12Generator::ProcessGeneratorToolsetField( diff --git a/Source/cmGlobalVisualStudio12Generator.h b/Source/cmGlobalVisualStudio12Generator.h index a84756e5f..edb86e2ab 100644 --- a/Source/cmGlobalVisualStudio12Generator.h +++ b/Source/cmGlobalVisualStudio12Generator.h @@ -9,18 +9,12 @@ #include "cmGlobalVisualStudio11Generator.h" -class cmGlobalGeneratorFactory; class cmMakefile; class cmake; /** \class cmGlobalVisualStudio12Generator */ class cmGlobalVisualStudio12Generator : public cmGlobalVisualStudio11Generator { -public: - static std::unique_ptr NewFactory(); - - bool MatchesGeneratorName(const std::string& name) const override; - protected: cmGlobalVisualStudio12Generator(cmake* cm, const std::string& name, std::string const& platformInGeneratorName); @@ -41,8 +35,4 @@ protected: // of the toolset is installed bool IsWindowsPhoneToolsetInstalled() const; bool IsWindowsStoreToolsetInstalled() const; - -private: - class Factory; - friend class Factory; }; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 7e04b9f72..8c644abc6 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -309,26 +309,6 @@ void cmGlobalVisualStudio7Generator::Generate() this->CallVisualStudioMacro(MacroReload, GetSLNFile(this->LocalGenerators[0].get())); } - - if (this->Version == VSVersion::VS12 && - !this->CMakeInstance->GetIsInTryCompile()) { - std::string cmakeWarnVS12; - if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue( - "CMAKE_WARN_VS12")) { - this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS12"); - cmakeWarnVS12 = *cached; - } else { - cmSystemTools::GetEnv("CMAKE_WARN_VS12", cmakeWarnVS12); - } - if (cmakeWarnVS12.empty() || !cmIsOff(cmakeWarnVS12)) { - this->CMakeInstance->IssueMessage( - MessageType::WARNING, - "The \"Visual Studio 12 2013\" generator is deprecated " - "and will be removed in a future version of CMake." - "\n" - "Add CMAKE_WARN_VS12=OFF to the cache to disable this warning."); - } - } } void cmGlobalVisualStudio7Generator::OutputSLNFile( diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index f96a84c88..5ff950627 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -104,8 +104,6 @@ std::string const& cmGlobalVisualStudioGenerator::GetPlatformName() const const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return "12.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "14.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -125,14 +123,6 @@ void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout) fout << '\n'; switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; - if (this->ExpressEdition) { - fout << "# Visual Studio Express 2013 for Windows Desktop\n"; - } else { - fout << "# Visual Studio 2013\n"; - } - break; case cmGlobalVisualStudioGenerator::VSVersion::VS14: // Visual Studio 14 writes .sln format 12.00 fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 4746f1342..a53b3bd2e 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -34,8 +34,6 @@ public: /** Known versions of Visual Studio. */ enum class VSVersion : uint16_t { - VS12 = 120, - /* VS13 = 130 was skipped */ VS14 = 140, VS15 = 150, VS16 = 160, @@ -185,9 +183,6 @@ protected: VSDependMap VSTargetDepends; void ComputeVSTargetDepends(cmGeneratorTarget*); - bool CheckTargetLinks(cmGeneratorTarget& target, const std::string& name); - std::string GetUtilityForTarget(cmGeneratorTarget& target, - const std::string&); virtual std::string WriteUtilityDepend(cmGeneratorTarget const*) = 0; std::string GetUtilityDepend(const cmGeneratorTarget* target); using UtilityDependsMap = std::map; diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index b4683aa05..14460fd76 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -56,7 +56,7 @@ static bool VSIsArm64Host() USHORT processMachine; USHORT nativeMachine; - return s_IsWow64Process2Impl != nullptr && + return s_IsWow64Process2Impl && s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine, &nativeMachine) && nativeMachine == IMAGE_FILE_MACHINE_ARM64; @@ -125,8 +125,6 @@ static unsigned int VSVersionToMajor( cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return 12; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return 14; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -143,8 +141,6 @@ static const char* VSVersionToToolset( cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return "v120"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "v140"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -161,8 +157,6 @@ static std::string VSVersionToMajorString( cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return "12"; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "14"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -179,8 +173,6 @@ static const char* VSVersionToAndroidToolset( cmGlobalVisualStudioGenerator::VSVersion v) { switch (v) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return ""; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "Clang_3_8"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -478,7 +470,6 @@ bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName( { std::string genName; switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: case cmGlobalVisualStudioGenerator::VSVersion::VS14: break; case cmGlobalVisualStudioGenerator::VSVersion::VS15: @@ -744,8 +735,6 @@ cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision() const { switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS12: - return ""; case cmGlobalVisualStudioGenerator::VSVersion::VS14: return "2.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS15: diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index aa948a514..7fa21d037 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -3994,7 +3994,6 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) { BuildObjectListOrString libSearchPaths(this, true); - std::string linkDirs; for (auto const& libDir : cli->GetDirectories()) { if (!libDir.empty() && libDir != "/usr/lib"_s) { cmPolicies::PolicyStatus cmp0142 = @@ -4012,6 +4011,16 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) for (auto& libDir : linkSearchPaths) { libSearchPaths.Add(this->XCodeEscapePath(libDir)); } + + // Add toolchain specified language link directories + std::string const& linkDirsString = + this->Makefiles.front()->GetSafeDefinition(cmStrCat( + "CMAKE_", cli->GetLinkLanguage(), "_STANDARD_LINK_DIRECTORIES")); + + for (const auto& libDir : cmList(linkDirsString)) { + libSearchPaths.Add(this->XCodeEscapePath(libDir)); + } + if (!libSearchPaths.IsEmpty()) { this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS", libSearchPaths.CreateList(), diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx index 6c3afefe7..55cc815aa 100644 --- a/Source/cmGraphVizWriter.cxx +++ b/Source/cmGraphVizWriter.cxx @@ -46,7 +46,7 @@ char const* const GRAPHVIZ_NODE_SHAPE_UTILITY = "box"; const char* getShapeForTarget(const cmLinkItem& item) { - if (item.Target == nullptr) { + if (!item.Target) { return GRAPHVIZ_NODE_SHAPE_LIBRARY_UNKNOWN; } @@ -473,7 +473,7 @@ bool cmGraphVizWriter::ItemExcluded(cmLinkItem const& item) return true; } - if (item.Target == nullptr) { + if (!item.Target) { return !this->GenerateForExternals; } diff --git a/Source/cmHexFileConverter.cxx b/Source/cmHexFileConverter.cxx index 2fa4b5505..54dfc9232 100644 --- a/Source/cmHexFileConverter.cxx +++ b/Source/cmHexFileConverter.cxx @@ -15,7 +15,7 @@ static unsigned int ChompStrlen(const char* line) { - if (line == nullptr) { + if (!line) { return 0; } unsigned int length = static_cast(strlen(line)); @@ -132,7 +132,7 @@ cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType( { char buf[1024]; FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); - if (inFile == nullptr) { + if (!inFile) { return Binary; } @@ -181,11 +181,11 @@ bool cmHexFileConverter::TryConvert(const std::string& inFileName, // try to open the file FILE* inFile = cmsys::SystemTools::Fopen(inFileName, "rb"); FILE* outFile = cmsys::SystemTools::Fopen(outFileName, "wb"); - if ((inFile == nullptr) || (outFile == nullptr)) { - if (inFile != nullptr) { + if (!inFile || !outFile) { + if (inFile) { fclose(inFile); } - if (outFile != nullptr) { + if (outFile) { fclose(outFile); } return false; @@ -194,7 +194,7 @@ bool cmHexFileConverter::TryConvert(const std::string& inFileName, // convert them line by line bool success = false; char buf[1024]; - while (fgets(buf, 1024, inFile) != nullptr) { + while (fgets(buf, 1024, inFile)) { if (type == MotorolaSrec) { success = ConvertMotorolaSrecLine(buf, outFile); } else if (type == IntelHex) { diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx index 49560546c..5ac2bd3db 100644 --- a/Source/cmIncludeCommand.cxx +++ b/Source/cmIncludeCommand.cxx @@ -20,6 +20,7 @@ bool cmIncludeCommand(std::vector const& args, { static std::map DeprecatedModules; if (DeprecatedModules.empty()) { + DeprecatedModules["CMakeFindFrameworks"] = cmPolicies::CMP0173; DeprecatedModules["Dart"] = cmPolicies::CMP0145; DeprecatedModules["Documentation"] = cmPolicies::CMP0106; DeprecatedModules["FindBoost"] = cmPolicies::CMP0167; diff --git a/Source/cmInstallAndroidMKExportGenerator.cxx b/Source/cmInstallAndroidMKExportGenerator.cxx new file mode 100644 index 000000000..b038ac169 --- /dev/null +++ b/Source/cmInstallAndroidMKExportGenerator.cxx @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallAndroidMKExportGenerator.h" + +#include + +#include + +#include "cmExportInstallAndroidMKGenerator.h" +#include "cmExportInstallFileGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +cmInstallAndroidMKExportGenerator::cmInstallAndroidMKExportGenerator( + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, cmListFileBacktrace backtrace) + : cmInstallExportGenerator(exportSet, std::move(destination), + std::move(filePermissions), configurations, + std::move(component), message, excludeFromAll, + std::move(filename), std::move(targetNamespace), + std::string{}, std::move(backtrace)) +{ + this->EFGen = cm::make_unique(this); +} + +cmInstallAndroidMKExportGenerator::~cmInstallAndroidMKExportGenerator() = + default; diff --git a/Source/cmInstallAndroidMKExportGenerator.h b/Source/cmInstallAndroidMKExportGenerator.h new file mode 100644 index 000000000..4ee80d4c6 --- /dev/null +++ b/Source/cmInstallAndroidMKExportGenerator.h @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallAndroidMKExportGenerator + * \brief Generate rules for creating Android .mk export files. + */ +class cmInstallAndroidMKExportGenerator : public cmInstallExportGenerator +{ +public: + cmInstallAndroidMKExportGenerator( + cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, cmListFileBacktrace backtrace); + cmInstallAndroidMKExportGenerator(cmInstallAndroidMKExportGenerator const&) = + delete; + ~cmInstallAndroidMKExportGenerator() override; + + cmInstallAndroidMKExportGenerator& operator=( + cmInstallAndroidMKExportGenerator const&) = delete; + + char const* InstallSubcommand() const override + { + return "EXPORT_ANDROID_MK"; + } +}; diff --git a/Source/cmInstallCMakeConfigExportGenerator.cxx b/Source/cmInstallCMakeConfigExportGenerator.cxx new file mode 100644 index 000000000..e247e5b44 --- /dev/null +++ b/Source/cmInstallCMakeConfigExportGenerator.cxx @@ -0,0 +1,43 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallCMakeConfigExportGenerator.h" + +#include + +#include + +#include "cmExportInstallCMakeConfigGenerator.h" +#include "cmExportInstallFileGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +cmInstallCMakeConfigExportGenerator::cmInstallCMakeConfigExportGenerator( + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, bool exportOld, + bool exportPackageDependencies, cmListFileBacktrace backtrace) + : cmInstallExportGenerator( + exportSet, std::move(destination), std::move(filePermissions), + configurations, std::move(component), message, excludeFromAll, + std::move(filename), std::move(targetNamespace), + std::move(cxxModulesDirectory), std::move(backtrace)) + , ExportOld(exportOld) + , ExportPackageDependencies(exportPackageDependencies) +{ + this->EFGen = cm::make_unique(this); +} + +cmInstallCMakeConfigExportGenerator::~cmInstallCMakeConfigExportGenerator() = + default; + +void cmInstallCMakeConfigExportGenerator::GenerateScript(std::ostream& os) +{ + auto* const efgen = + static_cast(this->EFGen.get()); + efgen->SetExportOld(this->ExportOld); + efgen->SetExportPackageDependencies(this->ExportPackageDependencies); + + this->cmInstallExportGenerator::GenerateScript(os); +} diff --git a/Source/cmInstallCMakeConfigExportGenerator.h b/Source/cmInstallCMakeConfigExportGenerator.h new file mode 100644 index 000000000..03296de07 --- /dev/null +++ b/Source/cmInstallCMakeConfigExportGenerator.h @@ -0,0 +1,42 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include +#include + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallCMakeConfigExportGenerator + * \brief Generate rules for creating CMake export files. + */ +class cmInstallCMakeConfigExportGenerator : public cmInstallExportGenerator +{ +public: + cmInstallCMakeConfigExportGenerator( + cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, + bool exportOld, bool exportPackageDependencies, + cmListFileBacktrace backtrace); + cmInstallCMakeConfigExportGenerator( + cmInstallCMakeConfigExportGenerator const&) = delete; + ~cmInstallCMakeConfigExportGenerator() override; + + cmInstallCMakeConfigExportGenerator& operator=( + cmInstallCMakeConfigExportGenerator const&) = delete; + + char const* InstallSubcommand() const override { return "EXPORT"; } + +protected: + void GenerateScript(std::ostream& os) override; + + bool const ExportOld; + bool const ExportPackageDependencies; +}; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 1567629af..9a797a773 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -20,21 +20,24 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" +#include "cmInstallAndroidMKExportGenerator.h" +#include "cmInstallCMakeConfigExportGenerator.h" #include "cmInstallCommandArguments.h" #include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" -#include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" #include "cmInstallFilesGenerator.h" #include "cmInstallGenerator.h" #include "cmInstallGetRuntimeDependenciesGenerator.h" #include "cmInstallImportedRuntimeArtifactsGenerator.h" +#include "cmInstallPackageInfoExportGenerator.h" #include "cmInstallRuntimeDependencySet.h" #include "cmInstallRuntimeDependencySetGenerator.h" #include "cmInstallScriptGenerator.h" @@ -134,6 +137,8 @@ public: const cmInstallCommandArguments* args) const; std::string GetManDestination(const cmInstallCommandArguments* args) const; std::string GetDocDestination(const cmInstallCommandArguments* args) const; + std::string GetProgramExecutablesDestination( + const cmInstallCommandArguments* args) const; std::string GetDestinationForType(const cmInstallCommandArguments* args, const std::string& type) const; @@ -289,7 +294,7 @@ void AddInstallRuntimeDependenciesGenerator( std::set const allowedTypes{ "BIN", "SBIN", "LIB", "INCLUDE", "SYSCONF", "SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA", "INFO", - "LOCALE", "MAN", "DOC", + "LOCALE", "MAN", "DOC", "LIBEXEC", }; template @@ -451,7 +456,8 @@ bool HandleTargetsMode(std::vector const& args, runtimeDependenciesArgVector; std::string runtimeDependencySetArg; std::vector unknownArgs; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); @@ -465,19 +471,31 @@ bool HandleTargetsMode(std::vector const& args, &unknownArgs) : RuntimeDependenciesArgs(); - cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments objectArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); - cmInstallCommandArguments bundleArgs(helper.DefaultComponentName); - cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName); - cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName); - cmInstallCommandArguments resourceArgs(helper.DefaultComponentName); + cmInstallCommandArguments archiveArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments objectArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments bundleArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments resourceArgs(helper.DefaultComponentName, + *helper.Makefile); cmInstallCommandIncludesArgument includesArgs; std::vector fileSetArgs( - argVectors.FileSets.size(), { helper.DefaultComponentName }); - cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName); + argVectors.FileSets.size(), + { cmInstallCommandFileSetArguments(helper.DefaultComponentName, + *helper.Makefile) }); + cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName, + *helper.Makefile); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -497,7 +515,8 @@ bool HandleTargetsMode(std::vector const& args, // cmArgumentParser::Bind() binds to a specific address, but the // objects in the vector can move around. So we parse in an object with a // fixed address and then copy the data into the vector. - cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName); + cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName, + *helper.Makefile); fileSetArg.Parse(argVectors.FileSets[i], &unknownArgs); fileSetArgs[i] = std::move(fileSetArg); } @@ -1308,16 +1327,21 @@ bool HandleImportedRuntimeArtifactsMode(std::vector const& args, ArgumentParser::MaybeEmpty> targetList; std::string runtimeDependencySetArg; std::vector unknownArgs; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("IMPORTED_RUNTIME_ARTIFACTS"_s, targetList) .Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); - cmInstallCommandArguments bundleArgs(helper.DefaultComponentName); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments bundleArgs(helper.DefaultComponentName, + *helper.Makefile); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME etc. @@ -1545,7 +1569,7 @@ bool HandleFilesMode(std::vector const& args, // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); ArgumentParser::MaybeEmpty> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector unknownArgs; @@ -1679,7 +1703,7 @@ bool HandleDirectoryMode(std::vector const& args, bool exclude_from_all = false; bool message_never = false; std::vector dirs; - const std::string* destination = nullptr; + cm::optional destination; std::string permissions_file; std::string permissions_dir; std::vector configurations; @@ -1837,7 +1861,33 @@ bool HandleDirectoryMode(std::vector const& args, } else if (doing == DoingConfigurations) { configurations.push_back(args[i]); } else if (doing == DoingDestination) { - destination = &args[i]; + // A trailing slash is meaningful for this form, but normalization + // preserves it if present + switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0177)) { + case cmPolicies::NEW: + destination = cmCMakePath(args[i]).Normal().String(); + break; + case cmPolicies::WARN: + // We can't be certain if a warning is appropriate if there are any + // generator expressions + if (cmGeneratorExpression::Find(args[i]) == cm::string_view::npos && + args[i] != cmCMakePath(args[i]).Normal().String()) { + status.GetMakefile().IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + destination = args[i]; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + // We should never get here, only OLD, WARN, and NEW are used + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0177)); + return false; + } doing = DoingNone; } else if (doing == DoingType) { if (allowedTypes.count(args[i]) == 0) { @@ -1907,7 +1957,7 @@ bool HandleDirectoryMode(std::vector const& args, } // Support installing an empty directory. - if (dirs.empty() && destination) { + if (dirs.empty() && destination.has_value()) { dirs.emplace_back(); } @@ -1915,15 +1965,13 @@ bool HandleDirectoryMode(std::vector const& args, if (dirs.empty()) { return true; } - std::string destinationStr; - if (!destination) { + if (!destination.has_value()) { if (type.empty()) { // A destination is required. status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } - destinationStr = helper.GetDestinationForType(nullptr, type); - destination = &destinationStr; + destination = helper.GetDestinationForType(nullptr, type); } else if (!type.empty()) { status.SetError(cmStrCat(args[0], " given both TYPE and DESTINATION " @@ -1955,7 +2003,7 @@ bool HandleExportAndroidMKMode(std::vector const& args, Helper helper(status); // This is the EXPORT mode. - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; std::string name_space; @@ -2028,10 +2076,10 @@ bool HandleExportAndroidMKMode(std::vector const& args, // Create the export install generator. helper.Makefile->AddInstallGenerator( - cm::make_unique( + cm::make_unique( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false, + ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), helper.Makefile->GetBacktrace())); return true; @@ -2048,7 +2096,7 @@ bool HandleExportMode(std::vector const& args, Helper helper(status); // This is the EXPORT mode. - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; std::string name_space; @@ -2151,16 +2199,152 @@ bool HandleExportMode(std::vector const& args, // Create the export install generator. helper.Makefile->AddInstallGenerator( - cm::make_unique( + cm::make_unique( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, - exportOld, false, exportPackageDependencies, + ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), + std::move(cxx_modules_directory), exportOld, exportPackageDependencies, helper.Makefile->GetBacktrace())); return true; } +bool HandlePackageInfoMode(std::vector const& args, + cmExecutionStatus& status) +{ +#ifndef CMAKE_BOOTSTRAP + if (!cmExperimental::HasSupportEnabled( + status.GetMakefile(), cmExperimental::Feature::ExportPackageInfo)) { + status.SetError("does not recognize sub-command PACKAGE_INFO"); + return false; + } + + Helper helper(status); + + // This is the PACKAGE_INFO mode. + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); + + ArgumentParser::NonEmpty pkg; + ArgumentParser::NonEmpty appendix; + ArgumentParser::NonEmpty exportName; + bool lowerCase = false; + ArgumentParser::NonEmpty version; + ArgumentParser::NonEmpty versionCompat; + ArgumentParser::NonEmpty versionSchema; + ArgumentParser::NonEmpty> defaultTargets; + ArgumentParser::NonEmpty> defaultConfigs; + ArgumentParser::NonEmpty cxxModulesDirectory; + + ica.Bind("PACKAGE_INFO"_s, pkg); + ica.Bind("EXPORT"_s, exportName); + ica.Bind("APPENDIX"_s, appendix); + ica.Bind("LOWER_CASE_FILE"_s, lowerCase); + ica.Bind("VERSION"_s, version); + ica.Bind("COMPAT_VERSION"_s, versionCompat); + ica.Bind("VERSION_SCHEMA"_s, versionSchema); + ica.Bind("DEFAULT_TARGETS"_s, defaultTargets); + ica.Bind("DEFAULT_CONFIGURATIONS"_s, defaultConfigs); + // ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); TODO? + + std::vector unknownArgs; + ica.Parse(args, &unknownArgs); + + if (!unknownArgs.empty()) { + // Unknown argument. + status.SetError( + cmStrCat(args[0], " given unknown argument \"", unknownArgs[0], "\".")); + return false; + } + + if (!ica.Finalize()) { + return false; + } + + if (exportName.empty()) { + status.SetError(cmStrCat(args[0], " missing EXPORT.")); + return false; + } + + if (version.empty()) { + if (!versionCompat.empty()) { + status.SetError("COMPAT_VERSION requires VERSION."); + return false; + } + if (!versionSchema.empty()) { + status.SetError("VERSION_SCHEMA requires VERSION."); + return false; + } + } else { + if (!appendix.empty()) { + status.SetError("APPENDIX and VERSION are mutually exclusive."); + return false; + } + } + if (!appendix.empty()) { + if (!defaultTargets.empty()) { + status.SetError("APPENDIX and DEFAULT_TARGETS are mutually exclusive."); + return false; + } + if (!defaultConfigs.empty()) { + status.SetError("APPENDIX and DEFAULT_CONFIGURATIONS " + "are mutually exclusive."); + return false; + } + } + + // Validate the package name. + if (!cmGeneratorExpression::IsValidTargetName(pkg) || + pkg.find(':') != std::string::npos) { + status.SetError( + cmStrCat(args[0], " given invalid package name \"", pkg, "\".")); + return false; + } + + // Construct the case-normalized package name and the file name. + std::string const pkgNameOnDisk = + (lowerCase ? cmSystemTools::LowerCase(pkg) : pkg); + std::string pkgFileName = [&]() -> std::string { + if (appendix.empty()) { + return cmStrCat(pkgNameOnDisk, ".cps"); + } + return cmStrCat(pkgNameOnDisk, '-', appendix, ".cps"); + }(); + + // Get or construct the destination path. + std::string dest = ica.GetDestination(); + if (dest.empty()) { + if (helper.Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Windows") { + dest = std::string{ "cps"_s }; + } else { + dest = cmStrCat(helper.GetLibraryDestination(nullptr), "/cps/", + pkgNameOnDisk); + } + } + + cmExportSet& exportSet = + helper.Makefile->GetGlobalGenerator()->GetExportSets()[exportName]; + + cmInstallGenerator::MessageLevel message = + cmInstallGenerator::SelectMessageLevel(helper.Makefile); + + // Create the export install generator. + helper.Makefile->AddInstallGenerator( + cm::make_unique( + &exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(), + ica.GetComponent(), message, ica.GetExcludeFromAll(), + std::move(pkgFileName), std::move(pkg), std::move(version), + std::move(versionCompat), std::move(versionSchema), + std::move(defaultTargets), std::move(defaultConfigs), + std::move(cxxModulesDirectory), helper.Makefile->GetBacktrace())); + + return true; +#else + static_cast(args); + status.SetError("PACKAGE_INFO not supported in bootstrap cmake"); + return false; +#endif +} + bool HandleRuntimeDependencySetMode(std::vector const& args, cmExecutionStatus& status) { @@ -2197,14 +2381,18 @@ bool HandleRuntimeDependencySetMode(std::vector const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector runtimeDependencyArgVector; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); // Now also parse the file(GET_RUNTIME_DEPENDENCY) args std::vector unknownArgs; @@ -2452,6 +2640,12 @@ std::string Helper::GetDocDestination( this->GetDataRootDestination(nullptr) + "/doc"); } +std::string Helper::GetProgramExecutablesDestination( + const cmInstallCommandArguments* args) const +{ + return this->GetDestination(args, "CMAKE_INSTALL_LIBEXECDIR", "libexec"); +} + std::string Helper::GetDestinationForType( const cmInstallCommandArguments* args, const std::string& type) const { @@ -2497,6 +2691,9 @@ std::string Helper::GetDestinationForType( if (type == "DOC") { return this->GetDocDestination(nullptr); } + if (type == "LIBEXEC") { + return this->GetProgramExecutablesDestination(nullptr); + } return ""; } @@ -2524,6 +2721,7 @@ bool cmInstallCommand(std::vector const& args, { "DIRECTORY"_s, HandleDirectoryMode }, { "EXPORT"_s, HandleExportMode }, { "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode }, + { "PACKAGE_INFO"_s, HandlePackageInfoMode }, { "RUNTIME_DEPENDENCY_SET"_s, HandleRuntimeDependencySetMode }, }; diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 730931645..f371cff20 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -3,11 +3,19 @@ #include "cmInstallCommandArguments.h" #include +#include #include +#include #include +#include "cmCMakePath.h" +#include "cmGeneratorExpression.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" #include "cmRange.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" // Table of valid permissions. @@ -20,10 +28,53 @@ const char* cmInstallCommandArguments::PermissionsTable[] = { const std::string cmInstallCommandArguments::EmptyString; cmInstallCommandArguments::cmInstallCommandArguments( - std::string defaultComponent) + std::string defaultComponent, cmMakefile& makefile) : DefaultComponentName(std::move(defaultComponent)) { - this->Bind("DESTINATION"_s, this->Destination); + std::function normalizeDest; + + switch (makefile.GetPolicyStatus(cmPolicies::CMP0177)) { + case cmPolicies::OLD: + normalizeDest = [this](cm::string_view arg) -> ArgumentParser::Continue { + this->Destination = std::string(arg.begin(), arg.end()); + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::WARN: + normalizeDest = + [this, &makefile](cm::string_view arg) -> ArgumentParser::Continue { + this->Destination = std::string(arg.begin(), arg.end()); + // We can't be certain if a warning is appropriate if there are any + // generator expressions + if (cmGeneratorExpression::Find(arg) == cm::string_view::npos && + arg != cmCMakePath(arg).Normal().String()) { + makefile.IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + } + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::NEW: + normalizeDest = [this](cm::string_view arg) -> ArgumentParser::Continue { + if (cmGeneratorExpression::Find(arg) == cm::string_view::npos) { + this->Destination = cmCMakePath(arg).Normal().String(); + } else { + this->Destination = + cmStrCat("$'); + } + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + // We should never get here, only OLD, WARN, and NEW are used + makefile.IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0177)); + } + + this->Bind("DESTINATION"_s, normalizeDest); this->Bind("COMPONENT"_s, this->Component); this->Bind("NAMELINK_COMPONENT"_s, this->NamelinkComponent); this->Bind("EXCLUDE_FROM_ALL"_s, this->ExcludeFromAll); @@ -41,7 +92,7 @@ const std::string& cmInstallCommandArguments::GetDestination() const if (!this->DestinationString.empty()) { return this->DestinationString; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetDestination(); } return EmptyString; @@ -52,7 +103,7 @@ const std::string& cmInstallCommandArguments::GetComponent() const if (!this->Component.empty()) { return this->Component; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetComponent(); } if (!this->DefaultComponentName.empty()) { @@ -75,7 +126,7 @@ const std::string& cmInstallCommandArguments::GetRename() const if (!this->Rename.empty()) { return this->Rename; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetRename(); } return EmptyString; @@ -86,7 +137,7 @@ const std::string& cmInstallCommandArguments::GetPermissions() const if (!this->PermissionsString.empty()) { return this->PermissionsString; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetPermissions(); } return EmptyString; @@ -97,7 +148,7 @@ bool cmInstallCommandArguments::GetOptional() const if (this->Optional) { return true; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetOptional(); } return false; @@ -108,7 +159,7 @@ bool cmInstallCommandArguments::GetExcludeFromAll() const if (this->ExcludeFromAll) { return true; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetExcludeFromAll(); } return false; @@ -119,7 +170,7 @@ bool cmInstallCommandArguments::GetNamelinkOnly() const if (this->NamelinkOnly) { return true; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetNamelinkOnly(); } return false; @@ -130,7 +181,7 @@ bool cmInstallCommandArguments::GetNamelinkSkip() const if (this->NamelinkSkip) { return true; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetNamelinkSkip(); } return false; @@ -141,7 +192,7 @@ bool cmInstallCommandArguments::HasNamelinkComponent() const if (!this->NamelinkComponent.empty()) { return true; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->HasNamelinkComponent(); } return false; @@ -163,7 +214,7 @@ const std::vector& cmInstallCommandArguments::GetConfigurations() if (!this->Configurations.empty()) { return this->Configurations; } - if (this->GenericArguments != nullptr) { + if (this->GenericArguments) { return this->GenericArguments->GetConfigurations(); } return this->Configurations; @@ -227,8 +278,8 @@ void cmInstallCommandIncludesArgument::Parse( } cmInstallCommandFileSetArguments::cmInstallCommandFileSetArguments( - std::string defaultComponent) - : cmInstallCommandArguments(std::move(defaultComponent)) + std::string defaultComponent, cmMakefile& makefile) + : cmInstallCommandArguments(std::move(defaultComponent), makefile) { this->Bind("FILE_SET"_s, this->FileSet); } diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 6e46aaca5..cb3917ef8 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -10,10 +10,13 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +class cmMakefile; + class cmInstallCommandArguments : public cmArgumentParser { public: - cmInstallCommandArguments(std::string defaultComponent); + cmInstallCommandArguments(std::string defaultComponent, + cmMakefile& makefile); void SetGenericArguments(cmInstallCommandArguments* args) { this->GenericArguments = args; @@ -78,7 +81,8 @@ private: class cmInstallCommandFileSetArguments : public cmInstallCommandArguments { public: - cmInstallCommandFileSetArguments(std::string defaultComponent); + cmInstallCommandFileSetArguments(std::string defaultComponent, + cmMakefile& makefile); void Parse(std::vector args, std::vector* unconsumedArgs); diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 2f3da3e36..9828bf207 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -6,12 +6,7 @@ #include #include -#include - #include "cmCryptoHash.h" -#ifndef CMAKE_BOOTSTRAP -# include "cmExportInstallAndroidMKGenerator.h" -#endif #include "cmExportInstallFileGenerator.h" #include "cmExportSet.h" #include "cmInstallType.h" @@ -22,29 +17,20 @@ #include "cmSystemTools.h" cmInstallExportGenerator::cmInstallExportGenerator( - cmExportSet* exportSet, std::string const& destination, - std::string file_permissions, std::vector const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - std::string filename, std::string name_space, - std::string cxx_modules_directory, bool exportOld, bool android, - bool exportPackageDependencies, cmListFileBacktrace backtrace) - : cmInstallGenerator(destination, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, + cmListFileBacktrace backtrace) + : cmInstallGenerator(std::move(destination), configurations, + std::move(component), message, excludeFromAll, false, + std::move(backtrace)) , ExportSet(exportSet) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , FileName(std::move(filename)) - , Namespace(std::move(name_space)) - , CxxModulesDirectory(std::move(cxx_modules_directory)) - , ExportOld(exportOld) - , ExportPackageDependencies(exportPackageDependencies) + , Namespace(std::move(targetNamespace)) + , CxxModulesDirectory(std::move(cxxModulesDirectory)) { - if (android) { -#ifndef CMAKE_BOOTSTRAP - this->EFGen = cm::make_unique(this); -#endif - } else { - this->EFGen = cm::make_unique(this); - } exportSet->AddInstallation(this); } @@ -92,7 +78,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) // Skip empty sets. if (this->ExportSet->GetTargetExports().empty()) { std::ostringstream e; - e << "INSTALL(EXPORT) given unknown export \"" + e << "INSTALL(" << this->InstallSubcommand() << ") given unknown export \"" << this->ExportSet->GetName() << "\""; cmSystemTools::Error(e.str()); return; @@ -108,7 +94,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) // Generate the import file for this export set. this->EFGen->SetExportFile(this->MainImportFile.c_str()); this->EFGen->SetNamespace(this->Namespace); - this->EFGen->SetExportOld(this->ExportOld); if (this->ConfigurationTypes->empty()) { if (!this->ConfigurationName.empty()) { this->EFGen->AddConfiguration(this->ConfigurationName); @@ -120,7 +105,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) this->EFGen->AddConfiguration(c); } } - this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies); this->EFGen->GenerateImportFile(); // Perform the main install script generation. @@ -149,37 +133,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, // Now create a configuration-specific install rule for the C++ module import // property file of each configuration. - auto cxx_module_dest = + auto const cxxModuleDestination = 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()) { + auto const cxxModuleInstallFilePath = this->EFGen->GetCxxModuleFile(); + auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob(); + if (!cxxModuleInstallFilePath.empty() && !configImportFilesGlob.empty()) { + auto const cxxModuleFilename = + cmSystemTools::GetFilenameName(cxxModuleInstallFilePath); + // 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-", - this->ExportSet->GetName(), ".cmake"); - std::string toInstallFile = - cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), - "/cxx-modules-", this->ExportSet->GetName(), ".cmake"); + std::string installedDir = + cmStrCat("$ENV{DESTDIR}", + ConvertToAbsoluteDestination(cxxModuleDestination), '/'); + std::string installedFile = cmStrCat(installedDir, cxxModuleFilename); 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"; + << indentN << " \"" << cxxModuleInstallFilePath << "\")\n"; os << indentN << "if(_cmake_export_file_changed)\n"; os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir - << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + << configImportFilesGlob << "\")\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 + << "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"; @@ -187,12 +171,11 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, 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, + files.push_back(cxxModuleInstallFilePath); + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent); files.clear(); @@ -201,7 +184,7 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, 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, + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent.Next()); os << indent << "endif()\n"; @@ -210,9 +193,9 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, 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()); + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, + i.second, false, this->FilePermissions.c_str(), + nullptr, nullptr, nullptr, indent.Next()); os << indent << "endif()\n"; files.clear(); } @@ -221,33 +204,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, Indent indent) { - // Remove old per-configuration export files if the main changes. - std::string installedDir = cmStrCat( - "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/'); - std::string installedFile = cmStrCat(installedDir, this->FileName); - 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 << " \"" << this->MainImportFile << "\")\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 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 */ + auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob(); + if (!configImportFilesGlob.empty()) { + // Remove old per-configuration export files if the main changes. + std::string installedDir = cmStrCat( + "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/'); + std::string installedFile = cmStrCat(installedDir, this->FileName); + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << this->MainImportFile << "\")\n"; + os << indentN << "if(_cmake_export_file_changed)\n"; + os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir + << configImportFilesGlob << "\")\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 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"; + } // Install the main export file. std::vector files; diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 5f928513c..610061020 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -17,19 +17,18 @@ class cmListFileBacktrace; class cmLocalGenerator; /** \class cmInstallExportGenerator - * \brief Generate rules for creating an export files. + * \brief Support class for generating rules for creating export files. */ class cmInstallExportGenerator : public cmInstallGenerator { public: - cmInstallExportGenerator(cmExportSet* exportSet, std::string const& dest, - std::string file_permissions, - const std::vector& configurations, - std::string const& component, MessageLevel message, - bool exclude_from_all, std::string filename, - std::string name_space, - std::string cxx_modules_directory, bool exportOld, - bool android, bool exportPackageDependencies, + cmInstallExportGenerator(cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector const& configurations, + std::string component, MessageLevel message, + bool excludeFromAll, std::string filename, + std::string targetNamespace, + std::string cxxModulesDirectory, cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -37,6 +36,8 @@ public: cmInstallExportGenerator& operator=(const cmInstallExportGenerator&) = delete; + virtual char const* InstallSubcommand() const = 0; + cmExportSet* GetExportSet() { return this->ExportSet; } bool Compute(cmLocalGenerator* lg) override; @@ -60,8 +61,6 @@ protected: void GenerateScript(std::ostream& os) override; void GenerateScriptConfigs(std::ostream& os, Indent indent) override; void GenerateScriptActions(std::ostream& os, Indent indent) override; - void GenerateImportFile(cmExportSet const* exportSet); - void GenerateImportFile(const char* config, cmExportSet const* exportSet); std::string TempDirCalculate() const; void ComputeTempDir(); @@ -70,8 +69,6 @@ protected: std::string const FileName; std::string const Namespace; std::string const CxxModulesDirectory; - bool const ExportOld; - bool const ExportPackageDependencies; cmLocalGenerator* LocalGenerator = nullptr; std::string TempDir; diff --git a/Source/cmInstallPackageInfoExportGenerator.cxx b/Source/cmInstallPackageInfoExportGenerator.cxx new file mode 100644 index 000000000..4ff045be9 --- /dev/null +++ b/Source/cmInstallPackageInfoExportGenerator.cxx @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallPackageInfoExportGenerator.h" + +#include + +#include + +#include "cmExportInstallFileGenerator.h" +#include "cmExportInstallPackageInfoGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +cmInstallPackageInfoExportGenerator::cmInstallPackageInfoExportGenerator( + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string packageName, std::string version, std::string versionCompat, + std::string versionSchema, std::vector defaultTargets, + std::vector defaultConfigurations, + std::string cxxModulesDirectory, cmListFileBacktrace backtrace) + : cmInstallExportGenerator( + exportSet, std::move(destination), std::move(filePermissions), + configurations, std::move(component), message, excludeFromAll, + std::move(filename), packageName + "::", std::move(cxxModulesDirectory), + std::move(backtrace)) +{ + this->EFGen = cm::make_unique( + this, std::move(packageName), std::move(version), std::move(versionCompat), + std::move(versionSchema), std::move(defaultTargets), + std::move(defaultConfigurations)); +} + +cmInstallPackageInfoExportGenerator::~cmInstallPackageInfoExportGenerator() = + default; diff --git a/Source/cmInstallPackageInfoExportGenerator.h b/Source/cmInstallPackageInfoExportGenerator.h new file mode 100644 index 000000000..c79df8477 --- /dev/null +++ b/Source/cmInstallPackageInfoExportGenerator.h @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallPackageInfoGenerator + * \brief Generate rules for creating CPS package info files. + */ +class cmInstallPackageInfoExportGenerator : public cmInstallExportGenerator +{ +public: + cmInstallPackageInfoExportGenerator( + cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string packageName, std::string version, std::string versionCompat, + std::string versionSchema, std::vector defaultTargets, + std::vector defaultConfigurations, + std::string cxxModulesDirectory, cmListFileBacktrace backtrace); + cmInstallPackageInfoExportGenerator( + cmInstallPackageInfoExportGenerator const&) = delete; + ~cmInstallPackageInfoExportGenerator() override; + + cmInstallPackageInfoExportGenerator& operator=( + cmInstallPackageInfoExportGenerator const&) = delete; + + char const* InstallSubcommand() const override { return "PACKAGE_INFO"; } +}; diff --git a/Source/cmInstallScriptHandler.cxx b/Source/cmInstallScriptHandler.cxx new file mode 100644 index 000000000..9a4e70f07 --- /dev/null +++ b/Source/cmInstallScriptHandler.cxx @@ -0,0 +1,167 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmInstallScriptHandler.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" + +#include "cmCryptoHash.h" +#include "cmGeneratedFileStream.h" +#include "cmJSONState.h" +#include "cmProcessOutput.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" + +using InstallScript = cmInstallScriptHandler::InstallScript; + +cmInstallScriptHandler::cmInstallScriptHandler(std::string _binaryDir, + std::string _component, + std::vector& args) + : binaryDir(std::move(_binaryDir)) + , component(std::move(_component)) +{ + const std::string& file = + cmStrCat(this->binaryDir, "/CMakeFiles/InstallScripts.json"); + if (cmSystemTools::FileExists(file)) { + int compare; + cmSystemTools::FileTimeCompare( + cmStrCat(this->binaryDir, "/CMakeFiles/cmake.check_cache"), file, + &compare); + if (compare < 1) { + args.insert(args.end() - 1, "-DCMAKE_INSTALL_LOCAL_ONLY=1"); + Json::CharReaderBuilder rbuilder; + auto JsonReader = + std::unique_ptr(rbuilder.newCharReader()); + std::vector content; + Json::Value value; + cmJSONState state(file, &value); + for (auto const& script : value["InstallScripts"]) { + this->commands.push_back(args); + this->commands.back().emplace_back(script.asCString()); + this->directories.push_back( + cmSystemTools::GetFilenamePath(script.asCString())); + } + } + } +} + +bool cmInstallScriptHandler::isParallel() +{ + return !this->commands.empty(); +} + +int cmInstallScriptHandler::install(unsigned int j) +{ + cm::uv_loop_ptr loop; + loop.init(); + std::vector scripts; + scripts.reserve(this->commands.size()); + for (auto const& cmd : this->commands) { + scripts.emplace_back(cmd); + } + std::size_t working = 0; + std::size_t installed = 0; + std::size_t i = 0; + + std::function queueScripts; + queueScripts = [&scripts, &working, &installed, &i, &loop, j, + &queueScripts]() { + for (auto queue = std::min(j - working, scripts.size() - i); queue > 0; + --queue) { + ++working; + scripts[i].start(loop, + [&scripts, &working, &installed, i, &queueScripts]() { + scripts[i].printResult(++installed, scripts.size()); + --working; + queueScripts(); + }); + ++i; + } + }; + queueScripts(); + uv_run(loop, UV_RUN_DEFAULT); + + // Write install manifest + std::string install_manifest; + if (this->component.empty()) { + install_manifest = "install_manifest.txt"; + } else { + cmsys::RegularExpression regEntry; + if (regEntry.compile("^[a-zA-Z0-9_.+-]+$") && + regEntry.find(this->component)) { + install_manifest = + cmStrCat("install_manifest_", this->component, ".txt"); + } else { + cmCryptoHash md5(cmCryptoHash::AlgoMD5); + md5.Initialize(); + install_manifest = + cmStrCat("install_manifest_", md5.HashString(this->component), ".txt"); + } + } + cmGeneratedFileStream fout(cmStrCat(this->binaryDir, "/", install_manifest)); + fout.SetCopyIfDifferent(true); + for (auto const& dir : this->directories) { + auto local_manifest = cmStrCat(dir, "/install_local_manifest.txt"); + if (cmSystemTools::FileExists(local_manifest)) { + cmsys::ifstream fin(local_manifest.c_str()); + std::string line; + while (std::getline(fin, line)) { + fout << line << "\n"; + } + } + } + return 0; +} + +InstallScript::InstallScript(const std::vector& cmd) +{ + this->name = cmSystemTools::RelativePath( + cmSystemTools::GetCurrentWorkingDirectory(), cmd.back()); + this->command = cmd; +} + +void InstallScript::start(cm::uv_loop_ptr& loop, + std::function callback) +{ + cmUVProcessChainBuilder builder; + builder.AddCommand(this->command) + .SetExternalLoop(*loop) + .SetMergedBuiltinStreams(); + this->chain = cm::make_unique(builder.Start()); + this->pipe.init(this->chain->GetLoop(), 0); + uv_pipe_open(this->pipe, this->chain->OutputStream()); + this->streamHandler = cmUVStreamRead( + this->pipe, + [this](std::vector data) { + std::string strdata; + cmProcessOutput(cmProcessOutput::Auto) + .DecodeText(data.data(), data.size(), strdata); + this->output.push_back(strdata); + }, + std::move(callback)); +} + +void InstallScript::printResult(std::size_t n, std::size_t total) +{ + cmSystemTools::Stdout(cmStrCat('[', n, '/', total, "] ", this->name, '\n')); + for (auto const& line : this->output) { + cmSystemTools::Stdout(line); + } +} diff --git a/Source/cmInstallScriptHandler.h b/Source/cmInstallScriptHandler.h new file mode 100644 index 000000000..414304425 --- /dev/null +++ b/Source/cmInstallScriptHandler.h @@ -0,0 +1,43 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include +#include +#include +#include + +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" + +class cmInstallScriptHandler +{ +public: + cmInstallScriptHandler() = default; + cmInstallScriptHandler(std::string, std::string, std::vector&); + bool isParallel(); + int install(unsigned int j); + class InstallScript + { + public: + InstallScript(const std::vector&); + void start(cm::uv_loop_ptr&, std::function); + void printResult(std::size_t n, std::size_t total); + + private: + std::vector command; + std::vector output; + std::string name; + std::unique_ptr chain; + std::unique_ptr streamHandler; + cm::uv_pipe_ptr pipe; + }; + +private: + std::vector> commands; + std::vector directories; + std::string binaryDir; + std::string component; +}; diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index e7998ead3..526eed659 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -66,7 +66,7 @@ void computeFilesToInstall( // Library interface name. std::string fromSOName; std::string toSOName; - if (library != output) { + if (!library.empty() && library != output) { haveNamelink = true; fromSOName = cmStrCat(fromDirConfig, library); toSOName = library; @@ -403,6 +403,10 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles( files.From.emplace_back(std::move(from1)); files.To.emplace_back(std::move(to1)); + } else if (this->Target->IsArchivedAIXSharedLibrary()) { + // Install only the archive on AIX. + computeFilesToInstall(files, this->NamelinkMode, fromDirConfig, + targetNames.Output, {}, targetNames.Real); } else { computeFilesToInstall(files, this->NamelinkMode, fromDirConfig, targetNames.Output, targetNames.SharedObject, diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h index 24884ed31..369c0b775 100644 --- a/Source/cmJSONHelpers.h +++ b/Source/cmJSONHelpers.h @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -34,9 +36,11 @@ enum ObjectError ExtraField, MissingRequired }; + using ErrorGenerator = std::function; using ObjectErrorGenerator = std::function; + ErrorGenerator EXPECTED_TYPE(const std::string& type); void INVALID_STRING(const Json::Value* value, cmJSONState* state); @@ -58,9 +62,28 @@ ErrorGenerator INVALID_NAMED_OBJECT_KEY( ObjectError errorType, const Json::Value::Members& extraFields); } -struct cmJSONHelperBuilder +#if __cplusplus >= 201703L +namespace details { +// A meta-function to check if a given callable type +// can be called with the only string ref arg. +template +struct is_bool_filter +{ + static constexpr bool value = false; +}; + +template +struct is_bool_filter, bool>>> { + static constexpr bool value = true; +}; +} +#endif +struct cmJSONHelperBuilder +{ template class Object { @@ -104,7 +127,6 @@ struct cmJSONHelperBuilder bool operator()(T& out, const Json::Value* value, cmJSONState* state) const { Json::Value::Members extraFields; - bool success = true; if (!value && this->AnyRequired) { Error(JsonErrors::ObjectError::RequiredMissing, extraFields)(value, state); @@ -119,6 +141,13 @@ struct cmJSONHelperBuilder extraFields = value->getMemberNames(); } + if (state->allowComments) { + extraFields.erase( + std::remove(extraFields.begin(), extraFields.end(), "$comment"), + extraFields.end()); + } + + bool success = true; for (auto const& m : this->Members) { std::string name(m.Name.data(), m.Name.size()); state->push_stack(name, value); @@ -153,6 +182,12 @@ struct cmJSONHelperBuilder cmJSONState* state)>; struct Member { + Member(cm::string_view name, MemberFunction func, bool required) + : Name{ name } + , Function{ std::move(func) } + , Required{ required } + { + } cm::string_view Name; MemberFunction Function; bool Required; @@ -165,14 +200,8 @@ struct cmJSONHelperBuilder Object& BindPrivate(const cm::string_view& name, MemberFunction&& func, bool required) { - Member m; - m.Name = name; - m.Function = std::move(func); - m.Required = required; - this->Members.push_back(std::move(m)); - if (required) { - this->AnyRequired = true; - } + this->Members.emplace_back(name, std::move(func), required); + this->AnyRequired = this->AnyRequired || required; return *this; } }; @@ -189,7 +218,6 @@ struct cmJSONHelperBuilder } if (!value->isString()) { error(value, state); - ; return false; } out = value->asString(); @@ -214,7 +242,6 @@ struct cmJSONHelperBuilder } if (!value->isInt()) { error(value, state); - ; return false; } out = value->asInt(); @@ -239,7 +266,6 @@ struct cmJSONHelperBuilder } if (!value->isUInt()) { error(value, state); - ; return false; } out = value->asUInt(); @@ -264,7 +290,6 @@ struct cmJSONHelperBuilder } if (!value->isBool()) { error(value, state); - ; return false; } out = value->asBool(); @@ -319,47 +344,125 @@ struct cmJSONHelperBuilder [](const T&) { return true; }); } - template - static cmJSONHelper> MapFilter( + enum class FilterResult + { + Continue, ///< A filter has accepted a given key (and value) + Skip, ///< A filter has rejected a given key (or value) + Error ///< A filter has found and reported an error + }; + + /// Iterate over the object's members and call a filter callable to + /// decide what to do with the current key/value. + /// A filter returns one of the `FilterResult` values. + /// A container type is an associative or a sequence + /// container of pairs (key, value). + template + static cmJSONHelper FilteredObject( const JsonErrors::ErrorGenerator& error, F func, Filter filter) { - return [error, func, filter](std::map& out, - const Json::Value* value, + return [error, func, filter](Container& out, const Json::Value* value, cmJSONState* state) -> bool { - bool success = true; + // NOTE Some compile-time code path don't use `filter` at all. + // So, suppress "unused lambda capture" warning is needed. + static_cast(filter); + if (!value) { out.clear(); return true; } if (!value->isObject()) { error(value, state); - ; return false; } out.clear(); + auto outIt = std::inserter(out, out.end()); + bool success = true; for (auto const& key : value->getMemberNames()) { - state->push_stack(cmStrCat(key, ""), &(*value)[key]); - if (!filter(key)) { - state->pop_stack(); - continue; - } - T t; - if (!func(t, &(*value)[key], state)) { - success = false; + state->push_stack(key, &(*value)[key]); +#if __cplusplus >= 201703L + if constexpr (std::is_same_v) { + // Filtering functionality isn't needed at all... + } else if constexpr (details::is_bool_filter::value) { + // A given `Filter` is `bool(const std::string&)` callable. + if (!filter(key)) { + state->pop_stack(); + continue; + } + } else { +#endif + // A full-featured `Filter` has been given + auto res = filter(key, &(*value)[key], state); + if (res == FilterResult::Skip) { + state->pop_stack(); + continue; + } + if (res == FilterResult::Error) { + state->pop_stack(); + success = false; + break; + } +#if __cplusplus >= 201703L } - out[key] = std::move(t); +#endif + typename Container::value_type::second_type t; + // ATTENTION Call the function first (for it's side-effects), + // then accumulate the result! + success = func(t, &(*value)[key], state) && success; + outIt = typename Container::value_type{ key, std::move(t) }; state->pop_stack(); } return success; }; } + template + static cmJSONHelper> MapFilter( + const JsonErrors::ErrorGenerator& error, F func, Filter filter) + { + // clang-format off + return FilteredObject>( + error, func, +#if __cplusplus >= 201703L + // In C++ 17 a filter callable can be passed as is. + // Depending on its type `FilteredObject()` will call + // it with a key only (backward compatible behavior) + // or with 3 args supported by the full-featured + // filtering feature. + filter +#else + // For C++14 and below, to keep backward compatibility + // with CMake Presets code, `MapFilter()` can accept only + // `bool(const std::string&)` callables. + [filter](const std::string &key, const Json::Value * /*value*/, + cmJSONState * /*state*/) -> FilterResult { + // Simple adaptor to translate `bool` to `FilterResult` + return filter(key) ? FilterResult::Continue : FilterResult::Skip; + } +#endif + ); + // clang-format on + } + template static cmJSONHelper> Map( const JsonErrors::ErrorGenerator& error, F func) { - return MapFilter(error, func, - [](const std::string&) { return true; }); + // clang-format off + return FilteredObject>( + error, func, +#if __cplusplus >= 201703L + // With C++ 17 and above, pass a marker type, that no + // filtering is needed at all. + std::true_type() +#else + // In C++ 14 and below, pass an always-true dummy functor. + [](const std::string& /*key*/, const Json::Value* /*value*/, + cmJSONState* /*state*/) -> FilterResult { + return FilterResult::Continue; + } +#endif + ); + // clang-format on } template @@ -384,10 +487,24 @@ struct cmJSONHelperBuilder cmJSONState* state) -> bool { if (!value) { error(value, state); - ; return false; } return func(out, value, state); }; } + + template + static cmJSONHelper Checked(const JsonErrors::ErrorGenerator& error, + F func, P predicate) + { + return [error, func, predicate](T& out, const Json::Value* value, + cmJSONState* state) -> bool { + bool result = func(out, value, state); + if (result && !predicate(out)) { + error(value, state); + result = false; + } + return result; + }; + } }; diff --git a/Source/cmJSONState.h b/Source/cmJSONState.h index 907126805..3223453af 100644 --- a/Source/cmJSONState.h +++ b/Source/cmJSONState.h @@ -65,6 +65,7 @@ public: std::vector parseStack; std::vector errors; std::string doc; + bool allowComments = false; private: std::string GetJsonContext(Location loc); diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx index eabc8b931..ca574d4a5 100644 --- a/Source/cmLinkItemGraphVisitor.cxx +++ b/Source/cmLinkItemGraphVisitor.cxx @@ -24,7 +24,7 @@ void cmLinkItemGraphVisitor::VisitItem(cmLinkItem const& item) void cmLinkItemGraphVisitor::VisitLinks(cmLinkItem const& item, cmLinkItem const& rootItem) { - if (item.Target == nullptr) { + if (!item.Target) { return; } @@ -100,7 +100,7 @@ void cmLinkItemGraphVisitor::GetDependencies(cmGeneratorTarget const& target, { const auto* implementationLibraries = target.GetLinkImplementationLibraries( config, cmGeneratorTarget::UseTo::Link); - if (implementationLibraries != nullptr) { + if (implementationLibraries) { for (auto const& lib : implementationLibraries->Libraries) { auto const& name = lib.AsStr(); dependencies[name] = Dependency(DependencyType::LinkPrivate, lib); @@ -109,7 +109,7 @@ void cmLinkItemGraphVisitor::GetDependencies(cmGeneratorTarget const& target, const auto* interfaceLibraries = target.GetLinkInterfaceLibraries( config, &target, cmGeneratorTarget::UseTo::Compile); - if (interfaceLibraries != nullptr) { + if (interfaceLibraries) { for (auto const& lib : interfaceLibraries->Libraries) { auto const& name = lib.AsStr(); if (dependencies.find(name) != dependencies.cend()) { diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx index a29e33f80..e823337e8 100644 --- a/Source/cmLinkLineComputer.cxx +++ b/Source/cmLinkLineComputer.cxx @@ -9,6 +9,7 @@ #include "cmComputeLinkInformation.h" #include "cmGeneratorTarget.h" +#include "cmList.h" #include "cmListFileCache.h" #include "cmOutputConverter.h" #include "cmStateTypes.h" @@ -112,18 +113,20 @@ std::string cmLinkLineComputer::ConvertToOutputForExisting( std::string cmLinkLineComputer::ComputeLinkPath( cmComputeLinkInformation& cli, std::string const& libPathFlag, - std::string const& libPathTerminator) + std::string const& libPathTerminator, std::string const& stdLinkDirString) { std::string linkPath; std::vector> linkPathList; - this->ComputeLinkPath(cli, libPathFlag, libPathTerminator, linkPathList); + this->ComputeLinkPath(cli, libPathFlag, libPathTerminator, stdLinkDirString, + linkPathList); cli.AppendValues(linkPath, linkPathList); return linkPath; } void cmLinkLineComputer::ComputeLinkPath( cmComputeLinkInformation& cli, std::string const& libPathFlag, - std::string const& libPathTerminator, std::vector>& linkPath) + std::string const& libPathTerminator, std::string const& stdLinkDirString, + std::vector>& linkPath) { if (cli.GetLinkLanguage() == "Swift") { std::string linkPathNoBT; @@ -160,6 +163,12 @@ void cmLinkLineComputer::ComputeLinkPath( libPathTerminator, " "); linkPath.emplace_back(libDir); } + + for (auto& linkDir : cmList(stdLinkDirString)) { + linkPath.emplace_back(cmStrCat(' ', libPathFlag, + this->ConvertToOutputForExisting(linkDir), + libPathTerminator, ' ')); + } } std::string cmLinkLineComputer::ComputeRPath(cmComputeLinkInformation& cli) diff --git a/Source/cmLinkLineComputer.h b/Source/cmLinkLineComputer.h index afd3944f1..63cbeb27c 100644 --- a/Source/cmLinkLineComputer.h +++ b/Source/cmLinkLineComputer.h @@ -36,11 +36,13 @@ public: std::string ComputeLinkPath(cmComputeLinkInformation& cli, std::string const& libPathFlag, - std::string const& libPathTerminator); + std::string const& libPathTerminator, + std::string const& stdLinkDirString); void ComputeLinkPath(cmComputeLinkInformation& cli, std::string const& libPathFlag, std::string const& libPathTerminator, + std::string const& stdLinkDirString, std::vector>& linkPath); std::string ComputeFrameworkPath(cmComputeLinkInformation& cli, diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 018b517d8..3963eb88c 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -160,7 +160,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries( cmGeneratorTarget::UseTo::Link); for (const cmLinkImplItem& iter : linkImpl->Libraries) { - if (iter.Target != nullptr && + if (iter.Target && iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { std::string libPath = iter.Target->GetLocation(cli.GetConfig()); if (item.Value == libPath) { diff --git a/Source/cmList.cxx b/Source/cmList.cxx index 939fbb557..cf65bb2bb 100644 --- a/Source/cmList.cxx +++ b/Source/cmList.cxx @@ -146,7 +146,7 @@ public: { std::string result = argument; for (auto const& filter : this->Filters) { - if (filter != nullptr) { + if (filter) { result = filter(result); } } diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 97f5de969..cf869c873 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -4,7 +4,7 @@ #include "cmListFileCache.h" #include -#include +#include #include #ifdef _WIN32 @@ -15,39 +15,70 @@ #include "cmListFileLexer.h" #include "cmMessageType.h" #include "cmMessenger.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" -struct cmListFileParser +namespace { + +enum class NestingStateEnum +{ + If, + Else, + While, + Foreach, + Function, + Macro, + Block +}; + +struct NestingState { + NestingStateEnum State; + cmListFileContext Context; +}; + +bool TopIs(std::vector& stack, NestingStateEnum state) +{ + return !stack.empty() && stack.back().State == state; +} + +class cmListFileParser +{ +public: cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, cmMessenger* messenger); - ~cmListFileParser(); cmListFileParser(const cmListFileParser&) = delete; cmListFileParser& operator=(const cmListFileParser&) = delete; - void IssueFileOpenError(std::string const& text) const; - void IssueError(std::string const& text) const; + bool ParseFile(const char* filename); bool ParseString(const char* str, const char* virtual_filename); + +private: bool Parse(); bool ParseFunction(const char* name, long line); bool AddArgument(cmListFileLexer_Token* token, cmListFileArgument::Delimiter delim); + void IssueFileOpenError(std::string const& text) const; + void IssueError(std::string const& text) const; + cm::optional CheckNesting() const; + + enum + { + SeparationOkay, + SeparationWarning, + SeparationError + } Separation; + cmListFile* ListFile; cmListFileBacktrace Backtrace; cmMessenger* Messenger; const char* FileName = nullptr; - cmListFileLexer* Lexer; + std::unique_ptr Lexer; std::string FunctionName; long FunctionLine; long FunctionLineEnd; std::vector FunctionArguments; - enum - { - SeparationOkay, - SeparationWarning, - SeparationError - } Separation; }; cmListFileParser::cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, @@ -55,13 +86,8 @@ cmListFileParser::cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, : ListFile(lf) , Backtrace(std::move(lfbt)) , Messenger(messenger) - , Lexer(cmListFileLexer_New()) -{ -} - -cmListFileParser::~cmListFileParser() + , Lexer(cmListFileLexer_New(), cmListFileLexer_Delete) { - cmListFileLexer_Delete(this->Lexer); } void cmListFileParser::IssueFileOpenError(const std::string& text) const @@ -74,7 +100,7 @@ void cmListFileParser::IssueError(const std::string& text) const { cmListFileContext lfc; lfc.FilePath = this->FileName; - lfc.Line = cmListFileLexer_GetCurrentLine(this->Lexer); + lfc.Line = cmListFileLexer_GetCurrentLine(this->Lexer.get()); cmListFileBacktrace lfbt = this->Backtrace; lfbt = lfbt.Push(lfc); this->Messenger->IssueMessage(MessageType::FATAL_ERROR, text, lfbt); @@ -93,13 +119,13 @@ bool cmListFileParser::ParseFile(const char* filename) // Open the file. cmListFileLexer_BOM bom; - if (!cmListFileLexer_SetFileName(this->Lexer, filename, &bom)) { + if (!cmListFileLexer_SetFileName(this->Lexer.get(), filename, &bom)) { this->IssueFileOpenError("cmListFileCache: error can not open file."); return false; } if (bom == cmListFileLexer_BOM_Broken) { - cmListFileLexer_SetFileName(this->Lexer, nullptr, nullptr); + cmListFileLexer_SetFileName(this->Lexer.get(), nullptr, nullptr); this->IssueFileOpenError("Error while reading Byte-Order-Mark. " "File not seekable?"); return false; @@ -107,7 +133,7 @@ bool cmListFileParser::ParseFile(const char* filename) // Verify the Byte-Order-Mark, if any. if (bom != cmListFileLexer_BOM_None && bom != cmListFileLexer_BOM_UTF8) { - cmListFileLexer_SetFileName(this->Lexer, nullptr, nullptr); + cmListFileLexer_SetFileName(this->Lexer.get(), nullptr, nullptr); this->IssueFileOpenError( "File starts with a Byte-Order-Mark that is not UTF-8."); return false; @@ -121,7 +147,7 @@ bool cmListFileParser::ParseString(const char* str, { this->FileName = virtual_filename; - if (!cmListFileLexer_SetString(this->Lexer, str)) { + if (!cmListFileLexer_SetString(this->Lexer.get(), str)) { this->IssueFileOpenError("cmListFileCache: cannot allocate buffer."); return false; } @@ -134,7 +160,8 @@ bool cmListFileParser::Parse() // Use a simple recursive-descent parser to process the token // stream. bool haveNewline = true; - while (cmListFileLexer_Token* token = cmListFileLexer_Scan(this->Lexer)) { + while (cmListFileLexer_Token* token = + cmListFileLexer_Scan(this->Lexer.get())) { if (token->type == cmListFileLexer_Token_Space) { } else if (token->type == cmListFileLexer_Token_Newline) { haveNewline = true; @@ -151,19 +178,19 @@ bool cmListFileParser::Parse() return false; } } else { - std::ostringstream error; - error << "Parse error. Expected a newline, got " - << cmListFileLexer_GetTypeAsString(this->Lexer, token->type) - << " with text \"" << token->text << "\"."; - this->IssueError(error.str()); + auto error = cmStrCat( + "Parse error. Expected a newline, got ", + cmListFileLexer_GetTypeAsString(this->Lexer.get(), token->type), + " with text \"", token->text, "\"."); + this->IssueError(error); return false; } } else { - std::ostringstream error; - error << "Parse error. Expected a command name, got " - << cmListFileLexer_GetTypeAsString(this->Lexer, token->type) - << " with text \"" << token->text << "\"."; - this->IssueError(error.str()); + auto error = cmStrCat( + "Parse error. Expected a command name, got ", + cmListFileLexer_GetTypeAsString(this->Lexer.get(), token->type), + " with text \"", token->text, "\"."); + this->IssueError(error); return false; } } @@ -181,38 +208,6 @@ bool cmListFileParser::Parse() return true; } -bool cmListFile::ParseFile(const char* filename, cmMessenger* messenger, - cmListFileBacktrace const& lfbt) -{ - if (!cmSystemTools::FileExists(filename) || - cmSystemTools::FileIsDirectory(filename)) { - return false; - } - - bool parseError = false; - - { - cmListFileParser parser(this, lfbt, messenger); - parseError = !parser.ParseFile(filename); - } - - return !parseError; -} - -bool cmListFile::ParseString(const char* str, const char* virtual_filename, - cmMessenger* messenger, - const cmListFileBacktrace& lfbt) -{ - bool parseError = false; - - { - cmListFileParser parser(this, lfbt, messenger); - parseError = !parser.ParseString(str, virtual_filename); - } - - return !parseError; -} - bool cmListFileParser::ParseFunction(const char* name, long line) { // Ininitialize a new function call. @@ -221,31 +216,27 @@ bool cmListFileParser::ParseFunction(const char* name, long line) // Command name has already been parsed. Read the left paren. cmListFileLexer_Token* token; - while ((token = cmListFileLexer_Scan(this->Lexer)) && + while ((token = cmListFileLexer_Scan(this->Lexer.get())) && token->type == cmListFileLexer_Token_Space) { } if (!token) { - std::ostringstream error; - /* clang-format off */ - error << "Unexpected end of file.\n" - << "Parse error. Function missing opening \"(\"."; - /* clang-format on */ - this->IssueError(error.str()); + this->IssueError("Unexpected end of file.\n" + "Parse error. Function missing opening \"(\"."); return false; } if (token->type != cmListFileLexer_Token_ParenLeft) { - std::ostringstream error; - error << "Parse error. Expected \"(\", got " - << cmListFileLexer_GetTypeAsString(this->Lexer, token->type) - << " with text \"" << token->text << "\"."; - this->IssueError(error.str()); + auto error = + cmStrCat("Parse error. Expected \"(\", got ", + cmListFileLexer_GetTypeAsString(this->Lexer.get(), token->type), + " with text \"", token->text, "\"."); + this->IssueError(error); return false; } // Arguments. unsigned long parenDepth = 0; this->Separation = SeparationOkay; - while ((token = cmListFileLexer_Scan(this->Lexer))) { + while ((token = cmListFileLexer_Scan(this->Lexer.get()))) { if (token->type == cmListFileLexer_Token_Space || token->type == cmListFileLexer_Token_Newline) { this->Separation = SeparationOkay; @@ -288,25 +279,26 @@ bool cmListFileParser::ParseFunction(const char* name, long line) this->Separation = SeparationError; } else { // Error. - std::ostringstream error; - error << "Parse error. Function missing ending \")\". " - << "Instead found " - << cmListFileLexer_GetTypeAsString(this->Lexer, token->type) - << " with text \"" << token->text << "\"."; - this->IssueError(error.str()); + auto error = cmStrCat( + "Parse error. Function missing ending \")\". " + "Instead found ", + cmListFileLexer_GetTypeAsString(this->Lexer.get(), token->type), + " with text \"", token->text, "\"."); + this->IssueError(error); return false; } } - std::ostringstream error; cmListFileContext lfc; lfc.FilePath = this->FileName; lfc.Line = line; cmListFileBacktrace lfbt = this->Backtrace; lfbt = lfbt.Push(lfc); - error << "Parse error. Function missing ending \")\". " - << "End of file reached."; - this->Messenger->IssueMessage(MessageType::FATAL_ERROR, error.str(), lfbt); + this->Messenger->IssueMessage( + MessageType::FATAL_ERROR, + "Parse error. Function missing ending \")\". " + "End of file reached.", + lfbt); return false; } @@ -319,49 +311,24 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, } bool isError = (this->Separation == SeparationError || delim == cmListFileArgument::Bracket); - std::ostringstream m; cmListFileContext lfc; lfc.FilePath = this->FileName; lfc.Line = token->line; cmListFileBacktrace lfbt = this->Backtrace; lfbt = lfbt.Push(lfc); - - m << "Syntax " << (isError ? "Error" : "Warning") << " in cmake code at " - << "column " << token->column << "\n" - << "Argument not separated from preceding token by whitespace."; - /* clang-format on */ + auto msg = + cmStrCat("Syntax ", (isError ? "Error" : "Warning"), + " in cmake code at column ", token->column, + "\n" + "Argument not separated from preceding token by whitespace."); if (isError) { - this->Messenger->IssueMessage(MessageType::FATAL_ERROR, m.str(), lfbt); + this->Messenger->IssueMessage(MessageType::FATAL_ERROR, msg, lfbt); return false; } - this->Messenger->IssueMessage(MessageType::AUTHOR_WARNING, m.str(), lfbt); + this->Messenger->IssueMessage(MessageType::AUTHOR_WARNING, msg, lfbt); return true; } -namespace { -enum class NestingStateEnum -{ - If, - Else, - While, - Foreach, - Function, - Macro, - Block -}; - -struct NestingState -{ - NestingStateEnum State; - cmListFileContext Context; -}; - -bool TopIs(std::vector& stack, NestingStateEnum state) -{ - return !stack.empty() && stack.back().State == state; -} -} - cm::optional cmListFileParser::CheckNesting() const { std::vector stack; @@ -455,6 +422,40 @@ cm::optional cmListFileParser::CheckNesting() const return cm::nullopt; } +} // anonymous namespace + +bool cmListFile::ParseFile(const char* filename, cmMessenger* messenger, + cmListFileBacktrace const& lfbt) +{ + if (!cmSystemTools::FileExists(filename) || + cmSystemTools::FileIsDirectory(filename)) { + return false; + } + + bool parseError = false; + + { + cmListFileParser parser(this, lfbt, messenger); + parseError = !parser.ParseFile(filename); + } + + return !parseError; +} + +bool cmListFile::ParseString(const char* str, const char* virtual_filename, + cmMessenger* messenger, + const cmListFileBacktrace& lfbt) +{ + bool parseError = false; + + { + cmListFileParser parser(this, lfbt, messenger); + parseError = !parser.ParseString(str, virtual_filename); + } + + return !parseError; +} + #include "cmConstStack.tcc" template class cmConstStack; @@ -462,9 +463,9 @@ std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc) { os << lfc.FilePath; if (lfc.Line > 0) { - os << ":" << lfc.Line; + os << ':' << lfc.Line; if (!lfc.Name.empty()) { - os << " (" << lfc.Name << ")"; + os << " (" << lfc.Name << ')'; } } else if (lfc.Line == cmListFileContext::DeferPlaceholderLine) { os << ":DEFERRED"; diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx index 1f18ff2ed..81edea5b6 100644 --- a/Source/cmLoadCommandCommand.cxx +++ b/Source/cmLoadCommandCommand.cxx @@ -55,7 +55,7 @@ struct SignalHandlerGuard { explicit SignalHandlerGuard(const char* name) { - LastName = name != nullptr ? name : "????"; + LastName = name ? name : "????"; signal(SIGSEGV, TrapsForSignals); #ifdef SIGBUS @@ -96,7 +96,7 @@ struct LoadedCommandImpl : cmLoadedCommandInfo #endif this->Destructor(this); } - if (this->Error != nullptr) { + if (this->Error) { free(this->Error); } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 94e62ff8b..42b517e58 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -500,6 +500,7 @@ void cmLocalGenerator::GenerateInstallRules() toplevel_install = 1; } file += "/cmake_install.cmake"; + this->GetGlobalGenerator()->AddInstallScript(file); cmGeneratedFileStream fout(file); fout.SetCopyIfDifferent(true); @@ -712,32 +713,40 @@ void cmLocalGenerator::GenerateInstallRules() break; } - // Record the install manifest. - if (toplevel_install) { - /* clang-format off */ + /* clang-format off */ + fout << - "if(CMAKE_INSTALL_COMPONENT)\n" - " if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n" - " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" - "${CMAKE_INSTALL_COMPONENT}.txt\")\n" - " else()\n" - " string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n" - " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" - "${CMAKE_INST_COMP_HASH}.txt\")\n" - " unset(CMAKE_INST_COMP_HASH)\n" - " endif()\n" - "else()\n" - " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" - "endif()\n" - "\n" - "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n" - " string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" + "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" " \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n" - " file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" + "if(CMAKE_INSTALL_LOCAL_ONLY)\n" + " file(WRITE \"" << + this->StateSnapshot.GetDirectory().GetCurrentBinary() << + "/install_local_manifest.txt\"\n" " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" "endif()\n"; - /* clang-format on */ - } + + if (toplevel_install) { + fout << + "if(CMAKE_INSTALL_COMPONENT)\n" + " if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n" + " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" + "${CMAKE_INSTALL_COMPONENT}.txt\")\n" + " else()\n" + " string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n" + " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" + "${CMAKE_INST_COMP_HASH}.txt\")\n" + " unset(CMAKE_INST_COMP_HASH)\n" + " endif()\n" + "else()\n" + " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" + "endif()\n" + "\n" + "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n" + " file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" + " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" + "endif()\n"; + } + /* clang-format on */ } void cmLocalGenerator::AddGeneratorTarget( @@ -902,7 +911,7 @@ std::string cmLocalGenerator::GetIncludeFlags( sysIncludeFlag = this->Makefile->GetDefinition( cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); sysIncludeFlagWarning = this->Makefile->GetDefinition( - cmStrCat("_CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING")); + cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING")); } cmValue fwSearchFlag = this->Makefile->GetDefinition( @@ -1888,6 +1897,10 @@ void cmLocalGenerator::OutputLinkLibraries( this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); } + // Add standard link directories for this language + std::string stdLinkDirString = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LINK_DIRECTORIES")); + // Add standard libraries for this language. std::string stdLibString = this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES")); @@ -1898,7 +1911,7 @@ void cmLocalGenerator::OutputLinkLibraries( frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag); linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator, - linkPath); + stdLinkDirString, linkPath); linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries); } @@ -3154,6 +3167,25 @@ void cmLocalGenerator::WriteUnitySourceInclude( unity_file << "\n"; } +namespace { +std::string unity_file_extension(std::string const& lang) +{ + std::string extension; + if (lang == "C") { + extension = "_c.c"; + } else if (lang == "CXX") { + extension = "_cxx.cxx"; + } else if (lang == "CUDA") { + extension = "_cu.cu"; + } else if (lang == "OBJC") { + extension = "_m.m"; + } else if (lang == "OBJCXX") { + extension = "_mm.mm"; + } + return extension; +} +} + std::vector cmLocalGenerator::AddUnityFilesModeAuto( cmGeneratorTarget* target, std::string const& lang, @@ -3172,17 +3204,8 @@ cmLocalGenerator::AddUnityFilesModeAuto( chunk = std::min(itemsLeft, batchSize); - std::string extension; - if (lang == "C") { - extension = "_c.c"; - } else if (lang == "CXX") { - extension = "_cxx.cxx"; - } else if (lang == "OBJC") { - extension = "_m.m"; - } else if (lang == "OBJCXX") { - extension = "_mm.mm"; - } - std::string filename = cmStrCat(filename_base, "unity_", batch, extension); + std::string filename = + cmStrCat(filename_base, "unity_", batch, unity_file_extension(lang)); auto const begin = filtered_sources.begin() + batch * batchSize; auto const end = begin + chunk; unity_files.emplace_back(this->WriteUnitySource( @@ -3220,8 +3243,8 @@ cmLocalGenerator::AddUnityFilesModeGroup( for (auto const& item : explicit_mapping) { auto const& name = item.first; - std::string filename = cmStrCat(filename_base, "unity_", name, - (lang == "C") ? "_c.c" : "_cxx.cxx"); + std::string filename = + cmStrCat(filename_base, "unity_", name, unity_file_extension(lang)); unity_files.emplace_back(this->WriteUnitySource( target, configs, cmMakeRange(item.second), beforeInclude, afterInclude, std::move(filename))); @@ -3283,7 +3306,7 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target) cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE"); cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE"); - for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX" }) { + for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX", "CUDA" }) { std::vector filtered_sources; std::copy_if(unitySources.begin(), unitySources.end(), std::back_inserter(filtered_sources), @@ -3424,7 +3447,7 @@ void cmLocalGenerator::AppendPositionIndependentLinkerFlags( } const char* PICValue = target->GetLinkPIEProperty(config); - if (PICValue == nullptr) { + if (!PICValue) { // POSITION_INDEPENDENT_CODE is not set return; } @@ -3552,7 +3575,7 @@ void cmLocalGenerator::AppendCompileOptions( std::string& options, const std::vector& options_vec, const char* regex) const { - if (regex != nullptr) { + if (regex) { // Filter flags upon specified reges. cmsys::RegularExpression r(regex); @@ -3572,7 +3595,7 @@ void cmLocalGenerator::AppendCompileOptions( std::vector>& options, const std::vector>& options_vec, const char* regex) const { - if (regex != nullptr) { + if (regex) { // Filter flags upon specified regular expressions. cmsys::RegularExpression r(regex); @@ -4369,6 +4392,7 @@ void cmLocalGenerator::GenerateFrameworkInfoPList( cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); + cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_NAME"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); mf->ConfigureFile(inFile, fname, false, false, false); } @@ -4625,6 +4649,12 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg, if (cmCustomCommand* cc = sf->GetCustomCommand()) { cc->AppendCommands(commandLines); cc->AppendDepends(depends); + if (cc->GetCodegen() && !implicit_depends.empty()) { + lg.GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + "Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom " + "command."); + } cc->AppendImplicitDepends(implicit_depends); return; } diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 7def1fe15..4a776b4df 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1763,6 +1763,21 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( this->WriteMakeRule(ruleFileStream, "The main all target", "all", depends, commands, true); + // Write the codegen rule. + if (this->GetGlobalGenerator()->CheckCMP0171()) { + recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/codegen"); + depends.clear(); + commands.clear(); + if (regenerate) { + depends.emplace_back("cmake_check_build_system"); + } + commands.push_back(this->GetRecursiveMakeCall(mf2Dir, recursiveTarget)); + AppendEcho(commands, "Finished generating code", + cmLocalUnixMakefileGenerator3::EchoColor::EchoGenerate); + this->WriteMakeRule(ruleFileStream, "The main codegen target", "codegen", + depends, commands, true); + } + // Write the clean rule. recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/clean"); commands.clear(); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 8577cc429..8be21c215 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -305,6 +305,8 @@ cmVS7FlagTable cmLocalVisualStudio7GeneratorFortranFlagTable[] = { { "Optimization", "O1", "min space", "optimizeMinSpace", 0 }, { "Optimization", "O3", "full optimize", "optimizeFull", 0 }, { "GlobalOptimizations", "Og", "global optimize", "true", 0 }, + { "InterproceduralOptimizations", "Qipo", + "Interprocedural optimization across multiple files", "ipoMultiFile", 0 }, { "InlineFunctionExpansion", "Ob0", "", "expandDisable", 0 }, { "InlineFunctionExpansion", "Ob1", "", "expandOnlyInline", 0 }, { "FavorSizeOrSpeed", "Os", "", "favorSize", 0 }, @@ -690,7 +692,13 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( this->AddCompileOptions(flags, target, langForClCompile, configName); // Check IPO related warning/error. - target->IsIPOEnabled(linkLanguage, configName); + if (target->IsIPOEnabled(linkLanguage, configName)) { + if (this->FortranProject) { + this->AppendCompileOptions(flags, + this->Makefile->GetSafeDefinition( + "CMAKE_Fortran_COMPILE_OPTIONS_IPO")); + } + } } if (this->FortranProject) { @@ -1123,7 +1131,10 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( this->WriteTargetVersionAttribute(fout, target); linkOptions.OutputFlagMap(fout, 4); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; - this->OutputLibraryDirectories(fout, cli.GetDirectories()); + std::string const& linkDirsString = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LINK_DIRECTORIES")); + this->OutputLibraryDirectories(fout, cmList(linkDirsString), + cli.GetDirectories()); fout << "\"\n"; temp = cmStrCat(target->GetPDBDirectory(configName), '/', targetNames.PDB); @@ -1206,7 +1217,10 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( this->WriteTargetVersionAttribute(fout, target); linkOptions.OutputFlagMap(fout, 4); fout << "\t\t\t\tAdditionalLibraryDirectories=\""; - this->OutputLibraryDirectories(fout, cli.GetDirectories()); + std::string const& linkDirsString = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LINK_DIRECTORIES")); + this->OutputLibraryDirectories(fout, cmList(linkDirsString), + cli.GetDirectories()); fout << "\"\n"; std::string path = this->ConvertToXMLOutputPathSingle( target->GetPDBDirectory(configName)); @@ -1265,9 +1279,9 @@ static std::string cmLocalVisualStudio7GeneratorEscapeForXML( static std::string GetEscapedPropertyIfValueNotNULL(const char* propertyValue) { - return propertyValue == nullptr - ? std::string() - : cmLocalVisualStudio7GeneratorEscapeForXML(propertyValue); + return propertyValue + ? cmLocalVisualStudio7GeneratorEscapeForXML(propertyValue) + : std::string(); } void cmLocalVisualStudio7Generator::OutputDeploymentDebuggerTool( @@ -1356,9 +1370,11 @@ void cmLocalVisualStudio7GeneratorInternals::OutputObjects( } void cmLocalVisualStudio7Generator::OutputLibraryDirectories( - std::ostream& fout, std::vector const& dirs) + std::ostream& fout, std::vector const& stdlink, + std::vector const& dirs) { const char* comma = ""; + for (std::string dir : dirs) { // Remove any trailing slash and skip empty paths. if (dir.back() == '/') { @@ -1384,6 +1400,12 @@ void cmLocalVisualStudio7Generator::OutputLibraryDirectories( << ',' << this->ConvertToXMLOutputPath(dir); comma = ","; } + + // No special processing on toolchain-defined standard link directory paths + for (const auto& dir : stdlink) { + fout << comma << this->ConvertToXMLOutputPath(dir); + comma = ","; + } } void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout, diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index bb05226b0..7c395d640 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -125,6 +125,7 @@ private: std::string const& config, cmGeneratorTarget* target); void OutputLibraryDirectories(std::ostream& fout, + std::vector const& stdlink, std::vector const& dirs); void WriteProjectSCC(std::ostream& fout, cmGeneratorTarget* target); void WriteProjectStart(std::ostream& fout, const std::string& libName, diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index ba9fab5ac..98da10dc8 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -199,12 +199,12 @@ bool cmMakefile::CheckCMP0037(std::string const& targetName, cmStateEnums::TargetType targetType) const { MessageType messageType = MessageType::AUTHOR_WARNING; - std::ostringstream e; + std::string e; bool issueMessage = false; switch (this->GetPolicyStatus(cmPolicies::CMP0037)) { case cmPolicies::WARN: if (targetType != cmStateEnums::INTERFACE_LIBRARY) { - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n"; + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0037), '\n'); issueMessage = true; } CM_FALLTHROUGH; @@ -218,11 +218,12 @@ bool cmMakefile::CheckCMP0037(std::string const& targetName, break; } if (issueMessage) { - e << "The target name \"" << targetName - << "\" is reserved or not valid for certain " - "CMake features, such as generator expressions, and may result " - "in undefined behavior."; - this->IssueMessage(messageType, e.str()); + e += + cmStrCat("The target name \"", targetName, + "\" is reserved or not valid for certain " + "CMake features, such as generator expressions, and may result " + "in undefined behavior."); + this->IssueMessage(messageType, e); if (messageType == MessageType::FATAL_ERROR) { return false; @@ -236,39 +237,37 @@ void cmMakefile::MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef, { // Warn if a _ROOT variable we may use is set. if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) { - std::ostringstream w; - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; + auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0074), '\n'); if (rootDef) { - w << "CMake variable " << rootVar << " is set to:\n" - << " " << *rootDef << "\n"; + e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, + '\n'); } if (rootEnv) { - w << "Environment variable " << rootVar << " is set to:\n" - << " " << *rootEnv << "\n"; + e += cmStrCat("Environment variable ", rootVar, " is set to:\n ", + *rootEnv, '\n'); } - w << "For compatibility, CMake is ignoring the variable."; - this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + e += "For compatibility, CMake is ignoring the variable."; + this->IssueMessage(MessageType::AUTHOR_WARNING, e); } } -void cmMakefile::MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF, - cm::optional const& rootENV) +void cmMakefile::MaybeWarnCMP0144(std::string const& rootVar, cmValue rootDef, + cm::optional const& rootEnv) { // Warn if a _ROOT variable we may use is set. - if ((rootDEF || rootENV) && this->WarnedCMP0144.insert(rootVAR).second) { - std::ostringstream w; - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0144) << "\n"; - if (rootDEF) { - w << "CMake variable " << rootVAR << " is set to:\n" - << " " << *rootDEF << "\n"; + if ((rootDef || rootEnv) && this->WarnedCMP0144.insert(rootVar).second) { + auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0144), '\n'); + if (rootDef) { + e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, + '\n'); } - if (rootENV) { - w << "Environment variable " << rootVAR << " is set to:\n" - << " " << *rootENV << "\n"; + if (rootEnv) { + e += cmStrCat("Environment variable ", rootVar, " is set to:\n ", + *rootEnv, '\n'); } - w << "For compatibility, find_package is ignoring the variable, but " + e += "For compatibility, find_package is ignoring the variable, but " "code in a .cmake module might still use it."; - this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + this->IssueMessage(MessageType::AUTHOR_WARNING, e); } } @@ -333,7 +332,6 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, } } - std::ostringstream msg; std::vector args; std::string temp; bool expand = this->GetCMakeInstance()->GetTraceExpand(); @@ -350,6 +348,7 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, } cm::optional const& deferId = bt.Top().DeferId; + std::ostringstream msg; switch (this->GetCMakeInstance()->GetTraceFormat()) { case cmake::TraceFormat::JSONv1: { #ifndef CMAKE_BOOTSTRAP @@ -370,25 +369,25 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, val["args"].append(arg); } val["time"] = cmSystemTools::GetTime(); - val["frame"] = (missing == CommandMissingFromStack::Yes ? 1 : 0) + + val["frame"] = int(missing == CommandMissingFromStack::Yes) + static_cast(this->ExecutionStatusStack.size()); - val["global_frame"] = (missing == CommandMissingFromStack::Yes ? 1 : 0) + + val["global_frame"] = int(missing == CommandMissingFromStack::Yes) + static_cast(this->RecursionDepth); msg << Json::writeString(builder, val); #endif break; } case cmake::TraceFormat::Human: - msg << full_path << "(" << lff.Line() << "):"; + msg << full_path << '(' << lff.Line() << "):"; if (deferId) { - msg << "DEFERRED:" << *deferId << ":"; + msg << "DEFERRED:" << *deferId << ':'; } - msg << " " << lff.OriginalName() << "("; + msg << " " << lff.OriginalName() << '('; for (std::string const& arg : args) { - msg << arg << " "; + msg << arg << ' '; } - msg << ")"; + msg << ')'; break; case cmake::TraceFormat::Undefined: msg << "INTERNAL ERROR: Trace format is Undefined"; @@ -435,7 +434,7 @@ public: }); #endif #ifdef CMake_ENABLE_DEBUGGER - if (this->Makefile->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->Makefile->GetCMakeInstance()->GetDebugAdapter()) { this->Makefile->GetCMakeInstance() ->GetDebugAdapter() ->OnBeginFunctionCall(mf, lfc.FilePath, lff); @@ -452,7 +451,7 @@ public: --this->Makefile->RecursionDepth; this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); #ifdef CMake_ENABLE_DEBUGGER - if (this->Makefile->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->Makefile->GetCMakeInstance()->GetDebugAdapter()) { this->Makefile->GetCMakeInstance() ->GetDebugAdapter() ->OnEndFunctionCall(); @@ -498,9 +497,9 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, // Check for maximum recursion depth. size_t depthLimit = this->GetRecursionDepthLimit(); if (this->RecursionDepth > depthLimit) { - std::ostringstream e; - e << "Maximum recursion depth of " << depthLimit << " exceeded"; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Maximum recursion depth of ", depthLimit, " exceeded")); cmSystemTools::SetFatalErrorOccurred(); return false; } @@ -652,26 +651,29 @@ void cmMakefile::IncludeScope::EnforceCMP0011() case cmPolicies::WARN: // Warn because the user did not set this policy. { - std::ostringstream w; - w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0011) << "\n" - << "The included script\n " - << this->Makefile->GetBacktrace().Top().FilePath << "\n" - << "affects policy settings. " - << "CMake is implying the NO_POLICY_SCOPE option for compatibility, " - << "so the effects are applied to the including context."; - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + auto e = cmStrCat( + cmPolicies::GetPolicyWarning(cmPolicies::CMP0011), + "\n" + "The included script\n" + " ", + this->Makefile->GetBacktrace().Top().FilePath, + "\n" + "affects policy settings. " + "CMake is implying the NO_POLICY_SCOPE option for compatibility, " + "so the effects are applied to the including context."); + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, e); } break; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: { - std::ostringstream e; - /* clang-format off */ - e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0011) << "\n" - << "The included script\n " - << this->Makefile->GetBacktrace().Top().FilePath << "\n" - << "affects policy settings, so it requires this policy to be set."; - /* clang-format on */ - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + auto e = cmStrCat( + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0011), + "\n" + "The included script\n ", + this->Makefile->GetBacktrace().Top().FilePath, + "\n" + "affects policy settings, so it requires this policy to be set."); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e); } break; case cmPolicies::OLD: case cmPolicies::NEW: @@ -694,7 +696,7 @@ bool cmMakefile::ReadDependentFile(const std::string& filename, IncludeScope incScope(this, filenametoread, noPolicyScope); #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( this, filenametoread); } @@ -704,7 +706,7 @@ bool cmMakefile::ReadDependentFile(const std::string& filename, if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); } #endif @@ -713,7 +715,7 @@ bool cmMakefile::ReadDependentFile(const std::string& filename, } #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( filenametoread, listFile.Functions); @@ -816,7 +818,7 @@ bool cmMakefile::ReadListFile(const std::string& filename) ListFileScope scope(this, filenametoread); #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( this, filenametoread); } @@ -826,7 +828,7 @@ bool cmMakefile::ReadListFile(const std::string& filename) if (!listFile.ParseFile(filenametoread.c_str(), this->GetMessenger(), this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); } #endif @@ -835,7 +837,7 @@ bool cmMakefile::ReadListFile(const std::string& filename) } #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( filenametoread, listFile.Functions); @@ -864,7 +866,7 @@ bool cmMakefile::ReadListFileAsString(const std::string& content, } #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( filenametoread, listFile.Functions); } @@ -956,22 +958,23 @@ void cmMakefile::EnforceDirectoryLevelRules() const { // Diagnose a violation of CMP0000 if necessary. if (this->CheckCMP0000) { - std::ostringstream msg; - msg << "No cmake_minimum_required command is present. " - << "A line of code such as\n" - << " cmake_minimum_required(VERSION " << cmVersion::GetMajorVersion() - << "." << cmVersion::GetMinorVersion() << ")\n" - << "should be added at the top of the file. " - << "The version specified may be lower if you wish to " - << "support older CMake versions for this project. " - << "For more information run " - << "\"cmake --help-policy CMP0000\"."; + std::string e = + cmStrCat("No cmake_minimum_required command is present. " + "A line of code such as\n" + " cmake_minimum_required(VERSION ", + cmVersion::GetMajorVersion(), '.', cmVersion::GetMinorVersion(), + ")\n" + "should be added at the top of the file. " + "The version specified may be lower if you wish to " + "support older CMake versions for this project. " + "For more information run " + "\"cmake --help-policy CMP0000\"."); switch (this->GetPolicyStatus(cmPolicies::CMP0000)) { case cmPolicies::WARN: // Warn because the user did not provide a minimum required // version. - this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, - msg.str(), this->Backtrace); + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, e, + this->Backtrace); CM_FALLTHROUGH; case cmPolicies::OLD: // OLD behavior is to use policy version 2.4 set in @@ -981,8 +984,8 @@ void cmMakefile::EnforceDirectoryLevelRules() const case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::NEW: // NEW behavior is to issue an error. - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, - msg.str(), this->Backtrace); + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, + this->Backtrace); cmSystemTools::SetFatalErrorOccurred(); break; } @@ -1052,8 +1055,13 @@ void cmMakefile::AddGeneratorAction(GeneratorAction&& action) } void cmMakefile::GeneratorAction::operator()(cmLocalGenerator& lg, - const cmListFileBacktrace& lfbt) + const cmListFileBacktrace& lfbt, + GeneratorActionWhen when) { + if (this->When != when) { + return; + } + if (cc) { CCAction(lg, lfbt, std::move(cc)); } else { @@ -1070,7 +1078,7 @@ void cmMakefile::DoGenerate(cmLocalGenerator& lg) // give all the commands a chance to do something // after the file has been parsed before generation for (auto& action : this->GeneratorActions) { - action.Value(lg, action.Backtrace); + action.Value(lg, action.Backtrace, GeneratorActionWhen::AfterConfigure); } this->GeneratorActionsInvoked = true; @@ -1104,6 +1112,14 @@ void cmMakefile::Generate(cmLocalGenerator& lg) } } +void cmMakefile::GenerateAfterGeneratorTargets(cmLocalGenerator& lg) +{ + for (auto& action : this->GeneratorActions) { + action.Value(lg, action.Backtrace, + GeneratorActionWhen::AfterGeneratorTargets); + } +} + namespace { // There are still too many implicit backtraces through cmMakefile. As a // workaround we reset the backtrace temporarily. @@ -1128,15 +1144,17 @@ bool cmMakefile::ValidateCustomCommand( const cmCustomCommandLines& commandLines) const { // TODO: More strict? - for (cmCustomCommandLine const& cl : commandLines) { - if (!cl.empty() && !cl[0].empty() && cl[0][0] == '"') { - std::ostringstream e; - e << "COMMAND may not contain literal quotes:\n " << cl[0] << "\n"; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return false; - } + const auto it = + std::find_if(commandLines.begin(), commandLines.end(), + [](const cmCustomCommandLine& cl) { + return !cl.empty() && !cl[0].empty() && cl[0][0] == '"'; + }); + if (it != commandLines.end()) { + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("COMMAND may not contain literal quotes:\n ", (*it)[0], '\n')); + return false; } - return true; } @@ -1156,10 +1174,10 @@ cmTarget* cmMakefile::GetCustomCommandTarget( if (ti == this->Targets.end()) { MessageType messageType = MessageType::AUTHOR_WARNING; bool issueMessage = false; - std::ostringstream e; + std::string e; switch (this->GetPolicyStatus(cmPolicies::CMP0040)) { case cmPolicies::WARN: - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0040) << "\n"; + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0040), '\n'); issueMessage = true; CM_FALLTHROUGH; case cmPolicies::OLD: @@ -1175,16 +1193,17 @@ cmTarget* cmMakefile::GetCustomCommandTarget( if (issueMessage) { if (cmTarget const* t = this->FindTargetToUse(target)) { if (t->IsImported()) { - e << "TARGET '" << target - << "' is IMPORTED and does not build here."; + e += cmStrCat("TARGET '", target, + "' is IMPORTED and does not build here."); } else { - e << "TARGET '" << target << "' was not created in this directory."; + e += cmStrCat("TARGET '", target, + "' was not created in this directory."); } } else { - e << "No TARGET '" << target - << "' has been created in this directory."; + e += cmStrCat("No TARGET '", target, + "' has been created in this directory."); } - this->GetCMakeInstance()->IssueMessage(messageType, e.str(), lfbt); + this->GetCMakeInstance()->IssueMessage(messageType, e, lfbt); } return nullptr; @@ -1193,21 +1212,19 @@ cmTarget* cmMakefile::GetCustomCommandTarget( cmTarget* t = &ti->second; if (objLibCommands == cmObjectLibraryCommands::Reject && t->GetType() == cmStateEnums::OBJECT_LIBRARY) { - std::ostringstream e; - e << "Target \"" << target - << "\" is an OBJECT library " - "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."; - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - lfbt); + auto e = cmStrCat( + "Target \"", target, + "\" is an OBJECT library " + "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."); + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, lfbt); return nullptr; } if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - std::ostringstream e; - e << "Target \"" << target - << "\" is an INTERFACE library " - "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."; - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - lfbt); + auto e = cmStrCat( + "Target \"", target, + "\" is an INTERFACE library " + "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."); + this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, lfbt); return nullptr; } @@ -1751,7 +1768,7 @@ void cmMakefile::Configure() this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart); #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( this, currentStart); } @@ -1761,7 +1778,7 @@ void cmMakefile::Configure() if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(), this->Backtrace)) { #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); } #endif @@ -1770,7 +1787,7 @@ void cmMakefile::Configure() } #ifdef CMake_ENABLE_DEBUGGER - if (this->GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (this->GetCMakeInstance()->GetDebugAdapter()) { this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( currentStart, listFile.Functions); @@ -1889,35 +1906,31 @@ void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) cmStrCat(currentStart, "/CMakeLists.txt"); if (!cmSystemTools::FileExists(currentStartFile, true)) { // The file is missing. Check policy CMP0014. - std::ostringstream e; - /* clang-format off */ - e << "The source directory\n" - << " " << currentStart << "\n" - << "does not contain a CMakeLists.txt file."; + auto e = cmStrCat("The source directory\n ", currentStart, + "\n" + "does not contain a CMakeLists.txt file."); /* clang-format on */ switch (this->GetPolicyStatus(cmPolicies::CMP0014)) { case cmPolicies::WARN: // Print the warning. - /* clang-format off */ - e << "\n" - << "CMake does not support this case but it used " - << "to work accidentally and is being allowed for " - << "compatibility." - << "\n" - << cmPolicies::GetPolicyWarning(cmPolicies::CMP0014); - /* clang-format on */ - this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + e += cmStrCat("\n" + "CMake does not support this case but it used " + "to work accidentally and is being allowed for " + "compatibility.\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0014)); + this->IssueMessage(MessageType::AUTHOR_WARNING, e); CM_FALLTHROUGH; case cmPolicies::OLD: // OLD behavior does not warn. break; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: - e << "\n" << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0014); + e += cmStrCat('\n', + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0014)); CM_FALLTHROUGH; case cmPolicies::NEW: // NEW behavior prints the error. - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + this->IssueMessage(MessageType::FATAL_ERROR, e); break; } return; @@ -1926,7 +1939,7 @@ void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) mf->Configure(); if (this->GetCMakeInstance()->GetDebugOutput()) { - std::string msg = + auto msg = cmStrCat(" Returning to ", this->GetCurrentSourceDirectory()); cmSystemTools::Message(msg); } @@ -2129,9 +2142,8 @@ void cmMakefile::MaybeWarnUninitialized(std::string const& variable, !this->VariableInitialized(variable)) { if (this->CheckSystemVars || (sourceFilename && this->IsProjectFile(sourceFilename))) { - std::ostringstream msg; - msg << "uninitialized variable \'" << variable << "\'"; - this->IssueMessage(MessageType::AUTHOR_WARNING, msg.str()); + this->IssueMessage(MessageType::AUTHOR_WARNING, + cmStrCat("uninitialized variable '", variable, '\'')); } } } @@ -2298,11 +2310,11 @@ cmSourceGroup* cmMakefile::GetSourceGroup( } } - if (sg != nullptr) { + if (sg) { // iterate through its children to find match source group for (unsigned int i = 1; i < name.size(); ++i) { sg = sg->LookupChild(name[i]); - if (sg == nullptr) { + if (!sg) { break; } } @@ -2327,7 +2339,7 @@ void cmMakefile::AddSourceGroup(const std::vector& name, for (i = lastElement; i >= 0; --i) { currentName.assign(name.begin(), name.begin() + i + 1); sg = this->GetSourceGroup(currentName); - if (sg != nullptr) { + if (sg) { break; } } @@ -2366,7 +2378,7 @@ cmSourceGroup* cmMakefile::GetOrCreateSourceGroup( const std::vector& folders) { cmSourceGroup* sg = this->GetSourceGroup(folders); - if (sg == nullptr) { + if (!sg) { this->AddSourceGroup(folders); sg = this->GetSourceGroup(folders); } @@ -2427,19 +2439,16 @@ void cmMakefile::ExpandVariablesCMP0019() if (pol != cmPolicies::OLD && pol != cmPolicies::WARN) { return; } - std::ostringstream w; + + std::string e; cmValue includeDirs = this->GetProperty("INCLUDE_DIRECTORIES"); if (includeDirs && mightExpandVariablesCMP0019(includeDirs->c_str())) { std::string dirs = *includeDirs; this->ExpandVariablesInString(dirs, true, true); if (pol == cmPolicies::WARN && dirs != *includeDirs) { - /* clang-format off */ - w << "Evaluated directory INCLUDE_DIRECTORIES\n" - << " " << *includeDirs << "\n" - << "as\n" - << " " << dirs << "\n"; - /* clang-format on */ + e = cmStrCat("Evaluated directory INCLUDE_DIRECTORIES\n ", *includeDirs, + "\nas\n ", dirs, '\n'); } this->SetProperty("INCLUDE_DIRECTORIES", dirs); } @@ -2456,12 +2465,9 @@ void cmMakefile::ExpandVariablesCMP0019() std::string dirs = *includeDirs; this->ExpandVariablesInString(dirs, true, true); if (pol == cmPolicies::WARN && dirs != *includeDirs) { - /* clang-format off */ - w << "Evaluated target " << t.GetName() << " INCLUDE_DIRECTORIES\n" - << " " << *includeDirs << "\n" - << "as\n" - << " " << dirs << "\n"; - /* clang-format on */ + e += cmStrCat("Evaluated target ", t.GetName(), + " INCLUDE_DIRECTORIES\n ", *includeDirs, "\nas\n ", + dirs, '\n'); } t.SetProperty("INCLUDE_DIRECTORIES", dirs); } @@ -2473,12 +2479,8 @@ void cmMakefile::ExpandVariablesCMP0019() const std::string orig = d; this->ExpandVariablesInString(d, true, true); if (pol == cmPolicies::WARN && d != orig) { - /* clang-format off */ - w << "Evaluated link directories\n" - << " " << orig << "\n" - << "as\n" - << " " << d << "\n"; - /* clang-format on */ + e += cmStrCat("Evaluated link directories\n ", orig, "\nas\n ", d, + '\n'); } } } @@ -2496,27 +2498,20 @@ void cmMakefile::ExpandVariablesCMP0019() const std::string orig = libName; this->ExpandVariablesInString(libName, true, true); if (pol == cmPolicies::WARN && libName != orig) { - /* clang-format off */ - w << "Evaluated link library\n" - << " " << orig << "\n" - << "as\n" - << " " << libName << "\n"; - /* clang-format on */ + e += cmStrCat("Evaluated link library\n ", orig, "\nas\n ", + libName, '\n'); } } } } - if (!w.str().empty()) { - std::ostringstream m; - /* clang-format off */ - m << cmPolicies::GetPolicyWarning(cmPolicies::CMP0019) - << "\n" - << "The following variable evaluations were encountered:\n" - << w.str(); - /* clang-format on */ - this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, - m.str(), this->Backtrace); + if (!e.empty()) { + auto m = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0019), + "\n" + "The following variable evaluations were encountered:\n", + e); + this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING, m, + this->Backtrace); } } @@ -2621,6 +2616,14 @@ bool cmMakefile::PlatformIsAppleSimulator() const .count(this->GetAppleSDKType()); } +bool cmMakefile::PlatformIsAppleCatalyst() const +{ + std::string systemName; + systemName = this->GetSafeDefinition("CMAKE_SYSTEM_NAME"); + systemName = cmSystemTools::LowerCase(systemName); + return systemName == "ios" && this->GetAppleSDKType() == AppleSDK::MacOS; +} + bool cmMakefile::PlatformSupportsAppleTextStubs() const { return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI"); @@ -2810,33 +2813,28 @@ const std::string& cmMakefile::ExpandVariablesInString( } // ...otherwise, see if there's a difference that needs to be warned about. else if (compareResults && (newResult != source || newError != mtype)) { - std::string msg = + auto msg = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0053), '\n'); std::string msg_input = original; cmSystemTools::ReplaceString(msg_input, "\n", "\n "); - msg += "For input:\n '"; - msg += msg_input; - msg += "'\n"; std::string msg_old = source; cmSystemTools::ReplaceString(msg_old, "\n", "\n "); - msg += "the old evaluation rules produce:\n '"; - msg += msg_old; - msg += "'\n"; + + msg += cmStrCat("For input:\n '", msg_input, "'\n", + "the old evaluation rules produce:\n '", msg_old, "'\n"); if (newError == mtype) { std::string msg_new = newResult; cmSystemTools::ReplaceString(msg_new, "\n", "\n "); - msg += "but the new evaluation rules produce:\n '"; - msg += msg_new; - msg += "'\n"; + msg += + cmStrCat("but the new evaluation rules produce:\n '", msg_new, "'\n"); } else { std::string msg_err = newErrorstr; cmSystemTools::ReplaceString(msg_err, "\n", "\n "); - msg += "but the new evaluation rules produce an error:\n "; - msg += msg_err; - msg += "\n"; + msg += cmStrCat("but the new evaluation rules produce an error:\n ", + msg_err, '\n'); } msg += @@ -2916,18 +2914,14 @@ MessageType cmMakefile::ExpandVariablesInStringOld( source = parser.GetResult(); } else { // Construct the main error message. - std::ostringstream error; - error << "Syntax error in cmake code "; + std::string error = "Syntax error in cmake code "; if (filename && line > 0) { // This filename and line number may be more specific than the // command context because one command invocation can have // arguments on multiple lines. - error << "at\n" - << " " << filename << ":" << line << "\n"; + error += cmStrCat("at\n ", filename, ':', line, '\n'); } - error << "when parsing string\n" - << " " << source << "\n"; - error << emsg; + error += cmStrCat("when parsing string\n ", source, '\n', emsg); // If the parser failed ("res" is false) then this is a real // argument parsing error, so the policy applies. Otherwise the @@ -2940,7 +2934,8 @@ MessageType cmMakefile::ExpandVariablesInStringOld( // decide whether it is an error. switch (this->GetPolicyStatus(cmPolicies::CMP0010)) { case cmPolicies::WARN: - error << "\n" << cmPolicies::GetPolicyWarning(cmPolicies::CMP0010); + error += + cmStrCat('\n', cmPolicies::GetPolicyWarning(cmPolicies::CMP0010)); CM_FALLTHROUGH; case cmPolicies::OLD: // OLD behavior is to just warn and continue. @@ -2948,15 +2943,15 @@ MessageType cmMakefile::ExpandVariablesInStringOld( break; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: - error << "\n" - << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0010); + error += cmStrCat( + '\n', cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0010)); break; case cmPolicies::NEW: // NEW behavior is to report the error. break; } } - errorstr = error.str(); + errorstr = std::move(error); } return mtype; } @@ -3237,10 +3232,10 @@ MessageType cmMakefile::ExpandVariablesInStringNew( if (nextAt && nextAt != in + 1 && nextAt == in + 1 + - strspn(in + 1, - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789/_.+-")) { + std::strspn(in + 1, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789/_.+-")) { std::string variable(in + 1, nextAt - in - 1); std::string varresult; @@ -3272,11 +3267,10 @@ MessageType cmMakefile::ExpandVariablesInStringNew( if (!openstack.empty() && !(isalnum(inc) || inc == '_' || inc == '/' || inc == '.' || inc == '+' || inc == '-')) { - errorstr += "Invalid character (\'"; + errorstr += "Invalid character ('"; errorstr += inc; result.append(last, in - last); - errorstr += cmStrCat("\') in a variable name: " - "'", + errorstr += cmStrCat("') in a variable name: '", result.substr(openstack.back().loc), '\''); mtype = MessageType::FATAL_ERROR; error = true; @@ -3297,20 +3291,15 @@ MessageType cmMakefile::ExpandVariablesInStringNew( } if (error) { - std::ostringstream emsg; - emsg << "Syntax error in cmake code "; + std::string e = "Syntax error in cmake code "; if (filename) { // This filename and line number may be more specific than the // command context because one command invocation can have // arguments on multiple lines. - emsg << "at\n" - << " " << filename << ":" << line << "\n"; + e += cmStrCat("at\n ", filename, ':', line, '\n'); } - emsg << "when parsing string\n" - << " " << source << "\n"; - emsg << errorstr; + errorstr = cmStrCat(e, "when parsing string\n ", source, '\n', errorstr); mtype = MessageType::FATAL_ERROR; - errorstr = emsg.str(); } else { // Append the rest of the unchanged part of the string. result.append(last); @@ -3418,8 +3407,8 @@ void cmMakefile::PopFunctionBlockerBarrier(bool reportError) std::ostringstream e; /* clang-format off */ e << "A logical block opening on the line\n" - << " " << lfc << "\n" - << "is not closed."; + " " << lfc << "\n" + "is not closed."; /* clang-format on */ this->IssueMessage(MessageType::FATAL_ERROR, e.str()); reportError = false; @@ -3560,13 +3549,9 @@ void cmMakefile::SetScriptModeFile(std::string const& scriptfile) void cmMakefile::SetArgcArgv(const std::vector& args) { this->AddDefinition("CMAKE_ARGC", std::to_string(args.size())); - // this->MarkVariableAsUsed("CMAKE_ARGC"); - for (unsigned int t = 0; t < args.size(); ++t) { - std::ostringstream tmpStream; - tmpStream << "CMAKE_ARGV" << t; - this->AddDefinition(tmpStream.str(), args[t]); - // this->MarkVariableAsUsed(tmpStream.str().c_str()); + for (auto i = 0u; i < args.size(); ++i) { + this->AddDefinition(cmStrCat("CMAKE_ARGV", i), args[i]); } } @@ -3900,7 +3885,7 @@ void cmMakefile::DisplayStatus(const std::string& message, float s) const cm->UpdateProgress(message, s); #ifdef CMake_ENABLE_DEBUGGER - if (cm->GetDebugAdapter() != nullptr) { + if (cm->GetDebugAdapter()) { cm->GetDebugAdapter()->OnMessageOutput(MessageType::MESSAGE, message); } #endif @@ -3940,7 +3925,7 @@ std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, break; } if (debug) { - debugBuffer = cmStrCat(debugBuffer, " ", itempl, "\n"); + debugBuffer = cmStrCat(debugBuffer, " ", itempl, '\n'); } } } @@ -3951,7 +3936,7 @@ std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot); if (!cmSystemTools::FileExists(moduleInCMakeRoot)) { if (debug) { - debugBuffer = cmStrCat(debugBuffer, " ", moduleInCMakeRoot, "\n"); + debugBuffer = cmStrCat(debugBuffer, " ", moduleInCMakeRoot, '\n'); } moduleInCMakeRoot.clear(); } @@ -3972,16 +3957,12 @@ std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) { switch (this->GetPolicyStatus(cmPolicies::CMP0017)) { case cmPolicies::WARN: { - std::ostringstream e; - /* clang-format off */ - e << "File " << *currentFile << " includes " - << moduleInCMakeModulePath - << " (found via CMAKE_MODULE_PATH) which shadows " - << moduleInCMakeRoot << ". This may cause errors later on .\n" - << cmPolicies::GetPolicyWarning(cmPolicies::CMP0017); - /* clang-format on */ - - this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + auto e = cmStrCat( + "File ", *currentFile, " includes ", moduleInCMakeModulePath, + " (found via CMAKE_MODULE_PATH) which shadows ", moduleInCMakeRoot, + ". This may cause errors later on .\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0017)); + this->IssueMessage(MessageType::AUTHOR_WARNING, e); CM_FALLTHROUGH; } case cmPolicies::OLD: @@ -4028,8 +4009,8 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, if (!def.IsOff()) { const std::string indentation = this->cmDefineRegex.match(1); cmSystemTools::ReplaceString(line, - cmStrCat("#", indentation, "cmakedefine"), - cmStrCat("#", indentation, "define")); + cmStrCat('#', indentation, "cmakedefine"), + cmStrCat('#', indentation, "define")); output += line; } else { output += "/* #undef "; @@ -4040,8 +4021,8 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, const std::string indentation = this->cmDefine01Regex.match(1); cmValue def = this->GetDefinition(this->cmDefine01Regex.match(2)); cmSystemTools::ReplaceString(line, - cmStrCat("#", indentation, "cmakedefine01"), - cmStrCat("#", indentation, "define")); + cmStrCat('#', indentation, "cmakedefine01"), + cmStrCat('#', indentation, "define")); output += line; if (!def.IsOff()) { output += " 1"; @@ -4053,7 +4034,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, } if (haveNewline) { - output += "\n"; + output += '\n'; } // Move to the next line. @@ -4108,76 +4089,86 @@ int cmMakefile::ConfigureFile(const std::string& infile, } if (copyonly) { - if (!cmSystemTools::CopyFileIfDifferent(sinfile, soutfile)) { - this->IssueMessage(MessageType::FATAL_ERROR, - cmSystemTools::GetLastSystemError()); - return 0; - } - if (!cmSystemTools::SetPermissions(soutfile, permissions)) { - this->IssueMessage(MessageType::FATAL_ERROR, - cmSystemTools::GetLastSystemError()); - return 0; - } - } else { - std::string newLineCharacters; - std::ios::openmode omode = std::ios::out | std::ios::trunc; - if (newLine.IsValid()) { - newLineCharacters = newLine.GetCharacters(); - omode |= std::ios::binary; - } else { - newLineCharacters = "\n"; - } - std::string tempOutputFile = cmStrCat(soutfile, ".tmp"); - cmsys::ofstream fout(tempOutputFile.c_str(), omode); - if (!fout) { - cmSystemTools::Error("Could not open file for write in copy operation " + - tempOutputFile); - cmSystemTools::ReportLastSystemError(""); - return 0; - } - cmsys::ifstream fin(sinfile.c_str()); - if (!fin) { - cmSystemTools::Error("Could not open file for read in copy operation " + - sinfile); - return 0; - } - - cmsys::FStream::BOM bom = cmsys::FStream::ReadBOM(fin); - if (bom != cmsys::FStream::BOM_None && bom != cmsys::FStream::BOM_UTF8) { - std::ostringstream e; - e << "File starts with a Byte-Order-Mark that is not UTF-8:\n " - << sinfile; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return 0; - } - // rewind to copy BOM to output file - fin.seekg(0); - - // now copy input to output and expand variables in the - // input file at the same time - std::string inLine; - std::string outLine; - while (cmSystemTools::GetLineFromStream(fin, inLine)) { - outLine.clear(); - this->ConfigureString(inLine, outLine, atOnly, escapeQuotes); - fout << outLine << newLineCharacters; - } - // close the files before attempting to copy - fin.close(); - fout.close(); - if (!cmSystemTools::CopyFileIfDifferent(tempOutputFile, soutfile)) { - this->IssueMessage(MessageType::FATAL_ERROR, - cmSystemTools::GetLastSystemError()); + const auto copy_status = + cmSystemTools::CopyFileIfDifferent(sinfile, soutfile); + if (!copy_status) { + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Fail to copy ", + copy_status.Path == cmsys::SystemTools::CopyStatus::SourcePath + ? "source" + : "destination", + "file: ", copy_status.GetString())); res = 0; } else { - if (!cmSystemTools::SetPermissions(soutfile, permissions)) { - this->IssueMessage(MessageType::FATAL_ERROR, - cmSystemTools::GetLastSystemError()); + const auto status = cmSystemTools::SetPermissions(soutfile, permissions); + if (!status) { + this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); res = 0; } } - cmSystemTools::RemoveFile(tempOutputFile); + return res; + } + + std::string newLineCharacters; + std::ios::openmode omode = std::ios::out | std::ios::trunc; + if (newLine.IsValid()) { + newLineCharacters = newLine.GetCharacters(); + omode |= std::ios::binary; + } else { + newLineCharacters = "\n"; + } + std::string tempOutputFile = cmStrCat(soutfile, ".tmp"); + cmsys::ofstream fout(tempOutputFile.c_str(), omode); + if (!fout) { + cmSystemTools::Error("Could not open file for write in copy operation " + + tempOutputFile); + cmSystemTools::ReportLastSystemError(""); + return 0; + } + cmsys::ifstream fin(sinfile.c_str()); + if (!fin) { + cmSystemTools::Error("Could not open file for read in copy operation " + + sinfile); + return 0; + } + + cmsys::FStream::BOM bom = cmsys::FStream::ReadBOM(fin); + if (bom != cmsys::FStream::BOM_None && bom != cmsys::FStream::BOM_UTF8) { + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("File starts with a Byte-Order-Mark that is not UTF-8:\n ", + sinfile)); + return 0; + } + // rewind to copy BOM to output file + fin.seekg(0); + + // now copy input to output and expand variables in the + // input file at the same time + std::string inLine; + std::string outLine; + while (cmSystemTools::GetLineFromStream(fin, inLine)) { + outLine.clear(); + this->ConfigureString(inLine, outLine, atOnly, escapeQuotes); + fout << outLine << newLineCharacters; + } + // close the files before attempting to copy + fin.close(); + fout.close(); + + auto status = cmSystemTools::MoveFileIfDifferent(tempOutputFile, soutfile); + if (!status) { + this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); + res = 0; + } else { + status = cmSystemTools::SetPermissions(soutfile, permissions); + if (!status) { + this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); + res = 0; + } } + return res; } @@ -4286,29 +4277,23 @@ void cmMakefile::AddCMakeDependFilesFromUser() std::string cmMakefile::FormatListFileStack() const { std::vector listFiles; - cmStateSnapshot snp = this->StateSnapshot; - while (snp.IsValid()) { - listFiles.push_back(snp.GetExecutionListFile()); - snp = snp.GetCallStackParent(); - } - std::reverse(listFiles.begin(), listFiles.end()); - std::ostringstream tmp; - size_t depth = listFiles.size(); - if (depth > 0) { - auto it = listFiles.end(); - do { - if (depth != listFiles.size()) { - tmp << "\n "; - } - --it; - tmp << "["; - tmp << depth; - tmp << "]\t"; - tmp << *it; - depth--; - } while (it != listFiles.begin()); + for (auto snp = this->StateSnapshot; snp.IsValid(); + snp = snp.GetCallStackParent()) { + listFiles.emplace_back(snp.GetExecutionListFile()); + } + + if (listFiles.empty()) { + return {}; } - return tmp.str(); + + auto depth = 1; + std::transform(listFiles.begin(), listFiles.end(), listFiles.begin(), + [&depth](const std::string& file) { + return cmStrCat('[', depth++, "]\t", file); + }); + + return cmJoinStrings(cmMakeRange(listFiles.rbegin(), listFiles.rend()), + "\n "_s, {}); } void cmMakefile::PushScope() @@ -4340,9 +4325,9 @@ void cmMakefile::RaiseScope(const std::string& var, const char* varDef) } if (!this->StateSnapshot.RaiseScope(var, varDef)) { - std::ostringstream m; - m << "Cannot set \"" << var << "\": current scope has no parent."; - this->IssueMessage(MessageType::AUTHOR_WARNING, m.str()); + this->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("Cannot set \"", var, "\": current scope has no parent.")); return; } @@ -4427,10 +4412,8 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, bool isCustom) const { if (this->IsAlias(name)) { - std::ostringstream e; - e << "cannot create target \"" << name - << "\" because an alias with the same name already exists."; - msg = e.str(); + msg = cmStrCat("cannot create target \"", name, + "\" because an alias with the same name already exists."); return false; } if (cmTarget* existing = this->FindTargetToUse(name)) { @@ -4439,10 +4422,9 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, if (existing->IsImported()) { // Imported targets were not supported in previous versions. // This is new code, so we can make it an error. - std::ostringstream e; - e << "cannot create target \"" << name - << "\" because an imported target with the same name already exists."; - msg = e.str(); + msg = cmStrCat( + "cannot create target \"", name, + "\" because an imported target with the same name already exists."); return false; } // target names must be globally unique @@ -4478,7 +4460,7 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, std::ostringstream e; e << "cannot create target \"" << name << "\" because another target with the same name already exists. " - << "The existing target is "; + "The existing target is "; switch (existing->GetType()) { case cmStateEnums::EXECUTABLE: e << "an executable "; @@ -4502,8 +4484,9 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, break; } e << "created in source directory \"" - << existing->GetMakefile()->GetCurrentSourceDirectory() << "\". " - << "See documentation for policy CMP0002 for more details."; + << existing->GetMakefile()->GetCurrentSourceDirectory() + << "\". " + "See documentation for policy CMP0002 for more details."; msg = e.str(); return false; } @@ -4518,43 +4501,48 @@ bool cmMakefile::EnforceUniqueDir(const std::string& srcPath, if (gg->BinaryDirectoryIsNew(binPath)) { return true; } - std::ostringstream e; + std::string e; switch (this->GetPolicyStatus(cmPolicies::CMP0013)) { case cmPolicies::WARN: // Print the warning. - /* clang-format off */ - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0013) - << "\n" - << "The binary directory\n" - << " " << binPath << "\n" - << "is already used to build a source directory. " - << "This command uses it to build source directory\n" - << " " << srcPath << "\n" - << "which can generate conflicting build files. " - << "CMake does not support this use case but it used " - << "to work accidentally and is being allowed for " - << "compatibility."; - /* clang-format on */ - this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0013), + "\n" + "The binary directory\n" + " ", + binPath, + "\n" + "is already used to build a source directory. " + "This command uses it to build source directory\n" + " ", + srcPath, + "\n" + "which can generate conflicting build files. " + "CMake does not support this use case but it used " + "to work accidentally and is being allowed for " + "compatibility."); + this->IssueMessage(MessageType::AUTHOR_WARNING, e); CM_FALLTHROUGH; case cmPolicies::OLD: // OLD behavior does not warn. return true; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: - e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0013) << "\n"; + e = cmStrCat(cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0013), + '\n'); CM_FALLTHROUGH; case cmPolicies::NEW: // NEW behavior prints the error. - /* clang-format off */ - e << "The binary directory\n" - << " " << binPath << "\n" - << "is already used to build a source directory. " - << "It cannot be used to build source directory\n" - << " " << srcPath << "\n" - << "Specify a unique binary directory name."; - /* clang-format on */ - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + e += cmStrCat("The binary directory\n" + " ", + binPath, + "\n" + "is already used to build a source directory. " + "It cannot be used to build source directory\n" + " ", + srcPath, + "\n" + "Specify a unique binary directory name."); + this->IssueMessage(MessageType::FATAL_ERROR, e); break; } @@ -4637,9 +4625,9 @@ bool cmMakefile::SetPolicy(const char* id, cmPolicies::PolicyStatus status) { cmPolicies::PolicyID pid; if (!cmPolicies::GetPolicyID(id, /* out */ pid)) { - std::ostringstream e; - e << "Policy \"" << id << "\" is not known to this version of CMake."; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); + this->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Policy \"", id, "\" is not known to this version of CMake.")); return false; } return this->SetPolicy(pid, status); @@ -4657,7 +4645,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0128 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0129 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index ea0b4c662..6e1739e67 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -143,6 +143,14 @@ public: bool EnforceUniqueName(std::string const& name, std::string& msg, bool isCustom = false) const; + enum class GeneratorActionWhen + { + // Run after all CMake code has been parsed. + AfterConfigure, + // Run after generator targets have been constructed. + AfterGeneratorTargets, + }; + class GeneratorAction { using ActionT = @@ -152,20 +160,29 @@ public: std::unique_ptr cc)>; public: - GeneratorAction(ActionT&& action) - : Action(std::move(action)) + GeneratorAction( + ActionT&& action, + GeneratorActionWhen when = GeneratorActionWhen::AfterConfigure) + : When(when) + , Action(std::move(action)) { } - GeneratorAction(std::unique_ptr tcc, CCActionT&& action) - : CCAction(std::move(action)) + GeneratorAction( + std::unique_ptr tcc, CCActionT&& action, + GeneratorActionWhen when = GeneratorActionWhen::AfterConfigure) + : When(when) + , CCAction(std::move(action)) , cc(std::move(tcc)) { } - void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt); + void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, + GeneratorActionWhen when); private: + GeneratorActionWhen When; + ActionT Action; // FIXME: Use std::variant @@ -190,6 +207,7 @@ public: * the makefile. */ void Generate(cmLocalGenerator& lg); + void GenerateAfterGeneratorTargets(cmLocalGenerator& lg); /** * Get the target for PRE_BUILD, PRE_LINK, or POST_BUILD commands. @@ -577,6 +595,9 @@ public: /** Return whether the target platform is an Apple simulator. */ bool PlatformIsAppleSimulator() const; + /** Return whether the target platform is an Apple catalyst. */ + bool PlatformIsAppleCatalyst() const; + /** Return whether the target platform supports generation of text base stubs (.tbd file) describing exports (Apple specific). */ bool PlatformSupportsAppleTextStubs() const; diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 481c52d8a..225e63baf 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -169,6 +169,11 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink) std::string linkRuleVar = cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_LIBRARY"); + if (this->GeneratorTarget->IsArchivedAIXSharedLibrary()) { + linkRuleVar = + cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_LIBRARY_ARCHIVE"); + } + std::string extraFlags; this->GetTargetLinkFlags(extraFlags, linkLanguage); this->LocalGenerator->AddConfigVariableFlags( @@ -788,7 +793,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( vars.LinkLibraries = linkLibs.c_str(); vars.ObjectsQuoted = buildObjs.c_str(); std::string targetOutSOName; - if (this->GeneratorTarget->HasSOName(this->GetConfigName())) { + if (this->GeneratorTarget->HasSOName(this->GetConfigName()) || + this->GeneratorTarget->IsArchivedAIXSharedLibrary()) { vars.SONameFlag = this->Makefile->GetSONameFlag(linkLanguage); targetOutSOName = this->LocalGenerator->ConvertToOutputFormat( this->TargetNames.SharedObject, cmOutputConverter::SHELL); diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index d5c50bcfc..9d0d46614 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -251,6 +251,8 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() std::vector customCommands; this->GeneratorTarget->GetCustomCommands(customCommands, this->GetConfigName()); + std::vector codegen_depends; + codegen_depends.reserve(customCommands.size()); for (cmSourceFile const* sf : customCommands) { if (this->CMP0113New && !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget) @@ -273,6 +275,33 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct)); } } + + if (ccg.GetCC().GetCodegen()) { + std::string const& output = ccg.GetOutputs().front(); + + // We always attach the actual commands to the first output. + codegen_depends.emplace_back(output); + } + } + + // Some make tools need a special dependency for an empty rule. + if (codegen_depends.empty()) { + std::string hack = this->GlobalGenerator->GetEmptyRuleHackDepends(); + if (!hack.empty()) { + codegen_depends.emplace_back(std::move(hack)); + } + } + + // Construct the codegen target. + { + std::string const codegenTarget = cmStrCat( + this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), + "/codegen"); + + // Write the rule. + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, + codegenTarget, codegen_depends, {}, + true); } // Add byproducts from build events to the clean rules @@ -2177,13 +2206,14 @@ void cmMakefileTargetGenerator::CreateLinkLibs( bool useResponseFile, std::vector& makefile_depends, std::string const& linkLanguage, ResponseFlagFor responseMode) { - std::string frameworkPath; - std::string linkPath; - cmComputeLinkInformation* pcli = - this->GeneratorTarget->GetLinkInformation(this->GetConfigName()); - this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, - frameworkPath, linkPath); - linkLibs = frameworkPath + linkPath + linkLibs; + if (cmComputeLinkInformation* pcli = + this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) { + std::string frameworkPath; + std::string linkPath; + this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, + frameworkPath, linkPath); + linkLibs = frameworkPath + linkPath + linkLibs; + } if (useResponseFile && linkLibs.find_first_not_of(' ') != std::string::npos) { diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index 68b3a5d2e..a4b25384b 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -208,7 +208,7 @@ bool cmMessageCommand(std::vector const& args, case Message::LogLevel::LOG_NOTICE: cmSystemTools::Message(IndentText(message, mf)); #ifdef CMake_ENABLE_DEBUGGER - if (mf.GetCMakeInstance()->GetDebugAdapter() != nullptr) { + if (mf.GetCMakeInstance()->GetDebugAdapter()) { mf.GetCMakeInstance()->GetDebugAdapter()->OnMessageOutput(type, message); } diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx index b4ea71cf4..01ff7f083 100644 --- a/Source/cmMessenger.cxx +++ b/Source/cmMessenger.cxx @@ -21,66 +21,31 @@ # include "cmDebuggerAdapter.h" #endif -MessageType cmMessenger::ConvertMessageType(MessageType t) const -{ - if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) { - if (this->GetDevWarningsAsErrors()) { - return MessageType::AUTHOR_ERROR; - } - return MessageType::AUTHOR_WARNING; - } - if (t == MessageType::DEPRECATION_WARNING || - t == MessageType::DEPRECATION_ERROR) { - if (this->GetDeprecatedWarningsAsErrors()) { - return MessageType::DEPRECATION_ERROR; - } - return MessageType::DEPRECATION_WARNING; - } - return t; -} - -bool cmMessenger::IsMessageTypeVisible(MessageType t) const -{ - if (t == MessageType::DEPRECATION_ERROR) { - return this->GetDeprecatedWarningsAsErrors(); - } - if (t == MessageType::DEPRECATION_WARNING) { - return !this->GetSuppressDeprecatedWarnings(); - } - if (t == MessageType::AUTHOR_ERROR) { - return this->GetDevWarningsAsErrors(); - } - if (t == MessageType::AUTHOR_WARNING) { - return !this->GetSuppressDevWarnings(); - } - - return true; -} - -static bool printMessagePreamble(MessageType t, std::ostream& msg) +namespace { +const char* getMessageTypeStr(MessageType t) { - // Construct the message header. - if (t == MessageType::FATAL_ERROR) { - msg << "CMake Error"; - } else if (t == MessageType::INTERNAL_ERROR) { - msg << "CMake Internal Error (please report a bug)"; - } else if (t == MessageType::LOG) { - msg << "CMake Debug Log"; - } else if (t == MessageType::DEPRECATION_ERROR) { - msg << "CMake Deprecation Error"; - } else if (t == MessageType::DEPRECATION_WARNING) { - msg << "CMake Deprecation Warning"; - } else if (t == MessageType::AUTHOR_WARNING) { - msg << "CMake Warning (dev)"; - } else if (t == MessageType::AUTHOR_ERROR) { - msg << "CMake Error (dev)"; - } else { - msg << "CMake Warning"; + switch (t) { + case MessageType::FATAL_ERROR: + return "Error"; + case MessageType::INTERNAL_ERROR: + return "Internal Error (please report a bug)"; + case MessageType::LOG: + return "Debug Log"; + case MessageType::DEPRECATION_ERROR: + return "Deprecation Error"; + case MessageType::DEPRECATION_WARNING: + return "Deprecation Warning"; + case MessageType::AUTHOR_WARNING: + return "Warning (dev)"; + case MessageType::AUTHOR_ERROR: + return "Error (dev)"; + default: + break; } - return true; + return "Warning"; } -static int getMessageColor(MessageType t) +int getMessageColor(MessageType t) { switch (t) { case MessageType::INTERNAL_ERROR: @@ -95,7 +60,7 @@ static int getMessageColor(MessageType t) } } -static void printMessageText(std::ostream& msg, std::string const& text) +void printMessageText(std::ostream& msg, std::string const& text) { msg << ":\n"; cmDocumentationFormatter formatter; @@ -103,7 +68,7 @@ static void printMessageText(std::ostream& msg, std::string const& text) formatter.PrintFormatted(msg, text); } -static void displayMessage(MessageType t, std::ostringstream& msg) +void displayMessage(MessageType t, std::ostringstream& msg) { // Add a note about warning suppression. if (t == MessageType::AUTHOR_WARNING) { @@ -115,7 +80,7 @@ static void displayMessage(MessageType t, std::ostringstream& msg) } // Add a terminating blank line. - msg << "\n"; + msg << '\n'; #if !defined(CMAKE_BOOTSTRAP) // Add a C++ stack trace to internal errors. @@ -125,7 +90,7 @@ static void displayMessage(MessageType t, std::ostringstream& msg) if (cmHasLiteralPrefix(stack, "WARNING:")) { stack = "Note:" + stack.substr(8); } - msg << stack << "\n"; + msg << stack << '\n'; } } #endif @@ -144,7 +109,6 @@ static void displayMessage(MessageType t, std::ostringstream& msg) } } -namespace { void PrintCallStack(std::ostream& out, cmListFileBacktrace bt, cm::optional const& topSource) { @@ -174,9 +138,46 @@ void PrintCallStack(std::ostream& out, cmListFileBacktrace bt, if (topSource) { lfc.FilePath = cmSystemTools::RelativeIfUnder(*topSource, lfc.FilePath); } - out << " " << lfc << "\n"; + out << " " << lfc << '\n'; } } + +} // anonymous namespace + +MessageType cmMessenger::ConvertMessageType(MessageType t) const +{ + if (t == MessageType::AUTHOR_WARNING || t == MessageType::AUTHOR_ERROR) { + if (this->GetDevWarningsAsErrors()) { + return MessageType::AUTHOR_ERROR; + } + return MessageType::AUTHOR_WARNING; + } + if (t == MessageType::DEPRECATION_WARNING || + t == MessageType::DEPRECATION_ERROR) { + if (this->GetDeprecatedWarningsAsErrors()) { + return MessageType::DEPRECATION_ERROR; + } + return MessageType::DEPRECATION_WARNING; + } + return t; +} + +bool cmMessenger::IsMessageTypeVisible(MessageType t) const +{ + if (t == MessageType::DEPRECATION_ERROR) { + return this->GetDeprecatedWarningsAsErrors(); + } + if (t == MessageType::DEPRECATION_WARNING) { + return !this->GetSuppressDeprecatedWarnings(); + } + if (t == MessageType::AUTHOR_ERROR) { + return this->GetDevWarningsAsErrors(); + } + if (t == MessageType::AUTHOR_WARNING) { + return !this->GetSuppressDevWarnings(); + } + + return true; } void cmMessenger::IssueMessage(MessageType t, const std::string& text, @@ -199,9 +200,9 @@ void cmMessenger::DisplayMessage(MessageType t, const std::string& text, const cmListFileBacktrace& backtrace) const { std::ostringstream msg; - if (!printMessagePreamble(t, msg)) { - return; - } + + // Print the message preamble. + msg << "CMake " << getMessageTypeStr(t); // Add the immediate context. this->PrintBacktraceTitle(msg, backtrace); @@ -214,7 +215,7 @@ void cmMessenger::DisplayMessage(MessageType t, const std::string& text, displayMessage(t, msg); #ifdef CMake_ENABLE_DEBUGGER - if (DebuggerAdapter != nullptr) { + if (DebuggerAdapter) { DebuggerAdapter->OnMessageOutput(t, msg.str()); } #endif diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 77c3e6abb..891187a89 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -1016,7 +1016,8 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement( vars["LANGUAGE_COMPILE_FLAGS"] = langFlags; auto const tgtNames = this->TargetNames(config); - if (genTarget->HasSOName(config)) { + if (genTarget->HasSOName(config) || + genTarget->IsArchivedAIXSharedLibrary()) { vars["SONAME_FLAG"] = this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config)); vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject, @@ -1163,11 +1164,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmNinjaVars& vars = linkBuild.Variables; if (this->GeneratorTarget->HasLinkDependencyFile(config)) { - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - this->ConvertToNinjaPath( - this->GetLocalGenerator()->GetLinkDependencyFile(this->GeneratorTarget, - config)), - cmOutputConverter::SHELL); + this->AddDepfileBinding(vars, + this->ConvertToNinjaPath( + this->GetLocalGenerator()->GetLinkDependencyFile( + this->GeneratorTarget, config))); } // Compute the comment. @@ -1316,7 +1316,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( t, gt, this->TargetLinkLanguage(config), config); vars["LANGUAGE_COMPILE_FLAGS"] = t; } - if (gt->HasSOName(config)) { + if (gt->HasSOName(config) || gt->IsArchivedAIXSharedLibrary()) { vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config)); vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject, cmOutputConverter::SHELL); @@ -1459,7 +1459,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmNinjaVars symlinkVars; bool const symlinkNeeded = - (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple()); + (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple() && + !gt->IsArchivedAIXSharedLibrary()); if (!symlinkNeeded) { vars["POST_BUILD"] = postBuildCmdLine; } else { diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index a10635a10..720020dcd 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -22,6 +22,7 @@ #include #include +#include "cmBuildDatabase.h" #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" #include "cmDyndepCollation.h" @@ -1232,6 +1233,21 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( language, "Modules.json")); build.ImplicitDeps.emplace_back( this->GetTargetDependInfoPath(language, config)); + { + auto bdb_path = + this->GeneratorTarget->BuildDatabasePath(language, config); + if (!bdb_path.empty()) { + build.ImplicitOuts.emplace_back(this->ConvertToNinjaPath(bdb_path)); + } + } + auto bdb_path = this->GeneratorTarget->BuildDatabasePath(language, config); + if (!bdb_path.empty()) { + auto db = cmBuildDatabase::ForTarget(this->GeneratorTarget, config); + auto mcdb_template_path = cmStrCat(bdb_path, ".in"); + db.Write(mcdb_template_path); + build.ImplicitDeps.emplace_back(std::move(mcdb_template_path)); + build.ImplicitOuts.emplace_back(std::move(bdb_path)); + } for (auto const& scanFiles : scanningFiles) { if (!scanFiles.ScanningOutput.empty()) { build.ExplicitDeps.push_back(scanFiles.ScanningOutput); @@ -1308,7 +1324,7 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, bool compilationPreprocesses, cmNinjaBuild& objBuild, cmNinjaVars& vars, const std::string& objectFileName, - cmLocalGenerator* lg) + cmNinjaTargetGenerator* tg) { cmNinjaBuild scanBuild(ruleName); @@ -1372,13 +1388,12 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName, // Scanning always provides a depfile for preprocessor dependencies. This // variable is unused in `msvc`-deptype scanners. - std::string const& depFileName = cmStrCat(scanBuild.Outputs.front(), ".d"); - scanBuild.Variables["DEP_FILE"] = - lg->ConvertToOutputFormat(depFileName, cmOutputConverter::SHELL); + tg->AddDepfileBinding(scanBuild.Variables, + cmStrCat(scanBuild.Outputs.front(), ".d")); if (compilePP) { // The actual compilation does not need a depfile because it // depends on the already-preprocessed source. - vars.erase("DEP_FILE"); + tg->RemoveDepfileBinding(vars); } return scanBuild; @@ -1455,25 +1470,19 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( if (this->GetMakefile()->GetSafeDefinition( cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { - bool replaceExt(false); + bool replaceExt = false; if (!language.empty()) { std::string repVar = cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); replaceExt = this->Makefile->IsOn(repVar); } - if (!replaceExt) { - // use original code - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(objectFileName, ".d"), cmOutputConverter::SHELL); - } else { - // Replace the original source file extension with the - // depend file extension. - std::string dependFileName = cmStrCat( - cmSystemTools::GetFilenameWithoutLastExtension(objectFileName), ".d"); - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(objectFileDir, '/', dependFileName), - cmOutputConverter::SHELL); - } + this->AddDepfileBinding( + vars, + replaceExt ? cmStrCat(objectFileDir, '/', + cmSystemTools::GetFilenameWithoutLastExtension( + objectFileName), + ".d") + : cmStrCat(objectFileName, ".d")); } this->SetMsvcTargetPdbVariable(vars, config); @@ -1598,8 +1607,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( cmNinjaBuild ppBuild = GetScanBuildStatement( scanRuleName, ppFileName, compilePP, compilePPWithDefines, - compilationPreprocesses, objBuild, vars, objectFileName, - this->LocalGenerator); + compilationPreprocesses, objBuild, vars, objectFileName, this); if (compilePP) { // In case compilation requires flags that are incompatible with @@ -1788,24 +1796,19 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( if (this->GetMakefile()->GetSafeDefinition( cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) { - bool replaceExt(false); + bool replaceExt = false; if (!language.empty()) { std::string repVar = cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE"); replaceExt = this->Makefile->IsOn(repVar); } - if (!replaceExt) { - // use original code - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(bmiFileName, ".d"), cmOutputConverter::SHELL); - } else { - // Replace the original source file extension with the - // depend file extension. - std::string dependFileName = cmStrCat( - cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), ".d"); - vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - cmStrCat(bmiFileDir, '/', dependFileName), cmOutputConverter::SHELL); - } + this->AddDepfileBinding( + vars, + replaceExt + ? cmStrCat(bmiFileDir, '/', + cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), + ".d") + : cmStrCat(bmiFileName, ".d")); } std::string d = @@ -1852,7 +1855,7 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement( cmNinjaBuild ppBuild = GetScanBuildStatement( scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild, - vars, bmiFileName, this->LocalGenerator); + vars, bmiFileName, this); ScanningFiles scanningFiles; @@ -2492,6 +2495,24 @@ void cmNinjaTargetGenerator::MacOSXContentGeneratorType::operator()( this->Generator->Configs[config].ExtraFiles.push_back(std::move(output)); } +void cmNinjaTargetGenerator::AddDepfileBinding(cmNinjaVars& vars, + std::string depfile) const +{ + std::string depfileForShell = + this->GetLocalGenerator()->ConvertToOutputFormat(depfile, + cmOutputConverter::SHELL); + if (depfile != depfileForShell) { + vars["depfile"] = std::move(depfile); + } + vars["DEP_FILE"] = std::move(depfileForShell); +} + +void cmNinjaTargetGenerator::RemoveDepfileBinding(cmNinjaVars& vars) const +{ + vars.erase("DEP_FILE"); + vars.erase("depfile"); +} + void cmNinjaTargetGenerator::addPoolNinjaVariable( const std::string& pool_property, cmGeneratorTarget* target, cmNinjaVars& vars) diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 2bfed80b5..571192271 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -42,6 +42,9 @@ public: std::string GetTargetName() const; + void AddDepfileBinding(cmNinjaVars& vars, std::string depfile) const; + void RemoveDepfileBinding(cmNinjaVars& vars) const; + protected: bool SetMsvcTargetPdbVariable(cmNinjaVars&, const std::string& config) const; diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index 15197ba27..35c8367fe 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -161,10 +161,6 @@ void cmNinjaUtilityTargetGenerator::WriteUtilBuildStatements( cmSystemTools::ReplaceString(command, "$(ARGS)", ""); command = gg->ExpandCFGIntDir(command, config); - if (command.find('$') != std::string::npos) { - return; - } - std::string ccConfig; if (genTarget->Target->IsPerConfig() && genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) { diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx index 75d15018b..aa3155a72 100644 --- a/Source/cmOutputRequiredFilesCommand.cxx +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -306,7 +306,7 @@ protected: // See if the cmSourceFile for it has any files specified as // dependency hints. - if (info->SourceFile != nullptr) { + if (info->SourceFile) { // Get the cmSourceFile corresponding to this. const cmSourceFile& cFile = *(info->SourceFile); diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index f193ed962..40b18ff90 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -15,6 +14,7 @@ #include "cmList.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -75,44 +75,61 @@ struct UserArgumentParser : public cmArgumentParser static void PassParsedArguments( const std::string& prefix, cmMakefile& makefile, const options_map& options, const single_map& singleValArgs, const multi_map& multiValArgs, - const std::vector& unparsed, + const std::vector& unparsed, const options_set& keywordsSeen, const options_set& keywordsMissingValues, bool parseFromArgV) { for (auto const& iter : options) { - makefile.AddDefinition(prefix + iter.first, + makefile.AddDefinition(cmStrCat(prefix, iter.first), iter.second ? "TRUE" : "FALSE"); } + const cmPolicies::PolicyStatus cmp0174 = + makefile.GetPolicyStatus(cmPolicies::CMP0174); for (auto const& iter : singleValArgs) { - if (!iter.second.empty()) { - makefile.AddDefinition(prefix + iter.first, iter.second); + if (keywordsSeen.find(iter.first) == keywordsSeen.end()) { + makefile.RemoveDefinition(cmStrCat(prefix, iter.first)); + } else if ((parseFromArgV && cmp0174 == cmPolicies::NEW) || + !iter.second.empty()) { + makefile.AddDefinition(cmStrCat(prefix, iter.first), iter.second); } else { - makefile.RemoveDefinition(prefix + iter.first); + // The OLD policy behavior doesn't define a variable for an empty or + // missing value, and we can't differentiate between those two cases. + if (parseFromArgV && (cmp0174 == cmPolicies::WARN)) { + makefile.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("The ", iter.first, + " keyword was followed by an empty string or no value at " + "all. Policy CMP0174 is not set, so " + "cmake_parse_arguments() will unset the ", + prefix, iter.first, + " variable rather than setting it to an empty string.")); + } + makefile.RemoveDefinition(cmStrCat(prefix, iter.first)); } } for (auto const& iter : multiValArgs) { if (!iter.second.empty()) { - makefile.AddDefinition(prefix + iter.first, + makefile.AddDefinition(cmStrCat(prefix, iter.first), JoinList(iter.second, parseFromArgV)); } else { - makefile.RemoveDefinition(prefix + iter.first); + makefile.RemoveDefinition(cmStrCat(prefix, iter.first)); } } if (!unparsed.empty()) { - makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS", + makefile.AddDefinition(cmStrCat(prefix, "UNPARSED_ARGUMENTS"), JoinList(unparsed, parseFromArgV)); } else { - makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS"); + makefile.RemoveDefinition(cmStrCat(prefix, "UNPARSED_ARGUMENTS")); } if (!keywordsMissingValues.empty()) { makefile.AddDefinition( - prefix + "KEYWORDS_MISSING_VALUES", + cmStrCat(prefix, "KEYWORDS_MISSING_VALUES"), cmList::to_string(cmMakeRange(keywordsMissingValues))); } else { - makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES"); + makefile.RemoveDefinition(cmStrCat(prefix, "KEYWORDS_MISSING_VALUES")); } } @@ -143,9 +160,10 @@ bool cmParseArgumentsCommand(std::vector const& args, parseFromArgV = true; argIter++; // move past PARSE_ARGV if (!cmStrToULong(*argIter, &argvStart)) { - status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, - "PARSE_ARGV index '" + *argIter + - "' is not an unsigned integer"); + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("PARSE_ARGV index '", *argIter, + "' is not an unsigned integer")); cmSystemTools::SetFatalErrorOccurred(); return true; } @@ -167,7 +185,7 @@ bool cmParseArgumentsCommand(std::vector const& args, auto const duplicateKey = [&status](std::string const& key) { status.GetMakefile().IssueMessage( - MessageType::WARNING, "keyword defined more than once: " + key); + MessageType::WARNING, cmStrCat("keyword defined more than once: ", key)); }; // the second argument is a (cmake) list of options without argument @@ -194,21 +212,20 @@ bool cmParseArgumentsCommand(std::vector const& args, std::string argc = status.GetMakefile().GetSafeDefinition("ARGC"); unsigned long count; if (!cmStrToULong(argc, &count)) { - status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, - "PARSE_ARGV called with ARGC='" + - argc + - "' that is not an unsigned integer"); + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("PARSE_ARGV called with ARGC='", argc, + "' that is not an unsigned integer")); cmSystemTools::SetFatalErrorOccurred(); return true; } for (unsigned long i = argvStart; i < count; ++i) { - std::ostringstream argName; - argName << "ARGV" << i; - cmValue arg = status.GetMakefile().GetDefinition(argName.str()); + const std::string argName{ cmStrCat("ARGV", i) }; + cmValue arg = status.GetMakefile().GetDefinition(argName); if (!arg) { - status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, - "PARSE_ARGV called with " + - argName.str() + " not set"); + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("PARSE_ARGV called with ", argName, " not set")); cmSystemTools::SetFatalErrorOccurred(); return true; } @@ -216,6 +233,14 @@ bool cmParseArgumentsCommand(std::vector const& args, } } + std::vector keywordsSeen; + parser.BindParsedKeywords(keywordsSeen); + + // For single-value keywords, only the last instance matters, since it + // determines the value stored. But if a keyword is repeated, it will be + // added to this vector if _any_ instance is missing a value. If one of the + // earlier instances is missing a value but the last one isn't, its presence + // in this vector will be misleading. std::vector keywordsMissingValues; parser.BindKeywordsMissingValue(keywordsMissingValues); @@ -223,7 +248,7 @@ bool cmParseArgumentsCommand(std::vector const& args, PassParsedArguments( prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, - unparsed, + unparsed, options_set(keywordsSeen.begin(), keywordsSeen.end()), options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()), parseFromArgV); diff --git a/Source/cmPkgConfigParser.cxx b/Source/cmPkgConfigParser.cxx new file mode 100644 index 000000000..992d1ea04 --- /dev/null +++ b/Source/cmPkgConfigParser.cxx @@ -0,0 +1,151 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmPkgConfigParser.h" + +#include +#include + +#include + +#include + +cmPkgConfigValueElement::cmPkgConfigValueElement(bool isVariable, + cm::string_view data) + : IsVariable{ isVariable } + , Data{ data } +{ +} + +cmPkgConfigEntry::cmPkgConfigEntry(bool isVariable, cm::string_view key) + : IsVariable{ isVariable } + , Key{ key } +{ +} + +cmPkgConfigParser::cmPkgConfigParser() +{ + llpkgc_init(static_cast(this), &Settings_); +} + +llpkgc_errno_t cmPkgConfigParser::Parse(char* buf, std::size_t len) +{ + return llpkgc_execute(static_cast(this), buf, len); +} + +llpkgc_errno_t cmPkgConfigParser::Finish() +{ + return llpkgc_finish(static_cast(this)); +} + +llpkgc_errno_t cmPkgConfigParser::Finish(char* buf, std::size_t len) +{ + Parse(buf, len); + return llpkgc_finish(static_cast(this)); +} + +int cmPkgConfigParser::OnSpanNext(const char*, std::size_t len) +{ + Len_ += len; + return 0; +} + +int cmPkgConfigParser::OnSpanNextTr(llpkgc_t* parser, const char* at, + std::size_t len) +{ + return static_cast(parser)->OnSpanNext(at, len); +} + +int cmPkgConfigParser::OnKey(const char* at, std::size_t len) +{ + Ptr_ = at; + Len_ = len; + Settings_.on_key = OnSpanNextTr; + return 0; +} + +int cmPkgConfigParser::OnKeyTr(llpkgc_t* parser, const char* at, + std::size_t len) +{ + return static_cast(parser)->OnKey(at, len); +} + +int cmPkgConfigParser::OnKeywordComplete() +{ + Data_.emplace_back(false, cm::string_view{ Ptr_, Len_ }); + Settings_.on_key = OnKeyTr; + return 0; +} + +int cmPkgConfigParser::OnKeywordCompleteTr(llpkgc_t* parser) +{ + return static_cast(parser)->OnKeywordComplete(); +} + +int cmPkgConfigParser::OnVariableComplete() +{ + Data_.emplace_back(true, cm::string_view{ Ptr_, Len_ }); + Settings_.on_key = OnKeyTr; + return 0; +} + +int cmPkgConfigParser::OnVariableCompleteTr(llpkgc_t* parser) +{ + return static_cast(parser)->OnVariableComplete(); +} + +int cmPkgConfigParser::OnValueLiteral(const char* at, std::size_t len) +{ + Ptr_ = at; + Len_ = len; + Settings_.on_value_literal = OnSpanNextTr; + return 0; +} + +int cmPkgConfigParser::OnValueLiteralTr(llpkgc_t* parser, const char* at, + std::size_t len) +{ + return static_cast(parser)->OnValueLiteral(at, len); +} + +int cmPkgConfigParser::OnValueLiteralComplete() +{ + Settings_.on_value_literal = OnValueLiteralTr; + + if (Len_) { + Data_.back().Val.emplace_back(false, cm::string_view{ Ptr_, Len_ }); + } + + return 0; +} + +int cmPkgConfigParser::OnValueLiteralCompleteTr(llpkgc_t* parser) +{ + return static_cast(parser)->OnValueLiteralComplete(); +} + +int cmPkgConfigParser::OnValueVariable(const char* at, std::size_t len) +{ + Ptr_ = at; + Len_ = len; + Settings_.on_value_variable = OnSpanNextTr; + return 0; +} + +int cmPkgConfigParser::OnValueVariableTr(llpkgc_t* parser, const char* at, + std::size_t len) +{ + return static_cast(parser)->OnValueVariable(at, len); +} + +int cmPkgConfigParser::OnValueVariableComplete() +{ + Settings_.on_value_variable = OnValueVariableTr; + Data_.back().Val.emplace_back(true, cm::string_view{ Ptr_, Len_ }); + return 0; +} + +int cmPkgConfigParser::OnValueVariableCompleteTr(llpkgc_t* parser) +{ + return static_cast(parser)->OnValueVariableComplete(); +} diff --git a/Source/cmPkgConfigParser.h b/Source/cmPkgConfigParser.h new file mode 100644 index 000000000..e671c2760 --- /dev/null +++ b/Source/cmPkgConfigParser.h @@ -0,0 +1,93 @@ +/* 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 +#include + +#include + +#include + +struct cmPkgConfigValueElement +{ + + cmPkgConfigValueElement() = default; + + cmPkgConfigValueElement(bool isVariable, cm::string_view data); + + bool IsVariable; + cm::string_view Data; +}; + +struct cmPkgConfigEntry +{ + + cmPkgConfigEntry() = default; + + cmPkgConfigEntry(bool isVariable, cm::string_view key); + + bool IsVariable; + cm::string_view Key; + std::vector Val; +}; + +class cmPkgConfigParser : llpkgc_t +{ +public: + cmPkgConfigParser(); + + llpkgc_errno_t Parse(char* buf, std::size_t len); + + llpkgc_errno_t Finish(); + llpkgc_errno_t Finish(char* buf, std::size_t len); + + std::vector& Data() { return Data_; } + +private: + int OnSpanNext(const char*, std::size_t len); + static int OnSpanNextTr(llpkgc_t* parser, const char* at, std::size_t len); + + int OnKey(const char* at, std::size_t len); + static int OnKeyTr(llpkgc_t* parser, const char* at, std::size_t len); + + int OnKeywordComplete(); + static int OnKeywordCompleteTr(llpkgc_t* parser); + + int OnVariableComplete(); + static int OnVariableCompleteTr(llpkgc_t* parser); + + int OnValueLiteral(const char* at, std::size_t len); + static int OnValueLiteralTr(llpkgc_t* parser, const char* at, + std::size_t len); + + int OnValueLiteralComplete(); + static int OnValueLiteralCompleteTr(llpkgc_t* parser); + + int OnValueVariable(const char* at, std::size_t len); + static int OnValueVariableTr(llpkgc_t* parser, const char* at, + std::size_t len); + + int OnValueVariableComplete(); + static int OnValueVariableCompleteTr(llpkgc_t* parser); + + llpkgc_settings_t Settings_{ + OnKeyTr, + OnValueLiteralTr, + OnValueVariableTr, + nullptr, // on_line_begin + OnKeywordCompleteTr, + OnVariableCompleteTr, + OnValueLiteralCompleteTr, + OnValueVariableCompleteTr, + nullptr, // on_value_complete + nullptr, // on_pkgc_complete + }; + + const char* Ptr_; + std::size_t Len_; + std::vector Data_; +}; diff --git a/Source/cmPkgConfigResolver.cxx b/Source/cmPkgConfigResolver.cxx new file mode 100644 index 000000000..251dfb74b --- /dev/null +++ b/Source/cmPkgConfigResolver.cxx @@ -0,0 +1,870 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmPkgConfigResolver.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmPkgConfigParser.h" + +namespace { + +void TrimBack(std::string& str) +{ + if (!str.empty()) { + auto it = str.end() - 1; + for (; std::isspace(*it); --it) { + if (it == str.begin()) { + str.clear(); + return; + } + } + str.erase(++it, str.end()); + } +} + +std::string AppendAndTrim(std::string& str, cm::string_view sv) +{ + auto size = str.length(); + str += sv; + if (str.empty()) { + return {}; + } + + auto begin = str.begin() + size; + auto cur = str.end() - 1; + + while (cur != begin && std::isspace(*cur)) { + --cur; + } + + if (std::isspace(*cur)) { + return {}; + } + + return { &*begin, static_cast(cur - begin) + 1 }; +} + +} // namespace + +std::string cmPkgConfigResult::StrOrDefault(const std::string& key, + cm::string_view def) +{ + auto it = Keywords.find(key); + return it == Keywords.end() ? std::string{ def } : it->second; +}; + +std::string cmPkgConfigResult::Name() +{ + return StrOrDefault("Name"); +} + +std::string cmPkgConfigResult::Description() +{ + return StrOrDefault("Description"); +} + +std::string cmPkgConfigResult::Version() +{ + return StrOrDefault("Version"); +} + +std::vector cmPkgConfigResult::Conflicts() +{ + auto it = Keywords.find("Conflicts"); + if (it == Keywords.end()) { + return {}; + } + + return cmPkgConfigResolver::ParseDependencies(it->second); +} + +std::vector cmPkgConfigResult::Provides() +{ + auto it = Keywords.find("Provides"); + if (it == Keywords.end()) { + return {}; + } + + return cmPkgConfigResolver::ParseDependencies(it->second); +} + +std::vector cmPkgConfigResult::Requires(bool priv) +{ + auto it = Keywords.find(priv ? "Requires.private" : "Requires"); + if (it == Keywords.end()) { + return {}; + } + + return cmPkgConfigResolver::ParseDependencies(it->second); +} + +cmPkgConfigCflagsResult cmPkgConfigResult::Cflags(bool priv) +{ + std::string cflags; + auto it = Keywords.find(priv ? "Cflags.private" : "Cflags"); + if (it != Keywords.end()) { + cflags += it->second; + } + + it = Keywords.find(priv ? "CFlags.private" : "CFlags"); + if (it != Keywords.end()) { + if (!cflags.empty()) { + cflags += " "; + } + cflags += it->second; + } + + auto tokens = cmPkgConfigResolver::TokenizeFlags(cflags); + + if (env.AllowSysCflags) { + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleCflags(tokens, *env.SysrootDir); + } + return cmPkgConfigResolver::MangleCflags(tokens); + } + + if (env.SysCflags) { + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleCflags(tokens, *env.SysrootDir, + *env.SysCflags); + } + return cmPkgConfigResolver::MangleCflags(tokens, *env.SysCflags); + } + + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleCflags( + tokens, *env.SysrootDir, std::vector{ "/usr/include" }); + } + + return cmPkgConfigResolver::MangleCflags( + tokens, std::vector{ "/usr/include" }); +} + +cmPkgConfigLibsResult cmPkgConfigResult::Libs(bool priv) +{ + auto it = Keywords.find(priv ? "Libs.private" : "Libs"); + if (it == Keywords.end()) { + return cmPkgConfigLibsResult(); + } + + auto tokens = cmPkgConfigResolver::TokenizeFlags(it->second); + + if (env.AllowSysLibs) { + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleLibs(tokens, *env.SysrootDir); + } + return cmPkgConfigResolver::MangleLibs(tokens); + } + + if (env.SysLibs) { + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleLibs(tokens, *env.SysrootDir, + *env.SysLibs); + } + return cmPkgConfigResolver::MangleLibs(tokens, *env.SysLibs); + } + + if (env.SysrootDir) { + return cmPkgConfigResolver::MangleLibs( + tokens, *env.SysrootDir, std::vector{ "/usr/lib" }); + } + + return cmPkgConfigResolver::MangleLibs( + tokens, std::vector{ "/usr/lib" }); +} + +void cmPkgConfigResolver::ReplaceSep(std::string& list) +{ +#ifndef _WIN32 + std::replace(list.begin(), list.end(), ':', ';'); +#else + static_cast(list); // Unused parameter +#endif +} + +cm::optional cmPkgConfigResolver::ResolveStrict( + const std::vector& entries, cmPkgConfigEnv env) +{ + cm::optional result; + cmPkgConfigResult config; + auto& keys = config.Keywords; + + if (env.SysrootDir) { + config.Variables["pc_sysrootdir"] = *env.SysrootDir; + } else { + config.Variables["pc_sysrootdir"] = "/"; + } + + if (env.TopBuildDir) { + config.Variables["pc_top_builddir"] = *env.TopBuildDir; + } + + config.env = std::move(env); + + for (const auto& entry : entries) { + std::string key(entry.Key); + if (entry.IsVariable) { + if (config.Variables.find(key) != config.Variables.end()) { + return result; + } + auto var = HandleVariableStrict(entry, config.Variables); + if (!var) { + return result; + } + config.Variables[key] = *var; + } else { + if (key == "Cflags" && keys.find("CFlags") != keys.end()) { + return result; + } + if (key == "CFlags" && keys.find("Cflags") != keys.end()) { + return result; + } + if (key == "Cflags.private" && + keys.find("CFlags.private") != keys.end()) { + return result; + } + if (key == "CFlags.private" && + keys.find("Cflags.private") != keys.end()) { + return result; + } + if (keys.find(key) != keys.end()) { + return result; + } + keys[key] = HandleKeyword(entry, config.Variables); + } + } + + if (keys.find("Name") == keys.end() || + keys.find("Description") == keys.end() || + keys.find("Version") == keys.end()) { + return result; + } + + result = std::move(config); + return result; +} + +cm::optional cmPkgConfigResolver::ResolvePermissive( + const std::vector& entries, cmPkgConfigEnv env) +{ + cm::optional result; + + cmPkgConfigResult config = ResolveBestEffort(entries, std::move(env)); + const auto& keys = config.Keywords; + + if (keys.find("Name") == keys.end() || + keys.find("Description") == keys.end() || + keys.find("Version") == keys.end()) { + return result; + } + + result = std::move(config); + return result; +} + +cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort( + const std::vector& entries, cmPkgConfigEnv env) +{ + cmPkgConfigResult result; + + if (env.SysrootDir) { + result.Variables["pc_sysrootdir"] = *env.SysrootDir; + } else { + result.Variables["pc_sysrootdir"] = "/"; + } + + if (env.TopBuildDir) { + result.Variables["pc_top_builddir"] = *env.TopBuildDir; + } + + result.env = std::move(env); + + for (const auto& entry : entries) { + std::string key(entry.Key); + if (entry.IsVariable) { + result.Variables[key] = + HandleVariablePermissive(entry, result.Variables); + } else { + result.Keywords[key] += HandleKeyword(entry, result.Variables); + } + } + return result; +} + +std::string cmPkgConfigResolver::HandleVariablePermissive( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables) +{ + std::string result; + for (const auto& segment : entry.Val) { + if (!segment.IsVariable) { + result += segment.Data; + } else if (entry.Key != segment.Data) { + auto it = variables.find(std::string{ segment.Data }); + if (it != variables.end()) { + result += it->second; + } + } + } + + TrimBack(result); + return result; +} + +cm::optional cmPkgConfigResolver::HandleVariableStrict( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables) +{ + cm::optional result; + + std::string value; + for (const auto& segment : entry.Val) { + if (!segment.IsVariable) { + value += segment.Data; + } else if (entry.Key == segment.Data) { + return result; + } else { + auto it = variables.find(std::string{ segment.Data }); + if (it != variables.end()) { + value += it->second; + } else { + return result; + } + } + } + + TrimBack(value); + result = std::move(value); + return result; +} + +std::string cmPkgConfigResolver::HandleKeyword( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables) +{ + std::string result; + for (const auto& segment : entry.Val) { + if (!segment.IsVariable) { + result += segment.Data; + } else { + auto it = variables.find(std::string{ segment.Data }); + if (it != variables.end()) { + result += it->second; + } + } + } + + TrimBack(result); + return result; +} + +std::vector cmPkgConfigResolver::TokenizeFlags( + const std::string& flagline) +{ + std::vector result; + + auto it = flagline.begin(); + while (it != flagline.end() && std::isspace(*it)) { + ++it; + } + + while (it != flagline.end()) { + const char* start = &(*it); + std::size_t len = 0; + + for (; it != flagline.end() && !std::isspace(*it); ++it) { + ++len; + } + + for (; it != flagline.end() && std::isspace(*it); ++it) { + ++len; + } + + result.emplace_back(start, len); + } + + return result; +} + +cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( + const std::vector& flags) +{ + cmPkgConfigCflagsResult result; + + for (auto flag : flags) { + if (flag.rfind("-I", 0) == 0) { + result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else { + result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( + const std::vector& flags, const std::string& sysroot) +{ + cmPkgConfigCflagsResult result; + + for (auto flag : flags) { + if (flag.rfind("-I", 0) == 0) { + std::string reroot = Reroot(flag, "-I", sysroot); + result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot)); + } else { + result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( + const std::vector& flags, + const std::vector& syspaths) +{ + cmPkgConfigCflagsResult result; + + for (auto flag : flags) { + if (flag.rfind("-I", 0) == 0) { + cm::string_view noprefix{ flag.data() + 2, flag.size() - 2 }; + + if (std::all_of(syspaths.begin(), syspaths.end(), + [&](const std::string& path) { + return noprefix.rfind(path, 0) == noprefix.npos; + })) { + result.Includes.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + + } else { + result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( + const std::vector& flags, const std::string& sysroot, + const std::vector& syspaths) +{ + cmPkgConfigCflagsResult result; + + for (auto flag : flags) { + if (flag.rfind("-I", 0) == 0) { + std::string reroot = Reroot(flag, "-I", sysroot); + cm::string_view noprefix{ reroot.data() + 2, reroot.size() - 2 }; + + if (std::all_of(syspaths.begin(), syspaths.end(), + [&](const std::string& path) { + return noprefix.rfind(path, 0) == noprefix.npos; + })) { + result.Includes.emplace_back(AppendAndTrim(result.Flagline, reroot)); + } + + } else { + result.CompileOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( + const std::vector& flags) +{ + cmPkgConfigLibsResult result; + + for (auto flag : flags) { + if (flag.rfind("-L", 0) == 0) { + result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else if (flag.rfind("-l", 0) == 0) { + result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else { + result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( + const std::vector& flags, const std::string& sysroot) +{ + cmPkgConfigLibsResult result; + + for (auto flag : flags) { + if (flag.rfind("-L", 0) == 0) { + std::string reroot = Reroot(flag, "-L", sysroot); + result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot)); + } else if (flag.rfind("-l", 0) == 0) { + result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else { + result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( + const std::vector& flags, + const std::vector& syspaths) +{ + cmPkgConfigLibsResult result; + + for (auto flag : flags) { + if (flag.rfind("-L", 0) == 0) { + cm::string_view noprefix{ flag.data() + 2, flag.size() - 2 }; + + if (std::all_of(syspaths.begin(), syspaths.end(), + [&](const std::string& path) { + return noprefix.rfind(path, 0) == noprefix.npos; + })) { + result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + + } else if (flag.rfind("-l", 0) == 0) { + result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else { + result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( + const std::vector& flags, const std::string& sysroot, + const std::vector& syspaths) +{ + cmPkgConfigLibsResult result; + + for (auto flag : flags) { + if (flag.rfind("-L", 0) == 0) { + std::string reroot = Reroot(flag, "-L", sysroot); + cm::string_view noprefix{ reroot.data() + 2, reroot.size() - 2 }; + + if (std::all_of(syspaths.begin(), syspaths.end(), + [&](const std::string& path) { + return noprefix.rfind(path, 0) == noprefix.npos; + })) { + result.LibDirs.emplace_back(AppendAndTrim(result.Flagline, reroot)); + } + + } else if (flag.rfind("-l", 0) == 0) { + result.LibNames.emplace_back(AppendAndTrim(result.Flagline, flag)); + } else { + result.LinkOptions.emplace_back(AppendAndTrim(result.Flagline, flag)); + } + } + + return result; +} + +std::string cmPkgConfigResolver::Reroot(cm::string_view flag, + cm::string_view prefix, + const std::string& sysroot) +{ + std::string result = std::string{ prefix }; + result += sysroot; + result += cm::string_view{ flag.data() + prefix.length(), + flag.size() - prefix.length() }; + return result; +} + +cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion( + std::string::const_iterator& cur, std::string::const_iterator end) +{ + cmPkgConfigVersionReq result; + if (*cur == '=') { + result.Operation = result.EQ; + ++cur; + } else if (*cur == '>') { + ++cur; + + if (cur == end) { + result.Operation = result.GT; + return result; + } + + if (*cur == '=') { + result.Operation = result.GT_EQ; + ++cur; + } else { + result.Operation = result.GT; + } + + } else if (*cur == '<') { + ++cur; + + if (cur == end) { + result.Operation = result.LT; + return result; + } + + if (*cur == '=') { + result.Operation = result.LT_EQ; + ++cur; + } else { + result.Operation = result.LT; + } + + } else if (*cur == '!') { + ++cur; + + if (cur == end) { + result.Operation = result.ANY; + return result; + } + + if (*cur == '=') { + result.Operation = result.NEQ; + ++cur; + } else { + result.Operation = result.ANY; + } + } + + for (;; ++cur) { + if (cur == end) { + return result; + } + + if (!std::isspace(*cur)) { + break; + } + } + + for (; cur != end && !std::isspace(*cur) && *cur != ','; ++cur) { + result.Version += *cur; + } + + return result; +} + +std::vector cmPkgConfigResolver::ParseDependencies( + const std::string& deps) +{ + + std::vector result; + + auto cur = deps.begin(); + auto end = deps.end(); + + while (cur != end) { + while ((std::isspace(*cur) || *cur == ',')) { + if (++cur == end) { + return result; + } + } + + result.emplace_back(); + auto& dep = result.back(); + + while (!std::isspace(*cur) && *cur != ',') { + dep.Name += *cur; + if (++cur == end) { + return result; + } + } + + auto in_operator = [&]() -> bool { + for (;; ++cur) { + if (cur == end) { + return false; + } + + if (*cur == '>' || *cur == '=' || *cur == '<' || *cur == '!') { + return true; + } + + if (!std::isspace(*cur)) { + return false; + } + } + }; + + if (!in_operator()) { + continue; + } + + dep.VerReq = ParseVersion(cur, end); + } + + return result; +} + +bool cmPkgConfigResolver::CheckVersion(const cmPkgConfigVersionReq& desired, + const std::string& provided) +{ + + if (desired.Operation == cmPkgConfigVersionReq::ANY) { + return true; + } + + // https://blog.jasonantman.com/2014/07/how-yum-and-rpm-compare-versions/ + + auto check_with_op = [&](int comp) -> bool { + switch (desired.Operation) { + case cmPkgConfigVersionReq::EQ: + return comp == 0; + case cmPkgConfigVersionReq::NEQ: + return comp != 0; + case cmPkgConfigVersionReq::GT: + return comp < 0; + case cmPkgConfigVersionReq::GT_EQ: + return comp <= 0; + case cmPkgConfigVersionReq::LT: + return comp > 0; + case cmPkgConfigVersionReq::LT_EQ: + return comp >= 0; + default: + return true; + } + }; + + if (desired.Version == provided) { + return check_with_op(0); + } + + auto a_cur = desired.Version.begin(); + auto a_end = desired.Version.end(); + + auto b_cur = provided.begin(); + auto b_end = provided.end(); + + while (a_cur != a_end && b_cur != b_end) { + while (a_cur != a_end && !std::isalnum(*a_cur) && *a_cur != '~') { + ++a_cur; + } + + while (b_cur != b_end && !std::isalnum(*b_cur) && *b_cur != '~') { + ++b_cur; + } + + if (a_cur == a_end || b_cur == b_end) { + break; + } + + if (*a_cur == '~' || *b_cur == '~') { + if (*a_cur != '~') { + return check_with_op(1); + } + + if (*b_cur != '~') { + return check_with_op(-1); + } + + ++a_cur; + ++b_cur; + continue; + } + + auto a_seg = a_cur; + auto b_seg = b_cur; + bool is_num; + + if (std::isdigit(*a_cur)) { + is_num = true; + while (a_cur != a_end && std::isdigit(*a_cur)) { + ++a_cur; + } + + while (b_cur != b_end && std::isdigit(*b_cur)) { + ++b_cur; + } + + } else { + is_num = false; + while (a_cur != a_end && std::isalpha(*a_cur)) { + ++a_cur; + } + + while (b_cur != b_end && std::isalpha(*b_cur)) { + ++b_cur; + } + } + + auto a_len = std::distance(a_seg, a_cur); + auto b_len = std::distance(b_seg, b_cur); + + if (!b_len) { + return check_with_op(is_num ? 1 : -1); + } + + if (is_num) { + while (a_seg != a_cur && *a_seg == '0') { + ++a_seg; + } + + while (b_seg != b_cur && *b_seg == '0') { + ++b_seg; + } + + a_len = std::distance(a_seg, a_cur); + b_len = std::distance(b_seg, b_cur); + + if (a_len != b_len) { + return check_with_op(a_len > b_len ? 1 : -1); + } + + auto cmp = std::memcmp(&*a_seg, &*b_seg, a_len); + if (cmp) { + return check_with_op(cmp); + } + } else { + auto cmp = std::memcmp(&*a_seg, &*b_seg, std::min(a_len, b_len)); + if (cmp) { + return check_with_op(cmp); + } + + if (a_len != b_len) { + return check_with_op(a_len > b_len ? 1 : -1); + } + } + } + + if (a_cur == a_end) { + if (b_cur == b_end) { + return check_with_op(0); + } + return check_with_op(-1); + } + + return check_with_op(1); +} + +cmPkgConfigVersionReq cmPkgConfigResolver::ParseVersion( + const std::string& version) +{ + cmPkgConfigVersionReq result; + + auto cur = version.begin(); + auto end = version.end(); + + if (cur == end) { + result.Operation = cmPkgConfigVersionReq::EQ; + return result; + } + + result = ParseVersion(cur, end); + cur = version.begin(); + + if (*cur != '=' && *cur != '!' && *cur != '<' && *cur != '>') { + result.Operation = cmPkgConfigVersionReq::EQ; + } + + return result; +} diff --git a/Source/cmPkgConfigResolver.h b/Source/cmPkgConfigResolver.h new file mode 100644 index 000000000..a230ae79b --- /dev/null +++ b/Source/cmPkgConfigResolver.h @@ -0,0 +1,172 @@ +/* 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 +#include +#include + +#include +#include + +// From cmPkgConfigParser.h, IWYU doesn't like including the header +struct cmPkgConfigEntry; + +struct cmPkgConfigCflagsResult +{ + std::string Flagline; + std::vector Includes; + std::vector CompileOptions; +}; + +struct cmPkgConfigLibsResult +{ + std::string Flagline; + std::vector LibDirs; + std::vector LibNames; + std::vector LinkOptions; +}; + +struct cmPkgConfigVersionReq +{ + enum + { + ANY = 0, + LT, + LT_EQ, + EQ, + NEQ, + GT_EQ, + GT, + } Operation = ANY; + std::string Version; +}; + +struct cmPkgConfigDependency +{ + std::string Name; + cmPkgConfigVersionReq VerReq; +}; + +struct cmPkgConfigEnv +{ + cm::optional> Path; + cm::optional> LibDirs; + cm::optional> SysCflags; + cm::optional> SysLibs; + + cm::optional SysrootDir; + cm::optional TopBuildDir; + + cm::optional DisableUninstalled; + + bool AllowSysCflags = true; + bool AllowSysLibs = true; +}; + +class cmPkgConfigResult +{ +public: + std::unordered_map Keywords; + std::unordered_map Variables; + + std::string Name(); + std::string Description(); + std::string Version(); + + std::vector Conflicts(); + std::vector Provides(); + std::vector Requires(bool priv = false); + + cmPkgConfigCflagsResult Cflags(bool priv = false); + cmPkgConfigLibsResult Libs(bool priv = false); + + cmPkgConfigEnv env; + +private: + std::string StrOrDefault(const std::string& key, cm::string_view def = ""); +}; + +class cmPkgConfigResolver +{ + friend class cmPkgConfigResult; + +public: + static cm::optional ResolveStrict( + const std::vector& entries, cmPkgConfigEnv env); + + static cm::optional ResolvePermissive( + const std::vector& entries, cmPkgConfigEnv env); + + static cmPkgConfigResult ResolveBestEffort( + const std::vector& entries, cmPkgConfigEnv env); + + static cmPkgConfigVersionReq ParseVersion(const std::string& version); + + static bool CheckVersion(const cmPkgConfigVersionReq& desired, + const std::string& provided); + + static void ReplaceSep(std::string& list); + +#ifdef _WIN32 + static const char Sep = ';'; +#else + static const char Sep = ':'; +#endif + +private: + static std::string HandleVariablePermissive( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables); + + static cm::optional HandleVariableStrict( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables); + + static std::string HandleKeyword( + const cmPkgConfigEntry& entry, + const std::unordered_map& variables); + + static std::vector TokenizeFlags( + const std::string& flagline); + + static cmPkgConfigCflagsResult MangleCflags( + const std::vector& flags); + + static cmPkgConfigCflagsResult MangleCflags( + const std::vector& flags, const std::string& sysroot); + + static cmPkgConfigCflagsResult MangleCflags( + const std::vector& flags, + const std::vector& syspaths); + + static cmPkgConfigCflagsResult MangleCflags( + const std::vector& flags, const std::string& sysroot, + const std::vector& syspaths); + + static cmPkgConfigLibsResult MangleLibs( + const std::vector& flags); + + static cmPkgConfigLibsResult MangleLibs( + const std::vector& flags, const std::string& sysroot); + + static cmPkgConfigLibsResult MangleLibs( + const std::vector& flags, + const std::vector& syspaths); + + static cmPkgConfigLibsResult MangleLibs( + const std::vector& flags, const std::string& sysroot, + const std::vector& syspaths); + + static std::string Reroot(cm::string_view flag, cm::string_view prefix, + const std::string& sysroot); + + static cmPkgConfigVersionReq ParseVersion(std::string::const_iterator& cur, + std::string::const_iterator end); + + static std::vector ParseDependencies( + const std::string& deps); +}; diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index d89c8c8cc..16aefff64 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -261,7 +261,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, { // Warn about policy versions for which support will be removed. if (warnCompat == WarnCompat::On && - (majorVer < 3 || (majorVer == 3 && minorVer < 5)) && + (majorVer < 3 || (majorVer == 3 && minorVer < 10)) && // Avoid warning on calls generated by install(EXPORT) // in CMake versions prior to 3.18. !(majorVer == 2 && minorVer == 6 && patchVer == 0 && @@ -270,7 +270,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, "cmake_policy") == 0)) { mf->IssueMessage( MessageType::DEPRECATION_WARNING, - "Compatibility with CMake < 3.5 will be removed from " + "Compatibility with CMake < 3.10 will be removed from " "a future version of CMake.\n" "Update the VERSION argument value or use a ... suffix " "to tell CMake that the project does not need compatibility with " diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index d893c44db..dbd6ce8bb 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -525,7 +525,34 @@ class cmMakefile; 3, 30, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0170, \ "FETCHCONTENT_FULLY_DISCONNECTED requirements are enforced.", 3, 30, \ - 0, cmPolicies::WARN) + 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0171, "'codegen' is a reserved target name.", 3, 31, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0172, \ + "The CPack module enables per-machine installation by default in " \ + "the CPack WIX Generator.", \ + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0173, "The CMakeFindFrameworks module is removed.", 3, \ + 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0174, \ + "cmake_parse_arguments(PARSE_ARGV) defines a variable for an empty " \ + "string after a single-value keyword.", \ + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0175, "add_custom_command() rejects invalid arguments.", \ + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0176, "execute_process() ENCODING is UTF-8 by default.", \ + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0177, "install() DESTINATION paths are normalized.", 3, \ + 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0178, "Test command lines preserve empty arguments.", 3, \ + 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0179, \ + "De-duplication of static libraries on link lines keeps first " \ + "occurrence.", \ + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0180, \ + "project() always sets _* as normal variables.", 3, \ + 31, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -569,7 +596,8 @@ class cmMakefile; F(CMP0156) \ F(CMP0157) \ F(CMP0160) \ - F(CMP0162) + F(CMP0162) \ + F(CMP0179) #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \ F(CMP0116) \ diff --git a/Source/cmProcessOutput.cxx b/Source/cmProcessOutput.cxx index e1df66116..601d70183 100644 --- a/Source/cmProcessOutput.cxx +++ b/Source/cmProcessOutput.cxx @@ -12,16 +12,18 @@ unsigned int cmProcessOutput::defaultCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE; #endif -cmProcessOutput::Encoding cmProcessOutput::FindEncoding( +cm::optional cmProcessOutput::FindEncoding( std::string const& name) { - Encoding encoding = Auto; + cm::optional encoding; if ((name == "UTF8") || (name == "UTF-8")) { encoding = UTF8; } else if (name == "NONE") { encoding = None; } else if (name == "ANSI") { encoding = ANSI; + } else if (name == "AUTO") { + encoding = Auto; } else if (name == "OEM") { encoding = OEM; } diff --git a/Source/cmProcessOutput.h b/Source/cmProcessOutput.h index 8cee987a2..f15e3f9e4 100644 --- a/Source/cmProcessOutput.h +++ b/Source/cmProcessOutput.h @@ -8,6 +8,8 @@ #include #include +#include + /** \class cmProcessOutput * \brief Decode text data to internal encoding. * @@ -31,7 +33,7 @@ public: * \param name a encoding name. * \return encoding enum value or Auto if \a name was not found. */ - static Encoding FindEncoding(std::string const& name); + static cm::optional FindEncoding(std::string const& name); /// The code page that is used as internal encoding to which we will encode. static unsigned int defaultCodepage; diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 90c104238..5d854cd45 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -58,11 +58,13 @@ bool cmProjectCommand(std::vector const& args, mf.SetProjectName(projectName); + cmPolicies::PolicyStatus cmp0180 = mf.GetPolicyStatus(cmPolicies::CMP0180); + std::string varName = cmStrCat(projectName, "_BINARY_DIR"_s); bool nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); mf.AddCacheDefinition(varName, mf.GetCurrentBinaryDirectory(), "Value Computed by CMake", cmStateEnums::STATIC); - if (nonCacheVarAlreadySet) { + if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) { mf.AddDefinition(varName, mf.GetCurrentBinaryDirectory()); } @@ -70,7 +72,7 @@ bool cmProjectCommand(std::vector const& args, nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); mf.AddCacheDefinition(varName, mf.GetCurrentSourceDirectory(), "Value Computed by CMake", cmStateEnums::STATIC); - if (nonCacheVarAlreadySet) { + if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) { mf.AddDefinition(varName, mf.GetCurrentSourceDirectory()); } @@ -85,7 +87,7 @@ bool cmProjectCommand(std::vector const& args, nonCacheVarAlreadySet = mf.IsNormalDefinitionSet(varName); mf.AddCacheDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF", "Value Computed by CMake", cmStateEnums::STATIC); - if (nonCacheVarAlreadySet) { + if (cmp0180 == cmPolicies::NEW || nonCacheVarAlreadySet) { mf.AddDefinition(varName, mf.IsRootMakefile() ? "ON" : "OFF"); } diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 591c53e66..2f377aec1 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -166,7 +166,7 @@ void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget( std::string const& comment) { // Test if the target already exists - if (localGen->FindGeneratorTargetToUse(name) == nullptr) { + if (!localGen->FindGeneratorTargetToUse(name)) { cmMakefile const* makefile = localGen->GetMakefile(); // Create utility target @@ -196,7 +196,7 @@ void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen( if (it != this->GlobalAutoGenTargets_.end()) { cmGeneratorTarget const* target = localGen->FindGeneratorTargetToUse(it->second); - if (target != nullptr) { + if (target) { target->Target->AddUtility(targetName, false, localGen->GetMakefile()); } } @@ -209,7 +209,7 @@ void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc( if (it != this->GlobalAutoRccTargets_.end()) { cmGeneratorTarget const* target = localGen->FindGeneratorTargetToUse(it->second); - if (target != nullptr) { + if (target) { target->Target->AddUtility(targetName, false, localGen->GetMakefile()); } } diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index ee53a8ec7..1fd406c50 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -357,7 +357,7 @@ void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies( cmQtAutoGenInitializer::GenVarsT const& genVars, std::vector& dependencies) const { - if (genVars.ExecutableTarget != nullptr) { + if (genVars.ExecutableTarget) { dependencies.push_back(genVars.ExecutableTarget->Target->GetName()); } else if (this->MultiConfig && this->UseBetterGraph) { cm::string_view const& configGenexWithCommandConfig = @@ -1340,9 +1340,12 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() } if (this->Uic.Enabled) { - for (const auto& file : this->Uic.UiHeaders) { + // Make all ui_*.h files byproducts of the ${target}_autogen/timestamp + // custom command if the generation of depfile is enabled. + auto& byProducts = useDepfile ? timestampByproducts : autogenByproducts; + for (auto const& file : this->Uic.UiHeaders) { this->AddGeneratedSource(file.first, this->Uic); - autogenByproducts.push_back(file.second); + byProducts.push_back(file.second); } } @@ -1480,6 +1483,16 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() } } + // For the Ninja, Makefile and Qt >= 5.15, add custom commands that create + // XXX_autogen/timestamp files. Those custom commands have a depfile + // assigned that is generated from the depfiles that were created by moc. + // + // The XXX_autogen targets merely wrap the XXX_autogen/timestamp custom + // commands. + // The dependency tree would then look like + // the original dependencies of '_autogen' target <-'/timestamp' file + // <- '_autogen' target + cmTarget* timestampTarget = nullptr; std::vector dependencies( this->AutogenTarget.DependFiles.begin(), @@ -1487,40 +1500,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() if (useDepfile) { // Create a custom command that generates a timestamp file and // has a depfile assigned. The depfile is created by JobDepFilesMergeT. - // - // Also create an additional '_autogen_timestamp_deps' that the custom - // command will depend on. It will have no sources or commands to - // execute, but it will have dependencies that would originally be - // assigned to the pre-Qt 5.15 'autogen' target. These dependencies will - // serve as a list of order-only dependencies for the custom command, - // without forcing the custom command to re-execute. - // - // The dependency tree would then look like - // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <- - // '_autogen' target. - const auto timestampTargetName = - cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps"); - - auto cc = cm::make_unique(); - cc->SetWorkingDirectory(this->Dir.Work.c_str()); - cc->SetDepends(dependencies); - cc->SetEscapeOldStyle(false); - timestampTarget = this->LocalGen->AddUtilityCommand(timestampTargetName, - true, std::move(cc)); - - this->LocalGen->AddGeneratorTarget( - cm::make_unique(timestampTarget, this->LocalGen)); - - // Set FOLDER property on the timestamp target, so it appears in the - // appropriate folder in an IDE or in the file api. - if (!this->TargetsFolder.empty()) { - timestampTarget->SetProperty("FOLDER", this->TargetsFolder); - } - - // Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the - // moc and uic executables (whichever are enabled). - dependencies.clear(); - dependencies.push_back(timestampTargetName); AddAutogenExecutableToDependencies(this->Moc, dependencies); AddAutogenExecutableToDependencies(this->Uic, dependencies); @@ -1565,7 +1544,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); this->AddGeneratedSource(outputFile, this->Moc); } - cc = cm::make_unique(); + auto cc = cm::make_unique(); cc->SetOutputs(outputFile); cc->SetByproducts(timestampByproducts); cc->SetDepends(dependencies); @@ -1634,13 +1613,17 @@ void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines( std::string const& infoFile, std::string const& processName, cmCustomCommandLines& commandLines) { + std::vector autogenConfigs; + this->GlobalGen->GetQtAutoGenConfigs(autogenConfigs); if (this->CrossConfig && this->UseBetterGraph) { commandLines.push_back(cmMakeCommandLine( { cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile, "$", "$>" })); } else if ((this->MultiConfig && this->GlobalGen->IsXcode()) || this->CrossConfig) { - for (std::string const& config : this->ConfigsList) { + const auto& configs = + processName == "cmake_autorcc" ? this->ConfigsList : autogenConfigs; + for (std::string const& config : configs) { commandLines.push_back( cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile, config })); @@ -1650,9 +1633,7 @@ void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines( if (this->MultiConfig) { autoInfoFileConfig = "$"; } else { - std::vector configs; - this->GlobalGen->GetQtAutoGenConfigs(configs); - autoInfoFileConfig = configs[0]; + autoInfoFileConfig = autogenConfigs[0]; } commandLines.push_back( cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName, @@ -1671,13 +1652,10 @@ bool cmQtAutoGenInitializer::InitRccTargets() sf->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "On"); } - std::vector ccOutput; - ccOutput.push_back(qrc.OutputFile); + std::vector ccOutput{ qrc.OutputFile }; - std::vector ccDepends; // Add the .qrc and info file to the custom command dependencies - ccDepends.push_back(qrc.QrcFile); - ccDepends.push_back(qrc.InfoFile); + std::vector ccDepends{ qrc.QrcFile, qrc.InfoFile }; cmCustomCommandLines commandLines; AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines); diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index ebdec1280..376ac2a74 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -130,7 +130,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, { content.clear(); if (!cmSystemTools::FileExists(filename, true)) { - if (error != nullptr) { + if (error) { *error = "Not a file."; } return false; @@ -142,7 +142,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, // Use lambda to save destructor calls of ifs return [&ifs, length, &content, error]() -> bool { if (!ifs) { - if (error != nullptr) { + if (error) { *error = "Opening the file for reading failed."; } return false; @@ -152,7 +152,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, content.assign(IsIt{ ifs }, IsIt{}); if (!ifs) { content.clear(); - if (error != nullptr) { + if (error) { *error = "Reading from the file failed."; } return false; @@ -167,7 +167,7 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, { // Make sure the parent directory exists if (!cmQtAutoGenerator::MakeParentDirectory(filename)) { - if (error != nullptr) { + if (error) { *error = "Could not create parent directory."; } return false; @@ -179,14 +179,14 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, // Use lambda to save destructor calls of ofs return [&ofs, &content, error]() -> bool { if (!ofs) { - if (error != nullptr) { + if (error) { *error = "Opening file for writing failed."; } return false; } ofs << content; if (!ofs.good()) { - if (error != nullptr) { + if (error) { *error = "File writing failed."; } return false; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 408a22cb5..251ef3a22 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -345,6 +345,8 @@ public: void MaybeWriteResponseFile(std::string const& outputFile, std::vector& cmd) const; + static void MaybePrependCmdExe(std::vector& cmd); + /** @brief Run an external process. Use only during Process() call! */ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector const& command, @@ -848,6 +850,54 @@ void cmQtAutoMocUicT::JobT::MaybeWriteResponseFile( #endif } +/* + * According to the CreateProcessW documentation which is the underlying + * function for all RunProcess calls: + * + * "To run a batch file, you must start the command interpreter; set" + * "lpApplicationName to cmd.exe and set lpCommandLine to the following" + * "arguments: /c plus the name of the batch file." + * + * we should to take care of the correctness of the command line when + * attempting to execute the batch files. + * + * Also cmd.exe is unable to parse batch file names correctly if they + * contain spaces. This function uses cmSystemTools::GetShortPath conversion + * to suppress this behavior. + * + * The function is noop on platforms different from the pure WIN32 one. + */ +void cmQtAutoMocUicT::JobT::MaybePrependCmdExe( + std::vector& cmdLine) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!cmdLine.empty()) { + const auto& applicationName = cmdLine.at(0); + if (cmSystemTools::StringEndsWith(applicationName, ".bat") || + cmSystemTools::StringEndsWith(applicationName, ".cmd")) { + std::vector output; + output.reserve(cmdLine.size() + 2); + output.emplace_back(cmSystemTools::GetComspec()); + output.emplace_back("/c"); + std::string tmpShortPath; + if (applicationName.find(' ') != std::string::npos && + cmSystemTools::GetShortPath(applicationName, tmpShortPath)) { + // If the batch file name contains spaces convert it to the windows + // short path. Otherwise it might cause issue when running cmd.exe. + output.emplace_back(tmpShortPath); + } else { + output.push_back(applicationName); + } + std::move(cmdLine.begin() + 1, cmdLine.end(), + std::back_inserter(output)); + cmdLine = std::move(output); + } + } +#else + static_cast(cmdLine); +#endif +} + bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, std::vector const& command, @@ -856,7 +906,7 @@ bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType, // Log command if (this->Log().Verbose()) { cm::string_view info; - if (infoMessage != nullptr) { + if (infoMessage) { info = *infoMessage; } this->Log().Info( @@ -891,6 +941,9 @@ void cmQtAutoMocUicT::JobMocPredefsT::Process() cm::append(cmd, this->MocConst().OptionsIncludes); // Check if response file is necessary MaybeWriteResponseFile(this->MocConst().PredefsFileAbs, cmd); + + MaybePrependCmdExe(cmd); + // Execute command if (!this->RunProcess(GenT::MOC, result, cmd, reason.get())) { this->LogCommandError(GenT::MOC, @@ -938,7 +991,7 @@ bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const { // Test if the file exists if (!this->MocEval().PredefsTime.Load(this->MocConst().PredefsFileAbs)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(this->MocConst().PredefsFileAbs), ", because it doesn't exist."); @@ -948,7 +1001,7 @@ bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const // Test if the settings changed if (this->MocConst().SettingsChanged) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(this->MocConst().PredefsFileAbs), ", because the moc settings changed."); @@ -962,7 +1015,7 @@ bool cmQtAutoMocUicT::JobMocPredefsT::Update(std::string* reason) const cmFileTime execTime; if (execTime.Load(exec)) { if (this->MocEval().PredefsTime.Older(execTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat( "Generating ", this->MessagePath(this->MocConst().PredefsFileAbs), " because it is older than ", this->MessagePath(exec), '.'); @@ -1800,7 +1853,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if the output file exists cmFileTime outputFileTime; if (!outputFileTime.Load(outputFile)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it doesn't exist, from ", this->MessagePath(sourceFile)); @@ -1810,7 +1863,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if any setting changed if (this->MocConst().SettingsChanged) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because the moc settings changed, from ", this->MessagePath(sourceFile)); @@ -1820,7 +1873,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if the source file is newer if (outputFileTime.Older(mapping.SourceFile->FileTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it's older than its source file, from ", this->MessagePath(sourceFile)); @@ -1831,7 +1884,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if the moc_predefs file is newer if (!this->MocConst().PredefsFileAbs.empty()) { if (outputFileTime.Older(this->MocEval().PredefsTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it's older than ", this->MessagePath(this->MocConst().PredefsFileAbs), @@ -1843,7 +1896,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if the moc executable is newer if (outputFileTime.Older(this->MocConst().ExecutableTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it's older than the moc executable, from ", this->MessagePath(sourceFile)); @@ -1862,7 +1915,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Find dependency file auto const depMatch = this->FindDependency(sourceDir, dep); if (depMatch.first.empty()) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), " from ", this->MessagePath(sourceFile), ", because its dependency ", @@ -1875,7 +1928,7 @@ bool cmQtAutoMocUicT::JobProbeDepsMocT::Probe(MappingT const& mapping, // Test if dependency file is older if (outputFileTime.Older(depMatch.second)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it's older than its dependency file ", this->MessagePath(depMatch.first), ", from ", @@ -1950,7 +2003,7 @@ bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping, // Test if the build file exists cmFileTime outputFileTime; if (!outputFileTime.Load(outputFile)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it doesn't exist, from ", this->MessagePath(sourceFile)); @@ -1960,7 +2013,7 @@ bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping, // Test if the uic settings changed if (this->UicConst().SettingsChanged) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because the uic settings changed, from ", this->MessagePath(sourceFile)); @@ -1970,7 +2023,7 @@ bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping, // Test if the source file is newer if (outputFileTime.Older(mapping.SourceFile->FileTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), " because it's older than the source file ", this->MessagePath(sourceFile)); @@ -1980,7 +2033,7 @@ bool cmQtAutoMocUicT::JobProbeDepsUicT::Probe(MappingT const& mapping, // Test if the uic executable is newer if (outputFileTime.Older(this->UicConst().ExecutableTime)) { - if (reason != nullptr) { + if (reason) { *reason = cmStrCat("Generating ", this->MessagePath(outputFile), ", because it's older than the uic executable, from ", this->MessagePath(sourceFile)); @@ -2090,6 +2143,7 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() cmd.push_back(sourceFile); MaybeWriteResponseFile(outputFile, cmd); + MaybePrependCmdExe(cmd); } // Execute moc command @@ -2156,6 +2210,8 @@ void cmQtAutoMocUicT::JobCompileUicT::Process() cmd.emplace_back(outputFile); cmd.emplace_back(sourceFile); + MaybePrependCmdExe(cmd); + cmWorkerPool::ProcessResultT result; if (this->RunProcess(GenT::UIC, result, cmd, this->Reason.get())) { // Uic command success diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx index 2b992ef28..ec70c0556 100644 --- a/Source/cmSearchPath.cxx +++ b/Source/cmSearchPath.cxx @@ -46,7 +46,7 @@ void cmSearchPath::AddPath(const std::string& path) void cmSearchPath::AddUserPath(const std::string& path) { - assert(this->FC != nullptr); + assert(this->FC); std::vector outPaths; @@ -68,7 +68,7 @@ void cmSearchPath::AddUserPath(const std::string& path) void cmSearchPath::AddCMakePath(const std::string& variable) { - assert(this->FC != nullptr); + assert(this->FC); // Get a path from a CMake variable. if (cmValue value = this->FC->Makefile->GetDefinition(variable)) { @@ -92,7 +92,7 @@ void cmSearchPath::AddEnvPath(const std::string& variable) void cmSearchPath::AddCMakePrefixPath(const std::string& variable) { - assert(this->FC != nullptr); + assert(this->FC); // Get a path from a CMake variable. if (cmValue value = this->FC->Makefile->GetDefinition(variable)) { @@ -154,7 +154,7 @@ void cmSearchPath::AddSuffixes(const std::vector& suffixes) void cmSearchPath::AddPrefixPaths(const std::vector& paths, const char* base) { - assert(this->FC != nullptr); + assert(this->FC); // default for programs std::string subdir = "bin"; @@ -222,7 +222,7 @@ void cmSearchPath::AddPrefixPaths(const std::vector& paths, void cmSearchPath::AddPathInternal(const std::string& path, const std::string& prefix, const char* base) { - assert(this->FC != nullptr); + assert(this->FC); std::string collapsedPath = cmSystemTools::CollapseFullPath(path, base); diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index 30ee36947..5443c4749 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -236,7 +236,7 @@ bool cmSourceFile::FindFullPath(std::string* error, "specifier or another FILE_SET within the target_sources() " "command."; } - if (error != nullptr) { + if (error) { *error = std::move(err); } else { makefile->IssueMessage(MessageType::FATAL_ERROR, err); diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index be2023f52..5cacbbc76 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -153,9 +153,6 @@ public: std::string GetObjectLibrary() const; private: - template - void StoreProperty(const std::string& prop, ValueType value); - cmSourceFileLocation Location; cmPropertyMap Properties; std::unique_ptr CustomCommand; diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h index 55cc71689..6c4711f13 100644 --- a/Source/cmStateDirectory.h +++ b/Source/cmStateDirectory.h @@ -76,10 +76,6 @@ public: void AddImportedTargetName(std::string const& name); private: - template - void StoreProperty(const std::string& prop, ValueType value, - cmListFileBacktrace const& lfbt); - cmLinkedTree::iterator DirectoryState; cmStateSnapshot Snapshot_; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 8217a9c91..6ee0225bf 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -298,9 +298,11 @@ void cmStateSnapshot::SetDefaultDefinitions() if (hostSystemName == "Windows") { this->SetDefinition("WIN32", "1"); this->SetDefinition("CMAKE_HOST_WIN32", "1"); + this->SetDefinition("CMAKE_HOST_EXECUTABLE_SUFFIX", ".exe"); } else { this->SetDefinition("UNIX", "1"); this->SetDefinition("CMAKE_HOST_UNIX", "1"); + this->SetDefinition("CMAKE_HOST_EXECUTABLE_SUFFIX", ""); } #if defined(__APPLE__) this->SetDefinition("APPLE", "1"); diff --git a/Source/cmString.cxx b/Source/cmString.cxx index f7f62932c..d7c0ec38e 100644 --- a/Source/cmString.cxx +++ b/Source/cmString.cxx @@ -67,7 +67,7 @@ std::string const& String::str() const char* String::c_str() { const char* c = this->data(); - if (c == nullptr) { + if (!c) { return c; } diff --git a/Source/cmString.hxx b/Source/cmString.hxx index 1994c2ce1..5e1b52444 100644 --- a/Source/cmString.hxx +++ b/Source/cmString.hxx @@ -518,7 +518,7 @@ public: *this = std::move(s); } - void swap(String& other) + void swap(String& other) noexcept { std::swap(this->string_, other.string_); std::swap(this->view_, other.view_); diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx index e352d8dd2..332bd8d5a 100644 --- a/Source/cmStringAlgorithms.cxx +++ b/Source/cmStringAlgorithms.cxx @@ -7,7 +7,6 @@ #include // IWYU pragma: keep #include #include -#include std::string cmTrimWhitespace(cm::string_view str) { @@ -239,51 +238,14 @@ bool cmStrToULongLong(std::string const& str, unsigned long long* value) return cmStrToULongLong(str.c_str(), value); } -template -std::size_t getJoinedLength(Range const& rng, cm::string_view separator) -{ - std::size_t rangeLength{}; - for (auto const& item : rng) { - rangeLength += item.size(); - } - - auto const separatorsLength = (rng.size() - 1) * separator.size(); - - return rangeLength + separatorsLength; -} - -template -std::string cmJoinImpl(Range const& rng, cm::string_view separator, - cm::string_view initial) -{ - if (rng.empty()) { - return { std::begin(initial), std::end(initial) }; - } - - std::string result; - result.reserve(initial.size() + getJoinedLength(rng, separator)); - result.append(std::begin(initial), std::end(initial)); - - auto begin = std::begin(rng); - auto end = std::end(rng); - result += *begin; - - for (++begin; begin != end; ++begin) { - result.append(std::begin(separator), std::end(separator)); - result += *begin; - } - - return result; -} - std::string cmJoin(std::vector const& rng, cm::string_view separator, cm::string_view initial) { - return cmJoinImpl(rng, separator, initial); + return cmJoinStrings(rng, separator, initial); } std::string cmJoin(cmStringRange const& rng, cm::string_view separator, cm::string_view initial) { - return cmJoinImpl(rng, separator, initial); + return cmJoinStrings(rng, separator, initial); } diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h index 55a1e4612..2bd615a65 100644 --- a/Source/cmStringAlgorithms.h +++ b/Source/cmStringAlgorithms.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -77,6 +79,38 @@ std::string cmJoin(Range const& rng, cm::string_view separator) return os.str(); } +/** Generic function to join strings range with separator + * and initial leading string into a single string. + */ +template +std::string cmJoinStrings(Range const& rng, cm::string_view separator, + cm::string_view initial) +{ + if (rng.empty()) { + return { std::begin(initial), std::end(initial) }; + } + + std::string result; + result.reserve( + std::accumulate(std::begin(rng), std::end(rng), + initial.size() + (rng.size() - 1) * separator.size(), + [](std::size_t sum, const std::string& item) { + return sum + item.size(); + })); + result.append(std::begin(initial), std::end(initial)); + + auto begin = std::begin(rng); + auto end = std::end(rng); + result += *begin; + + for (++begin; begin != end; ++begin) { + result.append(std::begin(separator), std::end(separator)); + result += *begin; + } + + return result; +} + /** * Faster overloads for std::string ranges. * If @a initial is provided, it prepends the resulted string without @@ -112,7 +146,7 @@ public: { } cmAlphaNum(const char* str) - : View_(str) + : View_(str ? cm::string_view(str) : cm::string_view()) { } cmAlphaNum(char ch) diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx index 998c135e8..768effd8a 100644 --- a/Source/cmStringReplaceHelper.cxx +++ b/Source/cmStringReplaceHelper.cxx @@ -27,7 +27,7 @@ bool cmStringReplaceHelper::Replace(const std::string& input, // Scan through the input for all matches. std::string::size_type base = 0; while (this->RegularExpression.find(input.c_str() + base)) { - if (this->Makefile != nullptr) { + if (this->Makefile) { this->Makefile->ClearMatches(); this->Makefile->StoreMatches(this->RegularExpression); } diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 093a18b82..5ad0439c9 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -36,6 +36,7 @@ #include "cmUVProcessChain.h" #include "cmUVStream.h" #include "cmValue.h" +#include "cmWorkingDirectory.h" #if !defined(CMAKE_BOOTSTRAP) # include @@ -86,6 +87,9 @@ #if defined(_WIN32) # include + +# include +# include // include wincrypt.h after windows.h # include #else @@ -967,6 +971,17 @@ cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion() result.dwBuildNumber = osviex.dwBuildNumber; return result; } + +std::string cmSystemTools::GetComspec() +{ + std::string comspec; + if (!cmSystemTools::GetEnv("COMSPEC", comspec) || + !cmSystemTools::FileIsFullPath(comspec)) { + comspec = "cmd.exe"; + } + return comspec; +} + #endif std::string cmSystemTools::GetRealPathResolvingWindowsSubst( @@ -1316,16 +1331,18 @@ cmSystemTools::RenameResult cmSystemTools::RenameFile( #endif } -void cmSystemTools::MoveFileIfDifferent(const std::string& source, - const std::string& destination) +cmsys::Status cmSystemTools::MoveFileIfDifferent( + const std::string& source, const std::string& destination) { + cmsys::Status res = {}; if (FilesDiffer(source, destination)) { if (RenameFile(source, destination)) { - return; + return res; } - CopyFileAlways(source, destination); + res = CopyFileAlways(source, destination); } RemoveFile(source); + return res; } void cmSystemTools::Glob(const std::string& directory, @@ -1851,12 +1868,18 @@ bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path) bool cmSystemTools::CreateTar(const std::string& outFileName, const std::vector& files, + const std::string& workingDirectory, cmTarCompression compressType, bool verbose, std::string const& mtime, std::string const& format, int compressionLevel) { #if !defined(CMAKE_BOOTSTRAP) - std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); + cmWorkingDirectory workdir(cmSystemTools::GetCurrentWorkingDirectory()); + if (!workingDirectory.empty()) { + workdir.SetDirectory(workingDirectory); + } + + const std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); cmsys::ofstream fout(outFileName.c_str(), std::ios::out | std::ios::binary); if (!fout) { std::string e = cmStrCat("Cannot open output file \"", outFileName, @@ -1942,7 +1965,7 @@ void list_item_verbose(FILE* out, struct archive_entry* entry) /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); - if ((p == nullptr) || (*p == '\0')) { + if (!p || (*p == '\0')) { snprintf(tmp, sizeof(tmp), "%lu ", static_cast(archive_entry_uid(entry))); p = tmp; @@ -1954,7 +1977,7 @@ void list_item_verbose(FILE* out, struct archive_entry* entry) fprintf(out, "%-*s ", static_cast(u_width), p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); - if (p != nullptr && p[0] != '\0') { + if (p && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { @@ -2099,7 +2122,7 @@ bool extract_tar(const std::string& outFileName, struct archive_entry* entry; struct archive* matching = archive_match_new(); - if (matching == nullptr) { + if (!matching) { cmSystemTools::Error("Out of memory"); return false; } @@ -2135,15 +2158,14 @@ bool extract_tar(const std::string& outFileName, if (verbose) { if (extract) { - cmSystemTools::Stdout("x "); - cmSystemTools::Stdout(cm_archive_entry_pathname(entry)); + cmSystemTools::Stdout( + cmStrCat("x ", cm_archive_entry_pathname(entry))); } else { list_item_verbose(stdout, entry); } cmSystemTools::Stdout("\n"); } else if (!extract) { - cmSystemTools::Stdout(cm_archive_entry_pathname(entry)); - cmSystemTools::Stdout("\n"); + cmSystemTools::Stdout(cmStrCat(cm_archive_entry_pathname(entry), '\n')); } if (extract) { if (extractTimestamps == cmSystemTools::cmTarExtractTimestamps::Yes) { @@ -2182,7 +2204,7 @@ bool extract_tar(const std::string& outFileName, } bool error_occured = false; - if (matching != nullptr) { + if (matching) { const char* p; int ar; @@ -2671,6 +2693,50 @@ std::string const& cmSystemTools::GetHTMLDoc() return cmSystemToolsHTMLDoc; } +cm::optional cmSystemTools::GetSystemConfigDirectory() +{ +#if defined(_WIN32) + LPWSTR lpwstr; + if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &lpwstr))) { + return cm::nullopt; + } + std::wstring wstr = std::wstring(lpwstr); + CoTaskMemFree(lpwstr); + std::string config = cmsys::Encoding::ToNarrow(wstr); + cmSystemTools::ConvertToUnixSlashes(config); + return config; +#else + auto config = cmSystemTools::GetEnvVar("XDG_CONFIG_HOME"); + if (!config.has_value()) { + config = cmSystemTools::GetEnvVar("HOME"); + if (config.has_value()) { +# if defined(__APPLE__) + config = cmStrCat(config.value(), "/Library/Application Support"); +# else + config = cmStrCat(config.value(), "/.config"); +# endif + } + } + return config; +#endif +} + +cm::optional cmSystemTools::GetCMakeConfigDirectory() +{ + auto config = cmSystemTools::GetEnvVar("CMAKE_CONFIG_DIR"); + if (!config.has_value()) { + config = cmSystemTools::GetSystemConfigDirectory(); + if (config.has_value()) { +#if defined(_WIN32) || defined(__APPLE__) + config = cmStrCat(config.value(), "/CMake"); +#else + config = cmStrCat(config.value(), "/cmake"); +#endif + } + } + return config; +} + std::string cmSystemTools::GetCurrentWorkingDirectory() { return cmSystemTools::CollapseFullPath( @@ -2817,6 +2883,10 @@ cm::optional AdjustRPathELF(std::string const& file, return cm::nullopt; // Not a valid ELF file. } + if (!elf.HasDynamicSection()) { + return true; // No dynamic section to update. + } + // Get the RPATH and RUNPATH entries from it. int se_count = 0; cmELF::StringEntry const* se[2] = { nullptr, nullptr }; @@ -2973,10 +3043,10 @@ static cm::optional ChangeRPathELF(std::string const& file, std::ostringstream e; /* clang-format off */ e << "The current " << se_name << " is:\n" - << " " << inRPath << "\n" - << "which does not contain:\n" - << " " << oldRPath << "\n" - << "as was expected."; + " " << inRPath << "\n" + "which does not contain:\n" + " " << oldRPath << "\n" + "as was expected."; /* clang-format on */ *emsg2 = e.str(); } @@ -3060,10 +3130,10 @@ static cm::optional ChangeRPathXCOFF(std::string const& file, std::ostringstream e; /* clang-format off */ e << "The current RPATH is:\n" - << " " << libPath << "\n" - << "which does not contain:\n" - << " " << oldRPath << "\n" - << "as was expected."; + " " << libPath << "\n" + "which does not contain:\n" + " " << oldRPath << "\n" + "as was expected."; /* clang-format on */ *emsg = e.str(); } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index d12ab070d..0531f63b8 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -211,8 +211,8 @@ public: std::string* err = nullptr); //! Rename a file if contents are different, delete the source otherwise - static void MoveFileIfDifferent(const std::string& source, - const std::string& destination); + static cmsys::Status MoveFileIfDifferent(const std::string& source, + const std::string& destination); /** * Run a single executable command @@ -497,6 +497,7 @@ public: const std::vector& files, bool verbose); static bool CreateTar(const std::string& outFileName, const std::vector& files, + const std::string& workingDirectory, cmTarCompression compressType, bool verbose, std::string const& mtime = std::string(), std::string const& format = std::string(), @@ -524,6 +525,10 @@ public: static std::string const& GetCMakeRoot(); static std::string const& GetHTMLDoc(); + /** Get the CMake config directory **/ + static cm::optional GetSystemConfigDirectory(); + static cm::optional GetCMakeConfigDirectory(); + /** Get the CWD mapped through the KWSys translation map. */ static std::string GetCurrentWorkingDirectory(); @@ -581,6 +586,9 @@ public: unsigned int dwBuildNumber; }; static WindowsVersion GetWindowsVersion(); + + /** Attempt to get full path to COMSPEC, default "cmd.exe" */ + static std::string GetComspec(); #endif /** Get the real path for a given path, removing all symlinks. diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1284130e4..f22083740 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -407,6 +407,8 @@ TargetProperty const StaticTargetProperties[] = { { "VS_USE_DEBUG_LIBRARIES"_s, IC::NonImportedTarget }, // ---- OpenWatcom { "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources }, + // ---- AIX + { "AIX_SHARED_LIBRARY_ARCHIVE"_s, IC::SharedLibraryTarget }, // -- Language // ---- C COMMON_LANGUAGE_PROPERTIES(C), @@ -464,6 +466,7 @@ TargetProperty const StaticTargetProperties[] = { { "LINKER_TYPE"_s, IC::CanCompileSources }, { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports }, { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget }, + { "LINK_LIBRARIES_STRATEGY"_s, IC::NormalNonImportedTarget }, { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources }, { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources }, // Initialize per-configuration name postfix property from the variable only @@ -596,6 +599,7 @@ TargetProperty const StaticTargetProperties[] = { // Metadata { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget }, + { "EXPORT_BUILD_DATABASE"_s, IC::CanCompileSources }, { "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources }, { "FOLDER"_s }, { "TEST_LAUNCHER"_s, IC::ExecutableTarget }, @@ -643,6 +647,7 @@ public: cmStateEnums::TargetType TargetType; cmMakefile* Makefile; cmPolicies::PolicyMap PolicyMap; + cmTarget const* TemplateTarget; std::string Name; std::string InstallPath; std::string RuntimeInstallPath; @@ -657,6 +662,7 @@ public: bool PerConfig; cmTarget::Visibility TargetVisibility; std::set>> Utilities; + std::set CodegenDependencies; std::vector PreBuildCommands; std::vector PreLinkCommands; std::vector PostBuildCommands; @@ -928,6 +934,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->impl->TargetType = type; this->impl->Makefile = mf; this->impl->Name = name; + this->impl->TemplateTarget = nullptr; this->impl->IsGeneratorProvided = false; this->impl->HaveInstallRule = false; this->impl->IsDLLPlatform = false; @@ -1183,6 +1190,14 @@ const std::string& cmTarget::GetName() const return this->impl->Name; } +const std::string& cmTarget::GetTemplateName() const +{ + if (this->impl->TemplateTarget) { + return this->impl->TemplateTarget->GetTemplateName(); + } + return this->impl->Name; +} + cmPolicies::PolicyStatus cmTarget::GetPolicyStatus( cmPolicies::PolicyID policy) const { @@ -1238,6 +1253,16 @@ void cmTarget::AddUtility(BT> util) this->impl->Utilities.emplace(std::move(util)); } +void cmTarget::AddCodegenDependency(std::string const& name) +{ + this->impl->CodegenDependencies.emplace(name); +} + +std::set const& cmTarget::GetCodegenDeps() const +{ + return this->impl->CodegenDependencies; +} + std::set>> const& cmTarget::GetUtilities() const { @@ -1273,6 +1298,12 @@ bool cmTarget::IsFrameworkOnApple() const this->IsApple() && this->GetPropertyAsBool("FRAMEWORK")); } +bool cmTarget::IsArchivedAIXSharedLibrary() const +{ + return (this->GetType() == cmStateEnums::SHARED_LIBRARY && this->IsAIX() && + this->GetPropertyAsBool("AIX_SHARED_LIBRARY_ARCHIVE")); +} + bool cmTarget::IsAppBundleOnApple() const { return (this->GetType() == cmStateEnums::EXECUTABLE && this->IsApple() && @@ -1765,6 +1796,7 @@ void cmTarget::CopyPolicyStatuses(cmTarget const* tgt) assert(tgt->IsImported()); this->impl->PolicyMap = tgt->impl->PolicyMap; + this->impl->TemplateTarget = tgt; } void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt) @@ -1865,6 +1897,10 @@ void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt) // Metadata "EchoString", "EXPORT_COMPILE_COMMANDS", + // Do *not* copy this property; it should be re-initialized at synthesis + // time from the `CMAKE_EXPORT_BUILD_DATABASE` variable as `IMPORTED` + // targets ignore the property initialization. + // "EXPORT_BUILD_DATABASE", "FOLDER", "LABELS", "PROJECT_LABEL", @@ -2986,7 +3022,9 @@ const char* cmTarget::GetSuffixVariableInternal( case cmStateEnums::SHARED_LIBRARY: switch (artifact) { case cmStateEnums::RuntimeBinaryArtifact: - return "CMAKE_SHARED_LIBRARY_SUFFIX"; + return this->IsArchivedAIXSharedLibrary() + ? "CMAKE_SHARED_LIBRARY_ARCHIVE_SUFFIX" + : "CMAKE_SHARED_LIBRARY_SUFFIX"; case cmStateEnums::ImportLibraryArtifact: return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_SUFFIX" : "CMAKE_IMPORT_LIBRARY_SUFFIX"; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 385dfe7ac..d0e9e3dd6 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -78,6 +78,7 @@ public: //! Set/Get the name of the target const std::string& GetName() const; + const std::string& GetTemplateName() const; //! Get the policy map cmPolicies::PolicyMap const& GetPolicyMap() const; @@ -173,6 +174,11 @@ public: void AddUtility(std::string const& name, bool cross, cmMakefile const* mf = nullptr); void AddUtility(BT> util); + + void AddCodegenDependency(std::string const& name); + + std::set const& GetCodegenDeps() const; + //! Get the utilities used by this target std::set>> const& GetUtilities() const; @@ -229,6 +235,9 @@ public: //! Return whether this target is a shared library Framework on Apple. bool IsFrameworkOnApple() const; + //! Return whether to archive shared library or not on AIX. + bool IsArchivedAIXSharedLibrary() const; + //! Return whether this target is an executable Bundle on Apple. bool IsAppBundleOnApple() const; @@ -328,9 +337,6 @@ public: bool HasFileSets() const; private: - template - void StoreProperty(const std::string& prop, ValueType value); - // Internal representation details. friend class cmGeneratorTarget; diff --git a/Source/cmTargetTraceDependencies.cxx b/Source/cmTargetTraceDependencies.cxx index cc91a423f..f14cfbf90 100644 --- a/Source/cmTargetTraceDependencies.cxx +++ b/Source/cmTargetTraceDependencies.cxx @@ -7,10 +7,12 @@ #include +#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmList.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmSourceFile.h" @@ -132,6 +134,8 @@ void cmTargetTraceDependencies::FollowName(std::string const& name) // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or // POST_BUILD command. this->GeneratorTarget->Target->AddUtility(t->GetName(), false); + + this->GeneratorTarget->Target->AddCodegenDependency(t->GetName()); } if (cmSourceFile* sf = i->second.Source) { // For now only follow the dependency if the source file is not a @@ -213,6 +217,11 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) // Collect target-level dependencies referenced in command lines. for (auto const& util : ccg.GetUtilities()) { this->GeneratorTarget->Target->AddUtility(util); + + if (ccg.GetCC().GetCodegen()) { + this->GeneratorTarget->Target->AddCodegenDependency( + util.Value.first); + } } // Collect file-level dependencies referenced in DEPENDS. @@ -226,6 +235,8 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) // The dependency does not name a target and may be a file we // know how to generate. Queue it. this->FollowName(dep); + } else { + this->GeneratorTarget->Target->AddCodegenDependency(dep); } } } diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index 8a3914467..c72e0207e 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx @@ -10,6 +10,7 @@ cmTest::cmTest(cmMakefile* mf) : Backtrace(mf->GetBacktrace()) , PolicyStatusCMP0158(mf->GetPolicyStatus(cmPolicies::CMP0158)) + , PolicyStatusCMP0178(mf->GetPolicyStatus(cmPolicies::CMP0178)) { this->Makefile = mf; this->OldStyle = true; diff --git a/Source/cmTest.h b/Source/cmTest.h index 480966a81..244aa6267 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -61,12 +61,22 @@ public: bool GetOldStyle() const { return this->OldStyle; } void SetOldStyle(bool b) { this->OldStyle = b; } - /** Get/Set if CMP0158 policy is NEW */ + /** Get if CMP0158 policy is NEW */ bool GetCMP0158IsNew() const { return this->PolicyStatusCMP0158 == cmPolicies::NEW; } + /** Get/Set the CMP0178 policy setting */ + cmPolicies::PolicyStatus GetCMP0178() const + { + return this->PolicyStatusCMP0178; + } + void SetCMP0178(cmPolicies::PolicyStatus p) + { + this->PolicyStatusCMP0178 = p; + } + /** Set/Get whether lists in command lines should be expanded. */ bool GetCommandExpandLists() const; void SetCommandExpandLists(bool b); @@ -82,4 +92,5 @@ private: cmMakefile* Makefile; cmListFileBacktrace Backtrace; cmPolicies::PolicyStatus PolicyStatusCMP0158; + cmPolicies::PolicyStatus PolicyStatusCMP0178; }; diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 840d8cf7e..ffb5e2163 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -174,15 +174,36 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, if (!cmNonempty(launcher)) { return; } - cmList launcherWithArgs{ ge.Parse(*launcher)->Evaluate(this->LG, - config) }; + const auto propVal = ge.Parse(*launcher)->Evaluate(this->LG, config); + cmList launcherWithArgs(propVal, cmList::ExpandElements::Yes, + this->Test->GetCMP0178() == cmPolicies::NEW + ? cmList::EmptyElements::Yes + : cmList::EmptyElements::No); if (!launcherWithArgs.empty() && !launcherWithArgs[0].empty()) { + if (this->Test->GetCMP0178() == cmPolicies::WARN) { + cmList argsWithEmptyValuesPreserved( + propVal, cmList::ExpandElements::Yes, cmList::EmptyElements::Yes); + if (launcherWithArgs != argsWithEmptyValuesPreserved) { + this->Test->GetMakefile()->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat("The ", propertyName, " property of target '", + target->GetName(), + "' contains empty list items. Those empty items are " + "being silently discarded to preserve backward " + "compatibility.\n", + cmPolicies::GetPolicyWarning(cmPolicies::CMP0178))); + } + } std::string launcherExe(launcherWithArgs[0]); cmSystemTools::ConvertToUnixSlashes(launcherExe); os << cmOutputConverter::EscapeForCMake(launcherExe) << " "; for (std::string const& arg : cmMakeRange(launcherWithArgs).advance(1)) { - os << cmOutputConverter::EscapeForCMake(arg) << " "; + if (arg.empty()) { + os << "\"\" "; + } else { + os << cmOutputConverter::EscapeForCMake(arg) << " "; + } } } }; diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index 7e47b4e51..4d8bc027d 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx @@ -115,7 +115,7 @@ std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT, ptr = localtime(&timeT); } - if (ptr == nullptr) { + if (!ptr) { return std::string(); } diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx index ccf24a0dc..92661346c 100644 --- a/Source/cmVSSetupHelper.cxx +++ b/Source/cmVSSetupHelper.cxx @@ -178,7 +178,7 @@ bool cmVSSetupAPIHelper::CheckInstalledComponent( bool cmVSSetupAPIHelper::GetVSInstanceInfo( SmartCOMPtr pInstance, VSInstanceInfo& vsInstanceInfo) { - if (pInstance == nullptr) { + if (!pInstance) { return false; } @@ -219,8 +219,7 @@ bool cmVSSetupAPIHelper::GetVSInstanceInfo( } LPSAFEARRAY lpsaPackages; - if (FAILED(pInstance->GetPackages(&lpsaPackages)) || - lpsaPackages == nullptr) { + if (FAILED(pInstance->GetPackages(&lpsaPackages)) || !lpsaPackages) { return false; } @@ -232,7 +231,7 @@ bool cmVSSetupAPIHelper::GetVSInstanceInfo( SmartCOMPtr package = nullptr; if (FAILED(ppData[i]->QueryInterface(IID_ISetupPackageReference, (void**)&package)) || - package == nullptr) { + !package) { continue; } @@ -372,8 +371,7 @@ bool cmVSSetupAPIHelper::EnumerateVSInstancesWithVswhere( bool cmVSSetupAPIHelper::EnumerateVSInstancesWithCOM( std::vector& VSInstances) { - if (initializationFailure || setupConfig == nullptr || - setupConfig2 == nullptr || setupHelper == nullptr) { + if (initializationFailure || !setupConfig || !setupConfig2 || !setupHelper) { return false; } @@ -452,7 +450,8 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance() // Enumerate VS instances with either COM interface or Vswhere if (!EnumerateVSInstancesWithCOM(vecVSInstancesAll) && - !EnumerateVSInstancesWithVswhere(vecVSInstancesAll)) { + !EnumerateVSInstancesWithVswhere(vecVSInstancesAll) && + this->SpecifiedVSInstallLocation.empty()) { return false; } @@ -610,21 +609,21 @@ bool cmVSSetupAPIHelper::Initialize() if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, nullptr, IID_ISetupConfiguration, CLSCTX_INPROC_SERVER)) || - setupConfig == nullptr) { + !setupConfig) { initializationFailure = true; return false; } if (FAILED(setupConfig.QueryInterface(IID_ISetupConfiguration2, (void**)&setupConfig2)) || - setupConfig2 == nullptr) { + !setupConfig2) { initializationFailure = true; return false; } if (FAILED( setupConfig.QueryInterface(IID_ISetupHelper, (void**)&setupHelper)) || - setupHelper == nullptr) { + !setupHelper) { initializationFailure = true; return false; } diff --git a/Source/cmVSSetupHelper.h b/Source/cmVSSetupHelper.h index b8be9b9bf..cc4d69687 100644 --- a/Source/cmVSSetupHelper.h +++ b/Source/cmVSSetupHelper.h @@ -21,14 +21,14 @@ public: SmartCOMPtr(T* p) { ptr = p; - if (ptr != nullptr) { + if (ptr) { ptr->AddRef(); } } SmartCOMPtr(const SmartCOMPtr& sptr) { ptr = sptr.ptr; - if (ptr != nullptr) { + if (ptr) { ptr->AddRef(); } } @@ -38,7 +38,7 @@ public: { if (*this != p) { ptr = p; - if (ptr != nullptr) { + if (ptr) { ptr->AddRef(); } } @@ -48,7 +48,7 @@ public: template HRESULT QueryInterface(REFCLSID rclsid, I** pp) { - if (pp != nullptr) { + if (pp) { return ptr->QueryInterface(rclsid, (void**)pp); } return E_FAIL; @@ -62,7 +62,7 @@ public: } ~SmartCOMPtr() { - if (ptr != nullptr) { + if (ptr) { ptr->Release(); } } diff --git a/Source/cmValue.cxx b/Source/cmValue.cxx index 044db2909..df806cf26 100644 --- a/Source/cmValue.cxx +++ b/Source/cmValue.cxx @@ -90,10 +90,10 @@ bool cmValue::IsInternallyOn(cm::string_view value) noexcept int cmValue::Compare(cmValue value) const noexcept { - if (this->Value == nullptr && !value) { + if (!this->Value && !value) { return 0; } - if (this->Value == nullptr) { + if (!this->Value) { return -1; } if (!value) { @@ -104,13 +104,13 @@ int cmValue::Compare(cmValue value) const noexcept int cmValue::Compare(cm::string_view value) const noexcept { - if (this->Value == nullptr && value.data() == nullptr) { + if (!this->Value && !value.data()) { return 0; } - if (this->Value == nullptr) { + if (!this->Value) { return -1; } - if (value.data() == nullptr) { + if (!value.data()) { return 1; } return cm::string_view(*this->Value).compare(value); diff --git a/Source/cmValue.h b/Source/cmValue.h index c924dda56..6b9b795e5 100644 --- a/Source/cmValue.h +++ b/Source/cmValue.h @@ -34,16 +34,16 @@ public: const std::string* Get() const noexcept { return this->Value; } const char* GetCStr() const noexcept { - return this->Value == nullptr ? nullptr : this->Value->c_str(); + return this->Value ? this->Value->c_str() : nullptr; } const std::string* operator->() const noexcept { - return this->Value == nullptr ? &cmValue::Empty : this->Value; + return this->Value ? this->Value : &cmValue::Empty; } const std::string& operator*() const noexcept { - return this->Value == nullptr ? cmValue::Empty : *this->Value; + return this->Value ? *this->Value : cmValue::Empty; } explicit operator bool() const noexcept { return this->Value != nullptr; } @@ -58,8 +58,7 @@ public: */ bool IsOn() const noexcept { - return this->Value != nullptr && - cmValue::IsOn(cm::string_view(*this->Value)); + return this->Value && cmValue::IsOn(cm::string_view(*this->Value)); } /** * Does the value indicate a false or off value ? Note that this is @@ -70,18 +69,16 @@ public: */ bool IsOff() const noexcept { - return this->Value == nullptr || - cmValue::IsOff(cm::string_view(*this->Value)); + return !this->Value || cmValue::IsOff(cm::string_view(*this->Value)); } /** Return true if value is NOTFOUND or ends in -NOTFOUND. */ bool IsNOTFOUND() const noexcept { - return this->Value != nullptr && - cmValue::IsNOTFOUND(cm::string_view(*this->Value)); + return this->Value && cmValue::IsNOTFOUND(cm::string_view(*this->Value)); } bool IsEmpty() const noexcept { - return this->Value == nullptr || this->Value->empty(); + return !this->Value || this->Value->empty(); } /** @@ -91,7 +88,7 @@ public: */ bool IsInternallyOn() const noexcept { - return this->Value != nullptr && + return this->Value && cmValue::IsInternallyOn(cm::string_view(*this->Value)); } @@ -105,7 +102,7 @@ public: */ static bool IsOn(const char* value) noexcept { - return value != nullptr && IsOn(cm::string_view(value)); + return value && IsOn(cm::string_view(value)); } static bool IsOn(cm::string_view) noexcept; @@ -124,20 +121,20 @@ public: */ static bool IsOff(const char* value) noexcept { - return value == nullptr || IsOff(cm::string_view(value)); + return !value || IsOff(cm::string_view(value)); } static bool IsOff(cm::string_view) noexcept; /** Return true if value is NOTFOUND or ends in -NOTFOUND. */ static bool IsNOTFOUND(const char* value) noexcept { - return value == nullptr || IsNOTFOUND(cm::string_view(value)); + return !value || IsNOTFOUND(cm::string_view(value)); } static bool IsNOTFOUND(cm::string_view) noexcept; static bool IsEmpty(const char* value) noexcept { - return value == nullptr || *value == '\0'; + return !value || *value == '\0'; } static bool IsEmpty(cm::string_view value) noexcept { return value.empty(); } @@ -148,7 +145,7 @@ public: */ static bool IsInternallyOn(const char* value) noexcept { - return value != nullptr && IsInternallyOn(cm::string_view(value)); + return value && IsInternallyOn(cm::string_view(value)); } static bool IsInternallyOn(cm::string_view) noexcept; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index a3d524436..694976e58 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -477,11 +477,6 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( Elem e0(BuildFileStream, "Project"); e0.Attribute("DefaultTargets", "Build"); const char* toolsVersion = this->GlobalGenerator->GetToolsVersion(); - if (this->GlobalGenerator->GetVersion() == - cmGlobalVisualStudioGenerator::VSVersion::VS12 && - this->GlobalGenerator->TargetsWindowsCE()) { - toolsVersion = "4.0"; - } e0.Attribute("ToolsVersion", toolsVersion); e0.Attribute("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); @@ -644,11 +639,8 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( // Disable the project upgrade prompt that is displayed the first time a // project using an older toolset version is opened in a newer version of - // the IDE (respected by VS 2013 and above). - if (this->GlobalGenerator->GetVersion() >= - cmGlobalVisualStudioGenerator::VSVersion::VS12) { - e1.Element("VCProjectUpgraderObjectName", "NoUpgrade"); - } + // the IDE. + e1.Element("VCProjectUpgraderObjectName", "NoUpgrade"); if (const char* vcTargetsPath = this->GlobalGenerator->GetCustomVCTargetsPath()) { @@ -813,6 +805,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( this->WriteCustomCommands(e0); this->WriteAllSources(e0); this->WriteDotNetReferences(e0); + this->WriteFrameworkReferences(e0); this->WritePackageReferences(e0); this->WriteImports(e0); this->WriteEmbeddedResourceGroup(e0); @@ -914,16 +907,6 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( return; } - if (this->HasCustomCommands()) { - std::string message = cmStrCat( - "The target \"", this->GeneratorTarget->GetName(), - "\" does not currently support add_custom_command as the Visual Studio " - "generators have not yet learned how to generate custom commands in " - ".Net SDK-style projects."); - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, message); - return; - } - Elem e0(BuildFileStream, "Project"); e0.Attribute("Sdk", *this->GeneratorTarget->GetProperty("DOTNET_SDK")); @@ -931,6 +914,8 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( Elem e1(e0, "PropertyGroup"); this->WriteCommonPropertyGroupGlobals(e1); + e1.Element("Configurations", cmJoinStrings(this->Configurations, ";", "")); + e1.Element("EnableDefaultItems", "false"); // Disable the project upgrade prompt that is displayed the first time a // project using an older toolset version is opened in a newer version @@ -1005,19 +990,25 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( e1.Attribute("Condition", cmStrCat("'$(Configuration)' == '", config, '\'')); e1.SetHasElements(); - this->WriteEvents(e1, config); std::string outDir = cmStrCat(this->GeneratorTarget->GetDirectory(config), '/'); ConvertToWindowsSlash(outDir); e1.Element("OutputPath", outDir); + e1.Element("AssemblyName", GetAssemblyName(config)); + Options& o = *(this->ClOptions[config]); OptionsHelper oh(o, e1); oh.OutputFlagMap(); } + for (const std::string& config : this->Configurations) { + this->WriteSdkStyleEvents(e0, config); + } + this->WriteDotNetDocumentationFile(e0); + this->WriteCustomCommands(e0); this->WriteAllSources(e0); this->WriteEmbeddedResourceGroup(e0); this->WriteXamlFilesGroup(e0); @@ -1079,21 +1070,6 @@ void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1) } } -bool cmVisualStudio10TargetGenerator::HasCustomCommands() const -{ - if (!this->GeneratorTarget->GetPreBuildCommands().empty() || - !this->GeneratorTarget->GetPreLinkCommands().empty() || - !this->GeneratorTarget->GetPostBuildCommands().empty()) { - return true; - } - - auto const& config_sources = this->GeneratorTarget->GetAllConfigSources(); - return std::any_of(config_sources.begin(), config_sources.end(), - [](cmGeneratorTarget::AllConfigSource const& si) { - return si.Source->GetCustomCommand(); - }); -} - void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0) { std::vector packageReferences = @@ -1187,6 +1163,21 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReference( this->WriteDotNetReferenceCustomTags(e2, ref); } +void cmVisualStudio10TargetGenerator::WriteFrameworkReferences(Elem& e0) +{ + cmList references; + if (cmValue vsFrameworkReferences = + this->GeneratorTarget->GetProperty("VS_FRAMEWORK_REFERENCES")) { + references.assign(*vsFrameworkReferences); + } + + Elem e1(e0, "ItemGroup"); + for (auto const& ref : references) { + Elem e2(e1, "FrameworkReference"); + e2.Attribute("Include", ref); + } +} + void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0) { cmValue imports = @@ -1604,13 +1595,7 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValuesManaged( this->WriteMSToolConfigurationValuesCommon(e1, config); - std::string postfixName = - cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX"); - std::string assemblyName = this->GeneratorTarget->GetOutputName( - config, cmStateEnums::RuntimeBinaryArtifact); - if (cmValue postfix = this->GeneratorTarget->GetProperty(postfixName)) { - assemblyName += *postfix; - } + std::string assemblyName = GetAssemblyName(config); e1.Element("AssemblyName", assemblyName); if (cmStateEnums::EXECUTABLE == this->GeneratorTarget->GetType()) { @@ -1842,6 +1827,15 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( symbolic = sf->GetPropertyAsBool("SYMBOLIC"); } } + + // Without UpToDateCheckInput VS will ignore the dependency files + // when doing it's fast up-to-date check and the command will not run + if (this->ProjectType == VsProjectType::csproj && + this->GeneratorTarget->IsDotNetSdkTarget()) { + Elem e1(e0, "ItemGroup"); + Elem e2(e1, "UpToDateCheckInput"); + e2.Attribute("Include", dep); + } } } if (this->ProjectType != VsProjectType::csproj) { @@ -1935,10 +1929,16 @@ void cmVisualStudio10TargetGenerator::WriteCustomRuleCSharp( } this->CSharpCustomCommandNames.insert(name); Elem e1(e0, "Target"); - e1.Attribute("Condition", this->CalcCondition(config)); + e1.Attribute("Condition", cmStrCat("'$(Configuration)' == '", config, '\'')); e1.S << "\n Name=\"" << name << "\""; e1.S << "\n Inputs=\"" << cmVS10EscapeAttr(inputs) << "\""; e1.S << "\n Outputs=\"" << cmVS10EscapeAttr(outputs) << "\""; + + // Run before sources are compiled... + e1.S << "\n BeforeTargets=\"CoreCompile\""; // BeforeBuild + // ...but after output directory has been created + e1.S << "\n DependsOnTargets=\"PrepareForBuild\""; + if (!comment.empty()) { Elem(e1, "Exec").Attribute("Command", cmStrCat("echo ", comment)); } @@ -2544,66 +2544,73 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) } const char* tool = nullptr; - switch (si.Kind) { - case cmGeneratorTarget::SourceKindAppManifest: - tool = "AppxManifest"; - break; - case cmGeneratorTarget::SourceKindCertificate: - tool = "None"; - break; - case cmGeneratorTarget::SourceKindCustomCommand: - // Handled elsewhere. - break; - case cmGeneratorTarget::SourceKindExternalObject: - tool = "Object"; - break; - case cmGeneratorTarget::SourceKindExtra: - this->WriteExtraSource(e1, si.Source, toolSettings); - break; - case cmGeneratorTarget::SourceKindHeader: - this->WriteHeaderSource(e1, si.Source, toolSettings); - break; - case cmGeneratorTarget::SourceKindIDL: - tool = "Midl"; - break; - case cmGeneratorTarget::SourceKindManifest: - // Handled elsewhere. - break; - case cmGeneratorTarget::SourceKindModuleDefinition: - tool = "None"; - break; - case cmGeneratorTarget::SourceKindCxxModuleSource: - case cmGeneratorTarget::SourceKindUnityBatched: - case cmGeneratorTarget::SourceKindObjectSource: { - const std::string& lang = si.Source->GetLanguage(); - if (lang == "C"_s || lang == "CXX"_s) { - tool = "ClCompile"; - } else if (lang == "ASM_MARMASM"_s && - this->GlobalGenerator->IsMarmasmEnabled()) { - tool = "MARMASM"; - } else if (lang == "ASM_MASM"_s && - this->GlobalGenerator->IsMasmEnabled()) { - tool = "MASM"; - } else if (lang == "ASM_NASM"_s && - this->GlobalGenerator->IsNasmEnabled()) { - tool = "NASM"; - } else if (lang == "RC"_s) { - tool = "ResourceCompile"; - } else if (lang == "CSharp"_s) { - tool = "Compile"; - } else if (lang == "CUDA"_s && - this->GlobalGenerator->IsCudaEnabled()) { - tool = "CudaCompile"; - } else { + const cmValue toolOverride = si.Source->GetProperty("VS_TOOL_OVERRIDE"); + + if (cmNonempty(toolOverride)) { + // Custom tool specified: the file will be built in a user-defined way + this->WriteExtraSource(e1, si.Source, toolSettings); + } else { + switch (si.Kind) { + case cmGeneratorTarget::SourceKindAppManifest: + tool = "AppxManifest"; + break; + case cmGeneratorTarget::SourceKindCertificate: tool = "None"; - } - } break; - case cmGeneratorTarget::SourceKindResx: - this->ResxObjs.push_back(si.Source); - break; - case cmGeneratorTarget::SourceKindXaml: - this->XamlObjs.push_back(si.Source); - break; + break; + case cmGeneratorTarget::SourceKindCustomCommand: + // Handled elsewhere. + break; + case cmGeneratorTarget::SourceKindExternalObject: + tool = "Object"; + break; + case cmGeneratorTarget::SourceKindExtra: + this->WriteExtraSource(e1, si.Source, toolSettings); + break; + case cmGeneratorTarget::SourceKindHeader: + this->WriteHeaderSource(e1, si.Source, toolSettings); + break; + case cmGeneratorTarget::SourceKindIDL: + tool = "Midl"; + break; + case cmGeneratorTarget::SourceKindManifest: + // Handled elsewhere. + break; + case cmGeneratorTarget::SourceKindModuleDefinition: + tool = "None"; + break; + case cmGeneratorTarget::SourceKindCxxModuleSource: + case cmGeneratorTarget::SourceKindUnityBatched: + case cmGeneratorTarget::SourceKindObjectSource: { + const std::string& lang = si.Source->GetLanguage(); + if (lang == "C"_s || lang == "CXX"_s) { + tool = "ClCompile"; + } else if (lang == "ASM_MARMASM"_s && + this->GlobalGenerator->IsMarmasmEnabled()) { + tool = "MARMASM"; + } else if (lang == "ASM_MASM"_s && + this->GlobalGenerator->IsMasmEnabled()) { + tool = "MASM"; + } else if (lang == "ASM_NASM"_s && + this->GlobalGenerator->IsNasmEnabled()) { + tool = "NASM"; + } else if (lang == "RC"_s) { + tool = "ResourceCompile"; + } else if (lang == "CSharp"_s) { + tool = "Compile"; + } else if (lang == "CUDA"_s && + this->GlobalGenerator->IsCudaEnabled()) { + tool = "CudaCompile"; + } else { + tool = "None"; + } + } break; + case cmGeneratorTarget::SourceKindResx: + this->ResxObjs.push_back(si.Source); + break; + case cmGeneratorTarget::SourceKindXaml: + this->XamlObjs.push_back(si.Source); + break; + } } std::string config; @@ -2673,6 +2680,12 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) } } } + if (haveUnityBuild && strcmp(tool, "CudaCompile") == 0 && + si.Source->GetProperty("UNITY_SOURCE_FILE")) { + if (!si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) { + exclude_configs = all_configs; + } + } if (si.Kind == cmGeneratorTarget::SourceKindObjectSource || si.Kind == cmGeneratorTarget::SourceKindUnityBatched) { @@ -3263,6 +3276,19 @@ std::string cmVisualStudio10TargetGenerator::GetTargetOutputName() const return cmStrCat(nameComponents.prefix, nameComponents.base); } +std::string cmVisualStudio10TargetGenerator::GetAssemblyName( + std::string const& config) const +{ + std::string postfixName = + cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX"); + std::string assemblyName = this->GeneratorTarget->GetOutputName( + config, cmStateEnums::RuntimeBinaryArtifact); + if (cmValue postfix = this->GeneratorTarget->GetProperty(postfixName)) { + assemblyName += *postfix; + } + return assemblyName; +} + bool cmVisualStudio10TargetGenerator::ComputeClOptions() { return std::all_of( @@ -3436,7 +3462,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( if (gotOneSys) { if (auto sysIncludeFlagWarning = this->Makefile->GetDefinition( - cmStrCat("_CMAKE_INCLUDE_SYSTEM_FLAG_", this->LangForClCompile, + cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", this->LangForClCompile, "_WARNING"))) { flags = cmStrCat(flags, ' ', *sysIncludeFlagWarning); } @@ -4446,14 +4472,21 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( this->AddTargetsFileAndConfigPair(ti, config); } - std::vector const& ldirs = cli.GetDirectories(); std::vector linkDirs; + std::vector const& ldirs = cli.GetDirectories(); for (std::string const& d : ldirs) { // first just full path linkDirs.push_back(d); // next path with configuration type Debug, Release, etc linkDirs.emplace_back(cmStrCat(d, "/$(Configuration)")); } + + std::string const& linkDirsString = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LINK_DIRECTORIES")); + for (const std::string& d : cmList(linkDirsString)) { + linkDirs.push_back(d); + } + linkDirs.push_back("%(AdditionalLibraryDirectories)"); linkOptions.AddFlag("AdditionalLibraryDirectories", linkDirs); @@ -4879,6 +4912,71 @@ void cmVisualStudio10TargetGenerator::WriteEvent( } } +void cmVisualStudio10TargetGenerator::WriteSdkStyleEvents( + Elem& e0, std::string const& configName) +{ + this->WriteSdkStyleEvent(e0, "PreLink", "BeforeTargets", "Link", + this->GeneratorTarget->GetPreLinkCommands(), + configName); + this->WriteSdkStyleEvent(e0, "PreBuild", "BeforeTargets", "PreBuildEvent", + this->GeneratorTarget->GetPreBuildCommands(), + configName); + this->WriteSdkStyleEvent(e0, "PostBuild", "AfterTargets", "PostBuildEvent", + this->GeneratorTarget->GetPostBuildCommands(), + configName); +} + +void cmVisualStudio10TargetGenerator::WriteSdkStyleEvent( + Elem& e0, const std::string& name, const std::string& when, + const std::string& target, std::vector const& commands, + std::string const& configName) +{ + if (commands.empty()) { + return; + } + Elem e1(e0, "Target"); + e1.Attribute("Condition", + cmStrCat("'$(Configuration)' == '", configName, '\'')); + e1.Attribute("Name", name + configName); + e1.Attribute(when.c_str(), target); + e1.SetHasElements(); + + cmLocalVisualStudio7Generator* lg = this->LocalGenerator; + std::string script; + const char* pre = ""; + std::string comment; + bool stdPipesUTF8 = false; + for (cmCustomCommand const& cc : commands) { + cmCustomCommandGenerator ccg(cc, configName, lg); + if (!ccg.HasOnlyEmptyCommandLines()) { + comment += pre; + comment += lg->ConstructComment(ccg); + script += pre; + pre = "\n"; + script += lg->ConstructScript(ccg); + + stdPipesUTF8 = stdPipesUTF8 || cc.GetStdPipesUTF8(); + } + } + if (!script.empty()) { + script += lg->FinishConstructScript(this->ProjectType); + } + comment = cmVS10EscapeComment(comment); + + std::string strippedComment = comment; + strippedComment.erase( + std::remove(strippedComment.begin(), strippedComment.end(), '\t'), + strippedComment.end()); + std::ostringstream oss; + if (!comment.empty() && !strippedComment.empty()) { + oss << "echo " << comment << "\n"; + } + oss << script << "\n"; + + Elem e2(e1, "Exec"); + e2.Attribute("Command", oss.str()); +} + void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) { cmGlobalGenerator::TargetDependSet const& unordered = @@ -5027,11 +5125,11 @@ std::string ComputeCertificateThumbprint(const std::string& source) cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (certFile != INVALID_HANDLE_VALUE && certFile != nullptr) { + if (certFile != INVALID_HANDLE_VALUE && certFile) { DWORD fileSize = GetFileSize(certFile, nullptr); if (fileSize != INVALID_FILE_SIZE) { auto certData = cm::make_unique(fileSize); - if (certData != nullptr) { + if (certData) { DWORD dwRead = 0; if (ReadFile(certFile, certData.get(), fileSize, &dwRead, nullptr)) { cryptBlob.cbData = fileSize; @@ -5042,11 +5140,11 @@ std::string ComputeCertificateThumbprint(const std::string& source) // Open the certificate as a store certStore = PFXImportCertStore(&cryptBlob, nullptr, CRYPT_EXPORTABLE); - if (certStore != nullptr) { + if (certStore) { // There should only be 1 cert. certContext = CertEnumCertificatesInStore(certStore, certContext); - if (certContext != nullptr) { + if (certContext) { // The hash is 20 bytes BYTE hashData[20]; DWORD hashLength = 20; @@ -5841,7 +5939,8 @@ void cmVisualStudio10TargetGenerator::UpdateCache() { std::vector packageReferences; - if (this->GeneratorTarget->HasPackageReferences()) { + if (this->GeneratorTarget->IsDotNetSdkTarget() || + this->GeneratorTarget->HasPackageReferences()) { // Store a cache entry that later determines, if a package restore is // required. this->GeneratorTarget->Makefile->AddCacheDefinition( @@ -5858,7 +5957,7 @@ void cmVisualStudio10TargetGenerator::UpdateCache() OrderedTargetDependSet depends(unordered, CMAKE_CHECK_BUILD_SYSTEM_TARGET); for (cmGeneratorTarget const* dt : depends) { - if (dt->HasPackageReferences()) { + if (dt->IsDotNetSdkTarget() || dt->HasPackageReferences()) { this->GeneratorTarget->Makefile->AddCacheDefinition( cmStrCat(this->GeneratorTarget->GetName(), "_REQUIRES_VS_PACKAGE_RESTORE"), diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 056f42688..9b7ae10a2 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -91,6 +91,7 @@ private: void WriteDotNetReference(Elem& e1, std::string const& ref, std::string const& hint, std::string const& config); + void WriteFrameworkReferences(Elem& e0); void WriteDotNetDocumentationFile(Elem& e0); void WriteImports(Elem& e0); void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref); @@ -121,6 +122,7 @@ private: std::vector GetIncludes(std::string const& config, std::string const& lang) const; std::string GetTargetOutputName() const; + std::string GetAssemblyName(std::string const& config) const; bool ComputeClOptions(); bool ComputeClOptions(std::string const& configName); @@ -193,6 +195,11 @@ private: void WriteEvent(Elem& e1, std::string const& name, std::vector const& commands, std::string const& configName); + void WriteSdkStyleEvents(Elem& e0, std::string const& configName); + void WriteSdkStyleEvent(Elem& e0, const std::string& name, + const std::string& when, const std::string& target, + std::vector const& commands, + std::string const& configName); void WriteGroupSources(Elem& e0, std::string const& name, ToolSources const& sources, std::vector&); @@ -282,8 +289,6 @@ private: void WriteCommonPropertyGroupGlobals( cmVisualStudio10TargetGenerator::Elem& e1); - bool HasCustomCommands() const; - std::unordered_map ParsedToolTargetSettings; bool PropertyIsSameInAllConfigs(const ConfigToSettings& toolSettings, const std::string& propName); diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx index dd8f459c2..2fbf657b5 100644 --- a/Source/cmWorkerPool.cxx +++ b/Source/cmWorkerPool.cxx @@ -71,7 +71,7 @@ private: void cmUVPipeBuffer::reset() { - if (this->UVPipe_.get() != nullptr) { + if (this->UVPipe_.get()) { this->EndFunction_ = nullptr; this->DataFunction_ = nullptr; this->Buffer_.clear(); @@ -83,7 +83,7 @@ void cmUVPipeBuffer::reset() bool cmUVPipeBuffer::init(uv_loop_t* uv_loop) { this->reset(); - if (uv_loop == nullptr) { + if (!uv_loop) { return false; } int ret = this->UVPipe_.init(*uv_loop, 0, this); @@ -93,7 +93,7 @@ bool cmUVPipeBuffer::init(uv_loop_t* uv_loop) bool cmUVPipeBuffer::startRead(DataFunction dataFunction, EndFunction endFunction) { - if (this->UVPipe_.get() == nullptr) { + if (!this->UVPipe_.get()) { return false; } if (!dataFunction || !endFunction) { @@ -120,7 +120,7 @@ void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread, { auto& pipe = *reinterpret_cast(stream->data); if (nread > 0) { - if (buf->base != nullptr) { + if (buf->base) { // Call data function pipe.DataFunction_(DataRange(buf->base, buf->base + nread)); } @@ -200,7 +200,7 @@ void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result, bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop, std::function finishedCallback) { - if (this->IsStarted() || (this->Result() == nullptr)) { + if (this->IsStarted() || !this->Result()) { return false; } @@ -364,9 +364,8 @@ void cmUVReadOnlyProcess::UVTryFinish() // There still might be data in the pipes after the process has finished. // Therefore check if the process is finished AND all pipes are closed // before signaling the worker thread to continue. - if ((this->UVProcess_.get() != nullptr) || - (this->UVPipeOut_.uv_pipe() != nullptr) || - (this->UVPipeErr_.uv_pipe() != nullptr)) { + if ((this->UVProcess_.get()) || (this->UVPipeOut_.uv_pipe()) || + (this->UVPipeErr_.uv_pipe())) { return; } this->IsFinished_ = true; diff --git a/Source/cmXCOFF.cxx b/Source/cmXCOFF.cxx index a6d278dd0..e459103f6 100644 --- a/Source/cmXCOFF.cxx +++ b/Source/cmXCOFF.cxx @@ -6,6 +6,7 @@ #include #include +#include #include "cmsys/FStream.hxx" @@ -16,10 +17,19 @@ # define __XCOFF32__ # define __XCOFF64__ # include +# define __AR_BIG__ +# include #else # error "This source may be compiled only on AIX." #endif +// Function to align a number num with align_num bytes. +size_t align(size_t num, int align_num) +{ + align_num = 1 << (align_num); + return (((num + align_num - 1) / align_num) * align_num); +} + class cmXCOFFInternal { public: @@ -79,6 +89,12 @@ struct XCOFF64 }; const unsigned char xcoff64_magic[] = { 0x01, 0xF7 }; +enum class IsArchive +{ + No, + Yes, +}; + template class Impl : public cmXCOFFInternal { @@ -93,6 +109,20 @@ class Impl : public cmXCOFFInternal std::streamoff LoaderImportFileTablePos = 0; std::vector LoaderImportFileTable; + bool Read(fl_hdr& x) + { + // FIXME: Add byte swapping if needed. + return static_cast( + this->Stream->read(reinterpret_cast(&x), sizeof(x))); + } + + bool Read(ar_hdr& x) + { + // FIXME: Add byte swapping if needed. + return static_cast( + this->Stream->read(reinterpret_cast(&x), sizeof(x))); + } + bool Read(typename XCOFF::filehdr& x) { // FIXME: Add byte swapping if needed. @@ -130,18 +160,40 @@ class Impl : public cmXCOFFInternal public: Impl(cmXCOFF* external, std::unique_ptr fin, - cmXCOFF::Mode mode); + cmXCOFF::Mode mode, IsArchive IsBigArchive, int nextEvenBytePos); cm::optional GetLibPath() override; bool SetLibPath(cm::string_view libPath) override; bool RemoveLibPath() override; + + // Needed for SetLibPath () to move in a archive while write. + IsArchive is_big_archive; + int nextEvenByte; + int bytes_to_align; }; template Impl::Impl(cmXCOFF* external, std::unique_ptr fin, - cmXCOFF::Mode mode) + cmXCOFF::Mode mode, IsArchive IsBigArchive, + int nextEvenBytePos) : cmXCOFFInternal(external, std::move(fin), mode) { + this->is_big_archive = IsBigArchive; + this->nextEvenByte = nextEvenBytePos; + if (this->is_big_archive == IsArchive::Yes) { + fl_hdr header; + this->Stream->read(reinterpret_cast(&header), sizeof(fl_hdr)); + + long long fstmoff = std::atoll(header.fl_fstmoff); + this->Stream->seekg(fstmoff, std::ios::beg); + + ar_hdr arHeader; + this->Stream->read(reinterpret_cast(&arHeader), sizeof(ar_hdr)); + + // Move the pointer to next even byte after reading headers. + this->Stream->seekg(this->nextEvenByte, std::ios::cur); + } + if (!this->Read(this->FileHeader)) { this->SetErrorMessage("Failed to read XCOFF file header."); return; @@ -158,6 +210,10 @@ Impl::Impl(cmXCOFF* external, std::unique_ptr fin, this->SetErrorMessage("XCOFF loader section missing."); return; } + this->bytes_to_align = + this->AuxHeader.o_algntext > this->AuxHeader.o_algndata + ? this->AuxHeader.o_algntext + : this->AuxHeader.o_algndata; if (!this->Stream->seekg((this->AuxHeader.o_snloader - 1) * sizeof(typename XCOFF::scnhdr), std::ios::cur)) { @@ -172,17 +228,33 @@ Impl::Impl(cmXCOFF* external, std::unique_ptr fin, this->SetErrorMessage("XCOFF loader section header missing STYP_LOADER."); return; } - if (!this->Stream->seekg(this->LoaderSectionHeader.s_scnptr, - std::ios::beg)) { + if (is_big_archive == IsArchive::Yes) { + size_t header_len = this->nextEvenByte + sizeof(fl_hdr) + sizeof(ar_hdr); + size_t scnptrFromArchiveStart = this->LoaderSectionHeader.s_scnptr + + align(header_len, this->bytes_to_align); + if (!this->Stream->seekg(scnptrFromArchiveStart, std::ios::beg)) { + this->SetErrorMessage("Failed to seek to XCOFF loader header."); + return; + } + } else if (!this->Stream->seekg(this->LoaderSectionHeader.s_scnptr, + std::ios::beg)) { this->SetErrorMessage("Failed to seek to XCOFF loader header."); return; } + if (!this->Read(this->LoaderHeader)) { this->SetErrorMessage("Failed to read XCOFF loader header."); return; } - this->LoaderImportFileTablePos = - this->LoaderSectionHeader.s_scnptr + this->LoaderHeader.l_impoff; + if (is_big_archive == IsArchive::Yes) { + size_t header_len = sizeof(fl_hdr) + sizeof(ar_hdr) + this->nextEvenByte; + size_t scnptrFromArchiveStartPlusOff = this->LoaderSectionHeader.s_scnptr + + this->LoaderHeader.l_impoff + align(header_len, this->bytes_to_align); + this->LoaderImportFileTablePos = scnptrFromArchiveStartPlusOff; + } else { + this->LoaderImportFileTablePos = + this->LoaderSectionHeader.s_scnptr + this->LoaderHeader.l_impoff; + } if (!this->Stream->seekg(this->LoaderImportFileTablePos)) { this->SetErrorMessage( "Failed to seek to XCOFF loader import file id table."); @@ -212,19 +284,22 @@ cm::optional Impl::GetLibPath() template bool Impl::SetLibPath(cm::string_view libPath) { - // The new LIBPATH must end in the standard AIX LIBPATH. -#define CM_AIX_LIBPATH "/usr/lib:/lib" + // The new LIBPATH must contain standard AIX LIBPATH entries. std::string libPathBuf; - if (libPath != CM_AIX_LIBPATH && - !cmHasLiteralSuffix(libPath, ":" CM_AIX_LIBPATH)) { - libPathBuf = std::string(libPath); - if (!libPathBuf.empty() && libPathBuf.back() != ':') { - libPathBuf.push_back(':'); - } - libPathBuf += CM_AIX_LIBPATH; - libPath = libPathBuf; - } -#undef CM_AIX_LIBPATH +#define ENSURE_ENTRY(x) \ + if (libPath != x && !cmHasLiteralPrefix(libPath, x ":") && \ + !cmHasLiteralSuffix(libPath, ":" x) && \ + libPath.find(":" x ":") == std::string::npos) { \ + libPathBuf = std::string(libPath); \ + if (!libPathBuf.empty() && libPathBuf.back() != ':') { \ + libPathBuf.push_back(':'); \ + } \ + libPathBuf += x; \ + libPath = libPathBuf; \ + } + ENSURE_ENTRY("/usr/lib") + ENSURE_ENTRY("/lib") +#undef ENSURE_ENTRY auto oldEnd = std::find(this->LoaderImportFileTable.begin(), this->LoaderImportFileTable.end(), '\0'); @@ -251,9 +326,18 @@ bool Impl::SetLibPath(cm::string_view libPath) this->LoaderImportFileTable = std::move(ift); } - if (!this->Stream->seekp(this->LoaderSectionHeader.s_scnptr + - offsetof(typename XCOFF::ldhdr, l_istlen), - std::ios::beg)) { + size_t scnptr; + if (this->is_big_archive == IsArchive::Yes) { + size_t header_len = sizeof(fl_hdr) + sizeof(ar_hdr) + this->nextEvenByte; + scnptr = this->LoaderSectionHeader.s_scnptr + + offsetof(typename XCOFF::ldhdr, l_istlen) + + align(header_len, this->bytes_to_align); + } else { + scnptr = this->LoaderSectionHeader.s_scnptr + + offsetof(typename XCOFF::ldhdr, l_istlen); + } + + if (!this->Stream->seekp(scnptr, std::ios::beg)) { this->SetErrorMessage( "Failed to seek to XCOFF loader header import file id table length."); return false; @@ -285,6 +369,20 @@ bool Impl::RemoveLibPath() } } +IsArchive check_if_big_archive(const char* fname) +{ + int len = std::strlen(fname); + if (len < 2) { + return IsArchive::No; + } + + if (std::strcmp(fname + len - 2, ".a") == 0) { + return IsArchive::Yes; + } else { + return IsArchive::No; + } +} + //============================================================================ // External class implementation. @@ -305,6 +403,52 @@ cmXCOFF::cmXCOFF(const char* fname, Mode mode) // Read the XCOFF magic number. unsigned char magic[2]; + + // To hold the length of the shared object name in the path. + int nextEvenByte = 0; + + // Read archive name length. + int archive_name_length = 0; + // If a big archive, we will read the archive file headers first. + // Then move to the next even byte to get the magic number. + if (check_if_big_archive(fname) == IsArchive::Yes) { + fl_hdr header; + f->read(reinterpret_cast(&header), sizeof(fl_hdr)); + + if (std::strncmp(header.fl_magic, AIAMAGBIG, SAIAMAG) != 0) { + this->ErrorMessage = "Not a valid archive file or wrong format"; + + return; + } + long long fstmoff = std::atoll(header.fl_fstmoff); + f->seekg(fstmoff, std::ios::beg); + + ar_hdr arHeader; + f->read(reinterpret_cast(&arHeader), sizeof(ar_hdr)); + + { + errno = 0; + char* ar_namlen_endp; + unsigned long ar_namlen = + strtoul(arHeader.ar_namlen, &ar_namlen_endp, 10); + if ((ar_namlen_endp != arHeader.ar_namlen) && (errno == 0)) { + archive_name_length = static_cast(ar_namlen); + } else { + this->ErrorMessage = "Error parsing archive name length."; + return; + } + } + + // Round off to even byte. + if (archive_name_length % 2 == 0) { + nextEvenByte = archive_name_length; + } else { + nextEvenByte = archive_name_length + 1; + } + + f->seekg(nextEvenByte, std::ios::cur); + } + if (!f->read(reinterpret_cast(magic), sizeof(magic))) { this->ErrorMessage = "Error reading XCOFF magic number."; return; @@ -316,9 +460,11 @@ cmXCOFF::cmXCOFF(const char* fname, Mode mode) // Check the XCOFF type. if (magic[0] == xcoff32_magic[0] && magic[1] == xcoff32_magic[1]) { - this->Internal = cm::make_unique>(this, std::move(f), mode); + this->Internal = cm::make_unique>( + this, std::move(f), mode, check_if_big_archive(fname), nextEvenByte); } else if (magic[0] == xcoff64_magic[0] && magic[1] == xcoff64_magic[1]) { - this->Internal = cm::make_unique>(this, std::move(f), mode); + this->Internal = cm::make_unique>( + this, std::move(f), mode, check_if_big_archive(fname), nextEvenByte); } else { this->ErrorMessage = "File is not a XCOFF32 or XCOFF64 binary."; } diff --git a/Source/cmXcFramework.cxx b/Source/cmXcFramework.cxx index e377fc9af..de9971352 100644 --- a/Source/cmXcFramework.cxx +++ b/Source/cmXcFramework.cxx @@ -173,6 +173,9 @@ const cmXcFrameworkPlistLibrary* cmXcFrameworkPlist::SelectSuitableLibrary( if (mf.PlatformIsAppleSimulator()) { systemVariant = cmXcFrameworkPlistSupportedPlatformVariant::simulator; } + if (mf.PlatformIsAppleCatalyst()) { + systemVariant = cmXcFrameworkPlistSupportedPlatformVariant::maccatalyst; + } for (auto const& lib : this->AvailableLibraries) { std::string supportedSystemName; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 775265f01..fcee5e56d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -97,7 +97,6 @@ # include "cmGlobalBorlandMakefileGenerator.h" # include "cmGlobalJOMMakefileGenerator.h" # include "cmGlobalNMakeMakefileGenerator.h" -# include "cmGlobalVisualStudio12Generator.h" # include "cmGlobalVisualStudio14Generator.h" # include "cmGlobalVisualStudioVersionedGenerator.h" # include "cmVSSetupHelper.h" @@ -1075,10 +1074,7 @@ void cmake::SetArgs(const std::vector& args) CommandArgument{ "--graphviz", "No file specified for --graphviz", CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { - std::string path = - cmSystemTools::CollapseFullPath(value); - cmSystemTools::ConvertToUnixSlashes(path); - state->GraphVizFile = path; + state->SetGraphVizFile(value); return true; } }, @@ -1590,6 +1586,12 @@ void cmake::SetArgs(const std::vector& args) } } + if (!expandedPreset->GraphVizFile.empty()) { + if (this->GraphVizFile.empty()) { + this->SetGraphVizFile(expandedPreset->GraphVizFile); + } + } + this->SetWarningFromPreset("dev", expandedPreset->WarnDev, expandedPreset->ErrorDev); this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated, @@ -2651,7 +2653,6 @@ std::unique_ptr cmake::EvaluateDefaultGlobalGenerator() }; static VSVersionedGenerator const vsGenerators[] = { { "14.0", "Visual Studio 14 2015" }, // - { "12.0", "Visual Studio 12 2013" }, // }; static const char* const vsEntries[] = { "\\Setup\\VC;ProductDir", // @@ -2732,7 +2733,7 @@ bool cmake::StartDebuggerIfEnabled() return true; } - if (DebugAdapter == nullptr) { + if (!DebugAdapter) { if (this->GetDebuggerPipe().empty()) { std::cerr << "Error: --debugger-pipe must be set when debugging is enabled.\n"; @@ -2762,7 +2763,7 @@ void cmake::StopDebuggerIfNeeded(int exitCode) } // The debug adapter may have failed to start (e.g. invalid pipe path). - if (DebugAdapter != nullptr) { + if (DebugAdapter) { DebugAdapter->ReportExitCode(exitCode); DebugAdapter.reset(); } @@ -2936,6 +2937,7 @@ int cmake::Generate() this->SaveCache(this->GetHomeOutputDirectory()); #if !defined(CMAKE_BOOTSTRAP) + this->GetGlobalGenerator()->WriteInstallJson(); this->FileAPI->WriteReplies(); #endif @@ -3045,7 +3047,6 @@ void cmake::AddDefaultGenerators() this->Generators.push_back( cmGlobalVisualStudioVersionedGenerator::NewFactory15()); this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory()); - this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory()); this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory()); @@ -3805,7 +3806,7 @@ int cmake::Build(int jobs, std::string dir, std::vector targets, // itself, there is the risk of building an out-of-date solution file due // to limitations of the underlying build system. std::string const stampList = cachePath + "/" + "CMakeFiles/" + - cmGlobalVisualStudio12Generator::GetGenerateStampList(); + cmGlobalVisualStudio14Generator::GetGenerateStampList(); // Note that the stampList file only exists for VS generators. if (cmSystemTools::FileExists(stampList)) { diff --git a/Source/cmake.h b/Source/cmake.h index 2d17ed291..cfe4edd20 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -26,6 +26,7 @@ #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" +#include "cmSystemTools.h" #include "cmValue.h" #if !defined(CMAKE_BOOTSTRAP) @@ -298,6 +299,14 @@ public: this->GeneratorToolsetSet = true; } + //! Set the name of the graphviz file. + void SetGraphVizFile(std::string const& ts) + { + std::string path = cmSystemTools::CollapseFullPath(ts); + cmSystemTools::ConvertToUnixSlashes(path); + this->GraphVizFile = path; + } + bool IsAKnownSourceExtension(cm::string_view ext) const { return this->CLikeSourceFileExtensions.Test(ext) || diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 8462734d3..b57cdb312 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,6 +27,7 @@ #include "cmConsoleBuf.h" #include "cmDocumentationEntry.h" #include "cmGlobalGenerator.h" +#include "cmInstallScriptHandler.h" #include "cmList.h" #include "cmMakefile.h" #include "cmMessageMetadata.h" @@ -43,6 +45,7 @@ #endif #include "cmsys/Encoding.hxx" +#include "cmsys/RegularExpression.hxx" #include "cmsys/Terminal.h" namespace { @@ -68,15 +71,21 @@ const cmDocumentationEntry cmDocumentationUsageNote = { "Run 'cmake --help' for more information." }; -const cmDocumentationEntry cmDocumentationOptions[31] = { +const cmDocumentationEntry cmDocumentationOptions[34] = { { "--preset ,--preset=", "Specify a configure preset." }, { "--list-presets[=]", "List available presets." }, - { "-E", "CMake command mode." }, + { "--workflow []", "Run a workflow preset." }, + { "-E", "CMake command mode. Run \"cmake -E\" for a summary of commands." }, { "-L[A][H]", "List non-advanced cached variables." }, + { "-LR[A][H] ", "Show cached variables that match the regex." }, { "--fresh", "Configure a fresh build tree, removing any existing cache file." }, - { "--build ", "Build a CMake-generated project binary tree." }, - { "--install ", "Install a CMake-generated project binary tree." }, + { "--build ", + "Build a CMake-generated project binary tree. Run \"cmake --build\" to " + "see compatible options and a quick help." }, + { "--install ", + "Install a CMake-generated project binary tree. Run \"cmake --install\" " + "to see compatible options and a quick help." }, { "--open ", "Open generated project in the associated application." }, { "-N", "View mode only." }, { "-P ", "Process script mode." }, @@ -85,6 +94,8 @@ const cmDocumentationEntry cmDocumentationOptions[31] = { "Generate graphviz of dependencies, see CMakeGraphVizOptions.cmake for " "more." }, { "--system-information [file]", "Dump information about this system." }, + { "--print-config-dir", + "Print CMake config directory for user-wide FileAPI queries." }, { "--log-level=", "Set the verbosity of messages from CMake files. " "--loglevel is also accepted for backward compatibility reasons." }, @@ -168,11 +179,11 @@ void cmakemainMessageCallback(const std::string& m, // cannot use it to print messages. Another implementation will // be needed to print colored messages on Windows. static_cast(md); - std::cerr << m << cmakemainGetStack(cm) << '\n' << std::flush; + std::cerr << m << cmakemainGetStack(cm) << std::endl; #else cmsysTerminal_cfprintf(md.desiredColor, stderr, "%s", m.c_str()); fflush(stderr); // stderr is buffered in some cases. - std::cerr << cmakemainGetStack(cm) << '\n' << std::flush; + std::cerr << cmakemainGetStack(cm) << std::endl; #endif } @@ -191,6 +202,21 @@ void cmakemainProgressCallback(const std::string& m, float prog, cmake* cm) } } +std::function getShowCachedCallback( + bool& show_flag, bool* help_flag = nullptr, std::string* filter = nullptr) +{ + return [=, &show_flag](std::string const& value) -> bool { + show_flag = true; + if (help_flag) { + *help_flag = true; + } + if (filter) { + *filter = value; + } + return true; + }; +} + int do_cmake(int ac, char const* const* av) { if (cmSystemTools::GetCurrentWorkingDirectory().empty()) { @@ -242,6 +268,8 @@ int do_cmake(int ac, char const* const* av) bool list_cached = false; bool list_all_cached = false; bool list_help = false; + // (Regex) Filter on the cached variable(s) to print. + std::string filter_var_name; bool view_only = false; cmake::WorkingMode workingMode = cmake::NORMAL_MODE; std::vector parsedArgs; @@ -266,13 +294,25 @@ int do_cmake(int ac, char const* const* av) CommandArgument{ "-N", CommandArgument::Values::Zero, CommandArgument::setToTrue(view_only) }, CommandArgument{ "-LAH", CommandArgument::Values::Zero, - CommandArgument::setToTrue(list_all_cached, list_help) }, + getShowCachedCallback(list_all_cached, &list_help) }, CommandArgument{ "-LA", CommandArgument::Values::Zero, - CommandArgument::setToTrue(list_all_cached) }, + getShowCachedCallback(list_all_cached) }, CommandArgument{ "-LH", CommandArgument::Values::Zero, - CommandArgument::setToTrue(list_cached, list_help) }, + getShowCachedCallback(list_cached, &list_help) }, CommandArgument{ "-L", CommandArgument::Values::Zero, - CommandArgument::setToTrue(list_cached) }, + getShowCachedCallback(list_cached) }, + CommandArgument{ + "-LRAH", CommandArgument::Values::One, + getShowCachedCallback(list_all_cached, &list_help, &filter_var_name) }, + CommandArgument{ + "-LRA", CommandArgument::Values::One, + getShowCachedCallback(list_all_cached, nullptr, &filter_var_name) }, + CommandArgument{ + "-LRH", CommandArgument::Values::One, + getShowCachedCallback(list_cached, &list_help, &filter_var_name) }, + CommandArgument{ + "-LR", CommandArgument::Values::One, + getShowCachedCallback(list_cached, nullptr, &filter_var_name) }, CommandArgument{ "-P", "No script specified for argument -P", CommandArgument::Values::One, CommandArgument::RequiresSeparator::No, @@ -367,7 +407,15 @@ int do_cmake(int ac, char const* const* av) if (list_cached || list_all_cached) { std::cout << "-- Cache values" << std::endl; std::vector keys = cm.GetState()->GetCacheEntryKeys(); + cmsys::RegularExpression regex_var_name; + if (!filter_var_name.empty()) { + regex_var_name.compile(filter_var_name); + } for (std::string const& k : keys) { + if (regex_var_name.is_valid() && !regex_var_name.find(k)) { + continue; + } + cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k); if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC && t != cmStateEnums::UNINITIALIZED) { @@ -429,6 +477,17 @@ int extract_job_number(std::string const& command, } return jobs; } +std::function extract_job_number_lambda_builder( + std::string& dir, int& jobs, const std::string& flag) +{ + return [&dir, &jobs, flag](std::string const& value) -> bool { + jobs = extract_job_number(flag, value); + if (jobs < 0) { + dir.clear(); + } + return true; + }; +}; #endif int do_build(int ac, char const* const* av) @@ -451,20 +510,10 @@ int do_build(int ac, char const* const* av) std::string presetName; bool listPresets = false; - auto jLambda = [&](std::string const& value) -> bool { - jobs = extract_job_number("-j", value); - if (jobs < 0) { - dir.clear(); - } - return true; - }; - auto parallelLambda = [&](std::string const& value) -> bool { - jobs = extract_job_number("--parallel", value); - if (jobs < 0) { - dir.clear(); - } - return true; - }; + auto jLambda = extract_job_number_lambda_builder(dir, jobs, "-j"); + auto parallelLambda = + extract_job_number_lambda_builder(dir, jobs, "--parallel"); + auto targetLambda = [&](std::string const& value) -> bool { if (!value.empty()) { cmList values{ value }; @@ -765,7 +814,7 @@ bool parse_default_directory_permissions(const std::string& permissions, std::ostringstream oss; for (auto i = 0u; i < parsedPermissions.size(); i++) { if (i != 0) { - oss << ";"; + oss << ';'; } oss << parsedPermissions[i]; } @@ -787,9 +836,14 @@ int do_install(int ac, char const* const* av) std::string defaultDirectoryPermissions; std::string prefix; std::string dir; + int jobs = 0; bool strip = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); + auto jLambda = extract_job_number_lambda_builder(dir, jobs, "-j"); + auto parallelLambda = + extract_job_number_lambda_builder(dir, jobs, "--parallel"); + auto verboseLambda = [&](std::string const&) -> bool { verbose = true; return true; @@ -806,6 +860,9 @@ int do_install(int ac, char const* const* av) CommandArgument{ "--default-directory-permissions", CommandArgument::Values::One, CommandArgument::setToValue(defaultDirectoryPermissions) }, + CommandArgument{ "-j", CommandArgument::Values::One, jLambda }, + CommandArgument{ "--parallel", CommandArgument::Values::One, + parallelLambda }, CommandArgument{ "--prefix", CommandArgument::Values::One, CommandArgument::setToValue(prefix) }, CommandArgument{ "--strip", CommandArgument::Values::Zero, @@ -822,7 +879,6 @@ int do_install(int ac, char const* const* av) inputArgs.reserve(ac - 3); cm::append(inputArgs, av + 3, av + ac); for (decltype(inputArgs.size()) i = 0; i < inputArgs.size(); ++i) { - std::string const& arg = inputArgs[i]; bool matched = false; bool parsed = false; @@ -853,6 +909,10 @@ int do_install(int ac, char const* const* av) " --component = Component-based install. Only install .\n" " --default-directory-permissions \n" " Default install permission. Use default permission .\n" + " -j --parallel \n" + " Build in parallel using the given number of jobs. \n" + " The CMAKE_INSTALL_PARALLEL_LEVEL environment variable\n" + " specifies a default parallel level when this option is not given.\n" " --prefix = The installation prefix CMAKE_INSTALL_PREFIX.\n" " --strip = Performing install/strip.\n" " -v --verbose = Enable verbose output.\n" @@ -906,9 +966,29 @@ int do_install(int ac, char const* const* av) } args.emplace_back("-P"); - args.emplace_back(dir + "/cmake_install.cmake"); - return cm.Run(args) ? 1 : 0; + auto handler = cmInstallScriptHandler(dir, component, args); + int ret = 0; + if (!handler.isParallel()) { + args.emplace_back(cmStrCat(dir, "/cmake_install.cmake")); + ret = int(bool(cm.Run(args))); + } else { + if (!jobs) { + jobs = 1; + auto envvar = cmSystemTools::GetEnvVar("CMAKE_INSTALL_PARALLEL_LEVEL"); + if (envvar.has_value()) { + jobs = extract_job_number("", envvar.value()); + if (jobs < 1) { + std::cerr << "Value of CMAKE_INSTALL_PARALLEL_LEVEL environment" + " variable must be a positive integer.\n"; + return 1; + } + } + } + ret = handler.install(jobs); + } + + return int(ret > 0); #endif } @@ -959,6 +1039,11 @@ int do_workflow(int ac, char const* const* av) break; } } + if (!matched && i == 0) { + inputArgs.insert(inputArgs.begin(), "--preset"); + matched = true; + parsed = arguments[0].parse("--preset", i, inputArgs); + } if (!(matched && parsed)) { if (!matched) { presetName.clear(); @@ -972,7 +1057,7 @@ int do_workflow(int ac, char const* const* av) if (presetName.empty() && listPresets == WorkflowListPresets::No) { /* clang-format off */ std::cerr << - "Usage: cmake --workflow [options]\n" + "Usage: cmake --workflow \n" "Options:\n" " --preset = Workflow preset to execute.\n" " --list-presets = List available workflow presets.\n" @@ -1071,6 +1156,13 @@ int main(int ac, char const* const* av) if (strcmp(av[1], "-E") == 0) { return do_command(ac, av, std::move(consoleBuf)); } + if (strcmp(av[1], "--print-config-dir") == 0) { + std::cout << cmSystemTools::ConvertToOutputPath( + cmSystemTools::GetCMakeConfigDirectory().value_or( + std::string())) + << std::endl; + return 0; + } } int ret = do_cmake(ac, av); #ifndef CMAKE_BOOTSTRAP diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 25b2ced30..c82cb32da 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -60,7 +60,6 @@ #include #include #include -#include #include #ifdef _WIN32 @@ -79,103 +78,94 @@ int cmcmd_cmake_ninja_depends(std::vector::const_iterator argBeg, std::vector::const_iterator argEnd); int cmcmd_cmake_ninja_dyndep(std::vector::const_iterator argBeg, std::vector::const_iterator argEnd); +int cmcmd_cmake_module_compile_db( + std::vector::const_iterator argBeg, + std::vector::const_iterator argEnd); namespace { +// ATTENTION If you add new commands, change here, +// and in `cmakemain.cxx` in the options table +const char* const HELP_AVAILABLE_COMMANDS = R"(Available commands: + capabilities - Report capabilities built into cmake in JSON format + cat [--] ... - concat the files and print them to the standard output + chdir dir cmd [args...] - run command in a given directory + compare_files [--ignore-eol] file1 file2 + - check if file1 is same as file2 + copy ... destination - copy files to destination (either file or directory) + copy_directory ... destination - copy content of ... directories to 'destination' directory + copy_directory_if_different ... destination - copy changed content of ... directories to 'destination' directory + copy_if_different ... destination - copy files if it has changed + echo [...] - displays arguments as text + echo_append [...] - displays arguments as text but no new line + env [--unset=NAME ...] [NAME=VALUE ...] [--] [...] + - run command in a modified environment + environment - display the current environment + make_directory ... - create parent and directories + md5sum ... - create MD5 checksum of files + sha1sum ... - create SHA1 checksum of files + sha224sum ... - create SHA224 checksum of files + sha256sum ... - create SHA256 checksum of files + sha384sum ... - create SHA384 checksum of files + sha512sum ... - create SHA512 checksum of files + remove [-f] ... - remove the file(s), use -f to force it (deprecated: use rm instead) + remove_directory ... - remove directories and their contents (deprecated: use rm instead) + rename oldname newname - rename a file or directory (on one volume) + rm [-rRf] [--] ... - remove files or directories, use -f to force it, r or R to remove directories and their contents recursively + sleep ... - sleep for given number of seconds + tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...] + - create or extract a tar or zip archive + time command [args...] - run command and display elapsed time + touch ... - touch a . + touch_nocreate ... - touch a but do not create it. + create_symlink old new - create a symbolic link new -> old + create_hardlink old new - create a hard link new -> old + true - do nothing with an exit code of 0 + false - do nothing with an exit code of 1 +)"; +#if defined(_WIN32) && !defined(__CYGWIN__) +const char* const HELP_AVAILABLE_WINDOWS_COMMANDS = + R"(Available on Windows only: + delete_regv key - delete registry value + env_vs8_wince sdkname - displays a batch file which sets the environment for the provided Windows CE SDK installed in VS2005 + env_vs9_wince sdkname - displays a batch file which sets the environment for the provided Windows CE SDK installed in VS2008 + write_regv key value - write registry value +)"; +#endif + void CMakeCommandUsage(std::string const& program) { - std::ostringstream errorStream; - -#ifndef CMAKE_BOOTSTRAP /* clang-format off */ - errorStream - << "cmake version " << cmVersion::GetCMakeVersion() << "\n"; -/* clang-format on */ + std::string help_screen = cmStrCat( +#ifndef CMAKE_BOOTSTRAP + "cmake version " + , cmVersion::GetCMakeVersion() + , "\n" #else - /* clang-format off */ - errorStream - << "cmake bootstrap\n"; -/* clang-format on */ + "cmake bootstrap\n" #endif - // If you add new commands, change here, - // and in cmakemain.cxx in the options table - /* clang-format off */ - errorStream - << "Usage: " << program << " -E [arguments...]\n" - << "Available commands: \n" - << " capabilities - Report capabilities built into cmake " - "in JSON format\n" - << " cat [--] ... - concat the files and print them to the " - "standard output\n" - << " chdir dir cmd [args...] - run command in a given directory\n" - << " compare_files [--ignore-eol] file1 file2\n" - << " - check if file1 is same as file2\n" - << " copy ... destination - copy files to destination " - "(either file or directory)\n" - << " copy_directory ... destination - copy content of ... " - "directories to 'destination' directory\n" - << " copy_directory_if_different ... destination - copy changed content of ... " - "directories to 'destination' directory\n" - << " copy_if_different ... destination - copy files if it has " - "changed\n" - << " echo [...] - displays arguments as text\n" - << " echo_append [...] - displays arguments as text but no new " - "line\n" - << " env [--unset=NAME ...] [NAME=VALUE ...] [--] [...]\n" - << " - run command in a modified environment\n" - << " environment - display the current environment\n" - << " make_directory ... - create parent and directories\n" - << " md5sum ... - create MD5 checksum of files\n" - << " sha1sum ... - create SHA1 checksum of files\n" - << " sha224sum ... - create SHA224 checksum of files\n" - << " sha256sum ... - create SHA256 checksum of files\n" - << " sha384sum ... - create SHA384 checksum of files\n" - << " sha512sum ... - create SHA512 checksum of files\n" - << " remove [-f] ... - remove the file(s), use -f to force " - "it (deprecated: use rm instead)\n" - << " remove_directory ... - remove directories and their contents (deprecated: use rm instead)\n" - << " rename oldname newname - rename a file or directory " - "(on one volume)\n" - << " rm [-rRf] [--] ... - remove files or directories, use -f " - "to force it, r or R to remove directories and their contents " - "recursively\n" - << " sleep ... - sleep for given number of seconds\n" - << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" - << " - create or extract a tar or zip archive\n" - << " time command [args...] - run command and display elapsed time\n" - << " touch ... - touch a .\n" - << " touch_nocreate ... - touch a but do not create it.\n" - << " create_symlink old new - create a symbolic link new -> old\n" - << " create_hardlink old new - create a hard link new -> old\n" - << " true - do nothing with an exit code of 0\n" - << " false - do nothing with an exit code of 1\n" + "Usage: " + , program + , " -E [arguments...]\n" + , HELP_AVAILABLE_COMMANDS #if defined(_WIN32) && !defined(__CYGWIN__) - << "Available on Windows only:\n" - << " delete_regv key - delete registry value\n" - << " env_vs8_wince sdkname - displays a batch file which sets the " - "environment for the provided Windows CE SDK installed in VS2005\n" - << " env_vs9_wince sdkname - displays a batch file which sets the " - "environment for the provided Windows CE SDK installed in VS2008\n" - << " write_regv key value - write registry value\n" + , HELP_AVAILABLE_WINDOWS_COMMANDS #endif - ; + ); /* clang-format on */ - - cmSystemTools::Error(errorStream.str()); + cmSystemTools::Error(help_screen); } bool cmTarFilesFrom(std::string const& file, std::vector& files) { if (cmSystemTools::FileIsDirectory(file)) { - std::ostringstream e; - e << "-E tar --files-from= file '" << file << "' is a directory"; - cmSystemTools::Error(e.str()); + cmSystemTools::Error( + cmStrCat("-E tar --files-from= file '", file, "' is a directory")); return false; } cmsys::ifstream fin(file.c_str()); if (!fin) { - std::ostringstream e; - e << "-E tar --files-from= file '" << file << "' not found"; - cmSystemTools::Error(e.str()); + cmSystemTools::Error( + cmStrCat("-E tar --files-from= file '", file, "' not found")); return false; } std::string line; @@ -186,10 +176,8 @@ bool cmTarFilesFrom(std::string const& file, std::vector& files) if (cmHasLiteralPrefix(line, "--add-file=")) { files.push_back(line.substr(11)); } else if (cmHasLiteralPrefix(line, "-")) { - std::ostringstream e; - e << "-E tar --files-from='" << file << "' file invalid line:\n" - << line << "\n"; - cmSystemTools::Error(e.str()); + cmSystemTools::Error(cmStrCat("-E tar --files-from='", file, + "' file invalid line:\n", line, '\n')); return false; } else { files.push_back(line); @@ -252,7 +240,7 @@ private: cmSystemTools::ConvertToLongPath(path); this->DepFile << cmCMakePath(path).GenericString() << std::endl; } else { - this->Output << this->Line << std::endl << std::flush; + this->Output << this->Line << std::endl; } return true; @@ -347,14 +335,14 @@ int HandleIWYU(const std::string& runCmd, const std::string& /* sourceFile */, int ret; if (!cmSystemTools::RunSingleCommand(iwyu_cmd, nullptr, &stdErr, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { - std::cerr << "Error running '" << iwyu_cmd[0] << "': " << stdErr << "\n"; + std::cerr << "Error running '" << iwyu_cmd[0] << "': " << stdErr << '\n'; return 1; } // Warn if iwyu reported anything. if (stdErr.find("should remove these lines:") != std::string::npos || stdErr.find("should add these lines:") != std::string::npos) { std::cerr << "Warning: include-what-you-use reported diagnostics:\n" - << stdErr << "\n"; + << stdErr << '\n'; } // Older versions of iwyu always returned a non-zero exit code, // so ignore it unless the user has enabled errors. @@ -403,7 +391,7 @@ int HandleTidy(const std::string& runCmd, const std::string& sourceFile, std::string stdErr; if (!cmSystemTools::RunSingleCommand(tidy_cmd, &stdOut, &stdErr, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { - std::cerr << "Error running '" << tidy_cmd[0] << "': " << stdErr << "\n"; + std::cerr << "Error running '" << tidy_cmd[0] << "': " << stdErr << '\n'; return 1; } // Output the stdout from clang-tidy to stderr @@ -432,7 +420,7 @@ int HandleLWYU(const std::string& runCmd, const std::string& sourceFile, int ret; if (!cmSystemTools::RunSingleCommand(lwyu_cmd, &stdOut, &stdErr, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { - std::cerr << "Error running '" << lwyu_cmd[0] << "': " << stdErr << "\n"; + std::cerr << "Error running '" << lwyu_cmd[0] << "': " << stdErr << '\n'; return 1; } @@ -457,13 +445,13 @@ int HandleCppLint(const std::string& runCmd, const std::string& sourceFile, if (!cmSystemTools::RunSingleCommand(cpplint_cmd, &stdOut, &stdOut, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { std::cerr << "Error running '" << cpplint_cmd[0] << "': " << stdOut - << "\n"; + << '\n'; return 1; } if (!stdOut.empty()) { - std::cerr << "Warning: cpplint diagnostics:\n"; - // Output the output from cpplint to stderr - std::cerr << stdOut; + std::cerr << "Warning: cpplint diagnostics:\n" + // Output the output from cpplint to stderr + << stdOut; } // always return 0 so the build can continue as cpplint returns non-zero @@ -503,7 +491,7 @@ int HandleCppCheck(const std::string& runCmd, const std::string& sourceFile, if (!cmSystemTools::RunSingleCommand(cppcheck_cmd, &stdOut, &stdErr, &ret, nullptr, cmSystemTools::OUTPUT_NONE)) { std::cerr << "Error running '" << cppcheck_cmd[0] << "': " << stdOut - << "\n"; + << '\n'; return 1; } std::cerr << stdOut; @@ -593,7 +581,7 @@ int cmcmd::HandleCoCompileCommands(std::vector const& args) } else { // if it was not a co-compiler or --source/--launcher then error std::cerr << "__run_co_compile given unknown argument: " << arg - << "\n"; + << '\n'; return 1; } } @@ -605,7 +593,7 @@ int cmcmd::HandleCoCompileCommands(std::vector const& args) std::cerr << "__run_co_compile missing command to run. " "Looking for one or more of the following:\n"; for (CoCompiler const& cc : CoCompilers) { - std::cerr << cc.Option << "\n"; + std::cerr << cc.Option << '\n'; } return 1; } @@ -792,7 +780,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, } cmsys::ifstream fin(args[3].c_str(), std::ios::in | std::ios::binary); if (!fin) { - std::cerr << "could not open object list file: " << args[3] << "\n"; + std::cerr << "could not open object list file: " << args[3] << '\n'; return 1; } std::vector files; @@ -815,7 +803,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, } FILE* fout = cmsys::SystemTools::Fopen(args[2], "w+"); if (!fout) { - std::cerr << "could not open output .def file: " << args[2] << "\n"; + std::cerr << "could not open output .def file: " << args[2] << '\n'; return 1; } bindexplib deffile; @@ -824,7 +812,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, if (cmHasLiteralPrefix(a, "--nm=")) { deffile.SetNmPath(a.substr(5)); } else { - std::cerr << "unknown argument: " << a << "\n"; + std::cerr << "unknown argument: " << a << '\n'; } } for (std::string const& file : files) { @@ -1052,8 +1040,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, if (args[1] == "touch" && args.size() > 2) { for (auto const& arg : cmMakeRange(args).advance(2)) { if (!cmSystemTools::Touch(arg, true)) { - std::cerr << "cmake -E touch: failed to update \""; - std::cerr << arg << "\".\n"; + std::cerr << "cmake -E touch: failed to update \"" << arg << "\".\n"; return 1; } } @@ -1064,8 +1051,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, if (args[1] == "touch_nocreate" && args.size() > 2) { for (auto const& arg : cmMakeRange(args).advance(2)) { if (!cmSystemTools::Touch(arg, false)) { - std::cerr << "cmake -E touch_nocreate: failed to update \""; - std::cerr << arg << "\".\n"; + std::cerr << "cmake -E touch_nocreate: failed to update \"" << arg + << "\".\n"; return 1; } } @@ -1115,7 +1102,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, auto time_finish = std::chrono::steady_clock::now(); std::chrono::duration time_elapsed = time_finish - time_start; - std::cout << "Elapsed time (seconds): " << time_elapsed.count() << "\n"; + std::cout << "Elapsed time (seconds): " << time_elapsed.count() << '\n'; return ret; } @@ -1252,7 +1239,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, std::string emsg = cmSystemTools::GetLastSystemError(); std::cerr << "failed to create symbolic link '" << destinationFileName << "' because existing path cannot be removed: " << emsg - << "\n"; + << '\n'; return 1; } if (!cmSystemTools::CreateSymlink(args[2], destinationFileName)) { @@ -1269,7 +1256,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, if (!cmSystemTools::FileExists(sourceFileName)) { std::cerr << "failed to create hard link because source path '" - << sourceFileName << "' does not exist \n"; + << sourceFileName << "' does not exist\n"; return 1; } @@ -1278,7 +1265,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, std::string emsg = cmSystemTools::GetLastSystemError(); std::cerr << "failed to create hard link '" << destinationFileName << "' because existing path cannot be removed: " << emsg - << "\n"; + << '\n'; return 1; } @@ -1410,13 +1397,18 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, } #endif + // Internal CMake C++ module compilation database support. + if (args[1] == "cmake_module_compile_db") { + return cmcmd_cmake_module_compile_db(args.begin() + 2, args.end()); + } + // Internal CMake unimplemented feature notification. if (args[1] == "cmake_unimplemented_variable") { std::cerr << "Feature not implemented for this platform."; if (args.size() == 3) { std::cerr << " Variable " << args[2] << " is not set."; } - std::cerr << std::endl; + std::cerr << '\n'; return 1; } @@ -1535,7 +1527,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, action = cmSystemTools::TarActionExtract; } break; default: { - std::cerr << "tar: Unknown argument: " << flag << "\n"; + std::cerr << "tar: Unknown argument: " << flag << '\n'; } } } @@ -1558,8 +1550,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector const& args, if (files.empty()) { std::cerr << "tar: No files or directories specified\n"; } - if (!cmSystemTools::CreateTar(outFile, files, compress, verbose, mtime, - format)) { + if (!cmSystemTools::CreateTar(outFile, files, {}, compress, verbose, + mtime, format)) { cmSystemTools::Error("Problem creating tar: " + outFile); return 1; } @@ -1692,17 +1684,17 @@ int cmcmd::HashSumFile(std::vector const& args, for (auto const& filename : cmMakeRange(args).advance(2)) { // Cannot compute sum of a directory if (cmSystemTools::FileIsDirectory(filename)) { - std::cerr << "Error: " << filename << " is a directory" << std::endl; + std::cerr << "Error: " << filename << " is a directory\n"; retval++; } else { cmCryptoHash hasher(algo); std::string value = hasher.HashFile(filename); if (value.empty()) { // To mimic "md5sum/shasum" behavior in a shell: - std::cerr << filename << ": No such file or directory" << std::endl; + std::cerr << filename << ": No such file or directory\n"; retval++; } else { - std::cout << value << " " << filename << std::endl; + std::cout << value << " " << filename << '\n'; } } } @@ -1897,8 +1889,7 @@ int cmcmd::ExecuteLinkScript(std::vector const& args) // Read command lines from the script. cmsys::ifstream fin(args[2].c_str()); if (!fin) { - std::cerr << "Error opening link script \"" << args[2] << "\"" - << std::endl; + std::cerr << "Error opening link script \"" << args[2] << "\"\n"; return 1; } @@ -1929,7 +1920,7 @@ int cmcmd::ExecuteLinkScript(std::vector const& args) // Report the command if verbose output is enabled. if (verbose) { - std::cout << command << std::endl; + std::cout << command << '\n'; } // Run the command and wait for it to exit. @@ -1971,7 +1962,7 @@ int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name) "echo Environment Selection: " << name << "\n" "set PATH=" << parser.GetPathDirectories() << "\n" "set INCLUDE=" << parser.GetIncludeDirectories() << "\n" - "set LIB=" << parser.GetLibraryDirectories() << std::endl; + "set LIB=" << parser.GetLibraryDirectories() << '\n'; /* clang-format on */ return 0; } @@ -2147,6 +2138,7 @@ class cmVSLink int Type; bool Verbose; bool Incremental = false; + bool LinkEmbedsManifest = true; bool LinkGeneratesManifest = true; std::vector LinkCommand; std::vector UserManifests; @@ -2245,7 +2237,7 @@ static bool RunCommand(const char* comment, { if (verbose) { std::cout << comment << ":\n"; - std::cout << cmJoin(command, " ") << "\n"; + std::cout << cmJoin(command, " ") << '\n'; } std::string output; int retCode = 0; @@ -2301,6 +2293,12 @@ bool cmVSLink::Parse(std::vector::const_iterator argBeg, } else if (cmHasLiteralPrefix(*arg, "--mt=")) { this->MtPath = arg->substr(5); ++arg; + } else if (cmHasLiteralPrefix(*arg, "--msvc-ver=")) { + unsigned long msvc_ver = 0; + if (cmStrToULong(arg->c_str() + 11, &msvc_ver)) { + this->LinkEmbedsManifest = msvc_ver > 1600; + } + ++arg; } else { std::cerr << "unknown argument '" << *arg << "'\n"; return false; @@ -2350,11 +2348,13 @@ bool cmVSLink::Parse(std::vector::const_iterator argBeg, // pass it to the link command. this->ManifestFileRC = intDir + "/manifest.rc"; this->ManifestFileRes = intDir + "/manifest.res"; + } - if (this->LinkGeneratesManifest) { - this->LinkCommand.emplace_back("/MANIFEST"); - this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile); - } + if (this->LinkGeneratesManifest && + (this->Incremental || !this->LinkEmbedsManifest)) { + this->LinkCommand.emplace_back("/MANIFEST"); + this->LinkCommand.emplace_back("/MANIFESTFILE:" + + this->LinkerManifestFile); } return true; @@ -2415,7 +2415,7 @@ int cmVSLink::LinkIncremental() std::string absManifestFile = cmSystemTools::CollapseFullPath(this->ManifestFile); if (this->Verbose) { - std::cout << "Create " << this->ManifestFileRC << "\n"; + std::cout << "Create " << this->ManifestFileRC << '\n'; } { cmsys::ofstream fout(this->ManifestFileRC.c_str()); @@ -2423,8 +2423,8 @@ int cmVSLink::LinkIncremental() return -1; } // Insert a pragma statement to specify utf-8 encoding. - fout << "#pragma code_page(65001)\n"; - fout << this->Type + fout << "#pragma code_page(65001)\n" + << this->Type << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ " "24 /* RT_MANIFEST */ \"" << absManifestFile << "\""; @@ -2434,7 +2434,7 @@ int cmVSLink::LinkIncremental() // generate a manifest file so the resource compiler succeeds. if (!cmSystemTools::FileExists(this->ManifestFile)) { if (this->Verbose) { - std::cout << "Create empty: " << this->ManifestFile << "\n"; + std::cout << "Create empty: " << this->ManifestFile << '\n'; } if (this->UserManifests.empty()) { // generate an empty manifest because there are no user provided @@ -2488,6 +2488,24 @@ int cmVSLink::LinkIncremental() int cmVSLink::LinkNonIncremental() { + if (!this->LinkEmbedsManifest) { + // Run the link command (possibly generates intermediate manifest). + if (!RunCommand("LINK", this->LinkCommand, this->Verbose, + FORMAT_DECIMAL)) { + return -1; + } + + // If we have no manifest files we are done. + if (!this->LinkGeneratesManifest && this->UserManifests.empty()) { + return 0; + } + + // Run the manifest tool to embed the final manifest in the binary. + std::string mtOut = "/outputresource:" + this->TargetFile + + (this->Type == 1 ? ";#1" : ";#2"); + return this->RunMT(mtOut, false); + } + // The MSVC link tool expects 'rc' to be in the PATH if it needs to embed // manifests, but the user might explicitly set 'CMAKE_RC_COMPILER' instead. // Add its location as a fallback at the end of PATH. diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index f152ca4e0..6e4f0a8c3 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -292,7 +292,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) DIR* dir = opendir(name.c_str()); if (!dir) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return Status::POSIX_errno(); @@ -303,7 +303,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) this->Internal->Files.emplace_back(d->d_name); } if (errno != 0) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return Status::POSIX_errno(); @@ -321,7 +321,7 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, DIR* dir = opendir(name.c_str()); if (!dir) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return 0; @@ -332,7 +332,7 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, count++; } if (errno != 0) { - if (errorMessage != nullptr) { + if (errorMessage) { *errorMessage = std::string(strerror(errno)); } return false; diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index b25b2580d..efe22334f 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -2541,7 +2541,7 @@ static void kwsysProcessKill(pid_t process_id) /* Kill all children if we can find them. */ #if defined(__linux__) || defined(__CYGWIN__) /* First try using the /proc filesystem. */ - if ((procdir = opendir("/proc")) != NULL) { + if ((procdir = opendir("/proc"))) { # if defined(MAXPATHLEN) char fname[MAXPATHLEN]; # elif defined(PATH_MAX) diff --git a/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx index b51e16d8a..75e7442c2 100644 --- a/Source/kwsys/RegularExpression.cxx +++ b/Source/kwsys/RegularExpression.cxx @@ -48,7 +48,7 @@ RegularExpression::RegularExpression(const RegularExpression& rxp) // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != nullptr) { + if (rxp.regmust) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -81,7 +81,7 @@ RegularExpression& RegularExpression::operator=(const RegularExpression& rxp) // Copy pointers into last successful "find" operation this->regmatch = rxp.regmatch; this->regmust = rxp.regmust; // Copy field - if (rxp.regmust != nullptr) { + if (rxp.regmust) { char* dum = rxp.program; ind = 0; while (dum != rxp.regmust) { @@ -339,7 +339,7 @@ bool RegularExpression::compile(const char* exp) const char* longest; int flags; - if (exp == nullptr) { + if (!exp) { // RAISE Error, SYM(RegularExpression), SYM(No_Expr), printf("RegularExpression::compile(): No expression supplied.\n"); return false; @@ -372,7 +372,7 @@ bool RegularExpression::compile(const char* exp) this->program = new char[comp.regsize]; this->progsize = static_cast(comp.regsize); - if (this->program == nullptr) { + if (!this->program) { // RAISE Error, SYM(RegularExpression), SYM(Out_Of_Memory), printf("RegularExpression::compile(): Out of memory.\n"); return false; @@ -415,7 +415,7 @@ bool RegularExpression::compile(const char* exp) if (flags & SPSTART) { longest = nullptr; size_t len = 0; - for (; scan != nullptr; scan = regnext(scan)) + for (; scan; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); @@ -461,9 +461,9 @@ char* RegExpCompile::reg(int paren, int* flagp) // Pick up the branches, linking them together. br = regbranch(&flags); - if (br == nullptr) + if (!br) return (nullptr); - if (ret != nullptr) + if (ret) regtail(ret, br); // OPEN -> first. else ret = br; @@ -473,7 +473,7 @@ char* RegExpCompile::reg(int paren, int* flagp) while (*regparse == '|') { regparse++; br = regbranch(&flags); - if (br == nullptr) + if (!br) return (nullptr); regtail(ret, br); // BRANCH -> BRANCH. if (!(flags & HASWIDTH)) @@ -486,7 +486,7 @@ char* RegExpCompile::reg(int paren, int* flagp) regtail(ret, ender); // Hook the tails of the branches to the closing node. - for (br = ret; br != nullptr; br = regnext(br)) + for (br = ret; br; br = regnext(br)) regoptail(br, ender); // Check for proper termination. @@ -527,16 +527,16 @@ char* RegExpCompile::regbranch(int* flagp) chain = nullptr; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); - if (latest == nullptr) + if (!latest) return (nullptr); *flagp |= flags & HASWIDTH; - if (chain == nullptr) // First piece. + if (!chain) // First piece. *flagp |= flags & SPSTART; else regtail(chain, latest); chain = latest; } - if (chain == nullptr) // Loop ran zero times. + if (!chain) // Loop ran zero times. regnode(NOTHING); return (ret); @@ -559,7 +559,7 @@ char* RegExpCompile::regpiece(int* flagp) int flags; ret = regatom(&flags); - if (ret == nullptr) + if (!ret) return (nullptr); op = *regparse; @@ -678,7 +678,7 @@ char* RegExpCompile::regatom(int* flagp) } break; case '(': ret = reg(1, &flags); - if (ret == nullptr) + if (!ret) return (nullptr); *flagp |= flags & (HASWIDTH | SPSTART); break; @@ -812,7 +812,7 @@ void RegExpCompile::regtail(char* p, const char* val) scan = p; for (;;) { temp = regnext(scan); - if (temp == nullptr) + if (!temp) break; scan = temp; } @@ -831,7 +831,7 @@ void RegExpCompile::regtail(char* p, const char* val) void RegExpCompile::regoptail(char* p, const char* val) { // "Operandless" and "op != BRANCH" are synonymous in practice. - if (p == nullptr || p == regdummyptr || OP(p) != BRANCH) + if (!p || p == regdummyptr || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } @@ -881,14 +881,14 @@ bool RegularExpression::find(char const* string, } // If there is a "must appear" string, look for it. - if (this->regmust != nullptr) { + if (this->regmust) { s = string; - while ((s = strchr(s, this->regmust[0])) != nullptr) { + while ((s = strchr(s, this->regmust[0]))) { if (strncmp(s, this->regmust, this->regmlen) == 0) break; // Found it. s++; } - if (s == nullptr) // Not present. + if (!s) // Not present. return false; } @@ -906,7 +906,7 @@ bool RegularExpression::find(char const* string, s = string; if (this->regstart != '\0') // We know what char it must start with. - while ((s = strchr(s, this->regstart)) != nullptr) { + while ((s = strchr(s, this->regstart))) { if (regFind.regtry(s, rmatch.startp, rmatch.endp, this->program)) return true; s++; @@ -969,7 +969,7 @@ int RegExpFind::regmatch(const char* prog) scan = prog; - while (scan != nullptr) { + while (scan) { next = regnext(scan); @@ -1001,12 +1001,12 @@ int RegExpFind::regmatch(const char* prog) reginput += len; } break; case ANYOF: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == nullptr) + if (*reginput == '\0' || !strchr(OPERAND(scan), *reginput)) return (0); reginput++; break; case ANYBUT: - if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != nullptr) + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput)) return (0); reginput++; break; @@ -1058,7 +1058,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set startp if some later invocation of the // same parentheses already has. // - if (regstartp[no] == nullptr) + if (!regstartp[no]) regstartp[no] = save; return (1); } else @@ -1109,7 +1109,7 @@ int RegExpFind::regmatch(const char* prog) // Don't set endp if some later invocation of the // same parentheses already has. // - if (regendp[no] == nullptr) + if (!regendp[no]) regendp[no] = save; return (1); } else @@ -1129,7 +1129,7 @@ int RegExpFind::regmatch(const char* prog) return (1); reginput = save; scan = regnext(scan); - } while (scan != nullptr && OP(scan) == BRANCH); + } while (scan && OP(scan) == BRANCH); return (0); // NOTREACHED } @@ -1207,13 +1207,13 @@ int RegExpFind::regrepeat(const char* p) } break; case ANYOF: - while (*scan != '\0' && strchr(opnd, *scan) != nullptr) { + while (*scan != '\0' && strchr(opnd, *scan)) { count++; scan++; } break; case ANYBUT: - while (*scan != '\0' && strchr(opnd, *scan) == nullptr) { + while (*scan != '\0' && !strchr(opnd, *scan)) { count++; scan++; } diff --git a/Source/kwsys/RegularExpression.hxx.in b/Source/kwsys/RegularExpression.hxx.in index 1dc1dfa20..d73482b04 100644 --- a/Source/kwsys/RegularExpression.hxx.in +++ b/Source/kwsys/RegularExpression.hxx.in @@ -86,7 +86,7 @@ inline RegularExpressionMatch::RegularExpressionMatch() */ inline bool RegularExpressionMatch::isValid() const { - return (this->startp[0] != nullptr); + return (this->startp[0]); } /** @@ -140,7 +140,7 @@ inline std::string::size_type RegularExpressionMatch::end(int n) const */ inline std::string RegularExpressionMatch::match(int n) const { - if (this->startp[n] == nullptr) { + if (!this->startp[n]) { return std::string(); } else { return std::string( @@ -551,7 +551,7 @@ inline bool RegularExpression::operator!=(const RegularExpression& r) const */ inline bool RegularExpression::is_valid() const { - return (this->program != nullptr); + return (this->program); } inline void RegularExpression::set_invalid() diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index 428d64912..933d64933 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -59,7 +59,7 @@ # include # endif # if !defined(siginfo_t) -typedef int siginfo_t; +using siginfo_t = int; # endif #else # include @@ -135,7 +135,7 @@ typedef int siginfo_t; using ResourceLimitType = struct rlimit64; # define GetResourceLimit getrlimit64 # else -typedef struct rlimit ResourceLimitType; +using ResourceLimitType = struct rlimit; # define GetResourceLimit getrlimit # endif #elif defined(__hpux) @@ -333,101 +333,58 @@ public: void RunMemoryCheck(); public: - using ID = struct tagID - + struct ID { - int Type; - int Family; - int Model; - int Revision; - int ExtendedFamily; - int ExtendedModel; - std::string ProcessorName; - std::string Vendor; - std::string SerialNumber; - std::string ModelName; }; - using CPUPowerManagement = struct tagCPUPowerManagement - + struct CPUPowerManagement { - bool HasVoltageID; - bool HasFrequencyID; - bool HasTempSenseDiode; }; - using CPUExtendedFeatures = struct tagCPUExtendedFeatures - + struct CPUExtendedFeatures { - bool Has3DNow; - bool Has3DNowPlus; - bool SupportsMP; - bool HasMMXPlus; - bool HasSSEMMX; - unsigned int LogicalProcessorsPerPhysical; - int APIC_ID; - CPUPowerManagement PowerManagement; }; - using CPUFeatures = struct CPUtagFeatures - + struct CPUFeatures { - bool HasFPU; - bool HasTSC; - bool HasMMX; - bool HasSSE; - bool HasSSEFP; - bool HasSSE2; - bool HasIA64; - bool HasAPIC; - bool HasCMOV; - bool HasMTRR; - bool HasACPI; - bool HasSerial; - bool HasThermal; - int CPUSpeed; - int L1CacheSize; - int L2CacheSize; - int L3CacheSize; - CPUExtendedFeatures ExtendedFeatures; }; @@ -874,7 +831,7 @@ int LoadLines(FILE* file, std::vector& lines) char buf[bufSize] = { '\0' }; while (!feof(file) && !ferror(file)) { errno = 0; - if (fgets(buf, bufSize, file) == nullptr) { + if (!fgets(buf, bufSize, file)) { if (ferror(file) && (errno == EINTR)) { clearerr(file); } @@ -900,7 +857,7 @@ int LoadLines(FILE* file, std::vector& lines) int LoadLines(const char* fileName, std::vector& lines) { FILE* file = fopen(fileName, "r"); - if (file == nullptr) { + if (!file) { return 0; } int nRead = LoadLines(file, lines); @@ -938,7 +895,7 @@ int GetFieldsFromFile(const char* fileName, const char** fieldNames, T* values) return -1; } int i = 0; - while (fieldNames[i] != nullptr) { + while (fieldNames[i]) { int ierr = NameValue(fields, fieldNames[i], values[i]); if (ierr) { return -(i + 2); @@ -970,7 +927,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, T* values) { FILE* file = popen(command, "r"); - if (file == nullptr) { + if (!file) { return -1; } std::vector fields; @@ -980,7 +937,7 @@ int GetFieldsFromCommand(const char* command, const char** fieldNames, return -1; } int i = 0; - while (fieldNames[i] != nullptr) { + while (fieldNames[i]) { int ierr = NameValue(fields, fieldNames[i], values[i]); if (ierr) { return -(i + 2); @@ -1016,7 +973,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGFPE: - oss << "Caught SIGFPE at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGFPE at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { # if defined(FPE_INTDIV) @@ -1064,7 +1021,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGSEGV: - oss << "Caught SIGSEGV at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGSEGV at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case SEGV_MAPERR: @@ -1082,7 +1039,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGBUS: - oss << "Caught SIGBUS at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGBUS at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case BUS_ADRALN: @@ -1122,7 +1079,7 @@ void StacktraceSignalHandler(int sigNo, siginfo_t* sigInfo, break; case SIGILL: - oss << "Caught SIGILL at " << (sigInfo->si_addr == nullptr ? "0x" : "") + oss << "Caught SIGILL at " << (sigInfo->si_addr ? "" : "0x") << sigInfo->si_addr << " "; switch (sigInfo->si_code) { case ILL_ILLOPC: @@ -1659,7 +1616,7 @@ int SystemInformationImplementation::GetFullyQualifiedDomainName( return -2; } - for (ifa = ifas; ifa != nullptr; ifa = ifa->ifa_next) { + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { int fam = ifa->ifa_addr ? ifa->ifa_addr->sa_family : -1; // Skip Loopback interfaces if (((fam == AF_INET) || (fam == AF_INET6)) && @@ -3448,8 +3405,9 @@ bool SystemInformationImplementation::RetrieveInformationFromCpuInfoFile() } size_t fileSize = 0; - while (!feof(fd)) { - buffer += static_cast(fgetc(fd)); + int fc; + while ((fc = fgetc(fd)) != EOF) { + buffer += static_cast(fc); fileSize++; } fclose(fd); @@ -3886,7 +3844,7 @@ long long SystemInformationImplementation::GetProcMemoryUsed() std::ostringstream oss; oss << "ps -o rss= -p " << pid; FILE* file = popen(oss.str().c_str(), "r"); - if (file == nullptr) { + if (!file) { return -1; } oss.str(""); @@ -3923,8 +3881,7 @@ double SystemInformationImplementation::GetLoadAverage() return -0.0; #elif defined(KWSYS_SYSTEMINFORMATION_USE_GetSystemTimes) // Old windows.h headers do not provide GetSystemTimes. - typedef BOOL(WINAPI * GetSystemTimesType)(LPFILETIME, LPFILETIME, - LPFILETIME); + using GetSystemTimesType = BOOL(WINAPI*)(LPFILETIME, LPFILETIME, LPFILETIME); static GetSystemTimesType pGetSystemTimes = (GetSystemTimesType)GetProcAddress(GetModuleHandleW(L"kernel32"), "GetSystemTimes"); @@ -4469,8 +4426,8 @@ void SystemInformationImplementation::CPUCountWindows() this->NumberOfPhysicalCPU = 0; this->NumberOfLogicalCPU = 0; - typedef BOOL(WINAPI * GetLogicalProcessorInformationType)( - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + using GetLogicalProcessorInformationType = + BOOL(WINAPI*)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); static GetLogicalProcessorInformationType pGetLogicalProcessorInformation = reinterpret_cast(GetProcAddress( GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation")); @@ -4499,10 +4456,7 @@ void SystemInformationImplementation::CPUCountWindows() (void)rc; // Silence unused variable warning } - typedef std::vector::iterator - pinfoIt_t; - for (pinfoIt_t it = ProcInfo.begin(); it != ProcInfo.end(); ++it) { - SYSTEM_LOGICAL_PROCESSOR_INFORMATION PInfo = *it; + for (SYSTEM_LOGICAL_PROCESSOR_INFORMATION const& PInfo : ProcInfo) { if (PInfo.Relationship != RelationProcessorCore) { continue; } @@ -5464,7 +5418,7 @@ bool SystemInformationImplementation::QueryOSInformation() this->OSVersion = operatingSystem; } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { // Windows XP and .NET server. - typedef BOOL(CALLBACK * LPFNPROC)(HANDLE, BOOL*); + using LPFNPROC = BOOL(CALLBACK*)(HANDLE, BOOL*); HINSTANCE hKernelDLL; LPFNPROC DLLProc; diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 63566160f..6cc103df5 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -268,7 +268,7 @@ static inline char* realpath(const char* path, char* resolved_path) snprintf(resolved_path, maxlen, "%s", path); BPath normalized(resolved_path, nullptr, true); const char* resolved = normalized.Path(); - if (resolved != nullptr) // nullptr == No such file. + if (resolved) // nullptr == No such file. { if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) { return resolved_path; @@ -798,7 +798,7 @@ bool SystemTools::HasEnv(const char* key) #else const char* v = getenv(key); #endif - return v != nullptr; + return v; } bool SystemTools::HasEnv(const std::string& key) @@ -1167,7 +1167,7 @@ static DWORD SystemToolsMakeRegistryMode(DWORD mode, // only add the modes when on a system that supports Wow64. static FARPROC wow64p = GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"); - if (wow64p == nullptr) { + if (!wow64p) { return mode; } @@ -2597,8 +2597,12 @@ SystemTools::CopyStatus SystemTools::CloneFileContent( // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to // be updated by `copy_file_if_different` and `copy_file`. + // These flags are meant to be COPYFILE_METADATA | COPYFILE_CLONE, but CLONE + // forces COPYFILE_NOFOLLOW_SRC and that violates the invariant that this + // should result in a file. if (copyfile(source.c_str(), destination.c_str(), nullptr, - COPYFILE_METADATA | COPYFILE_CLONE) < 0) { + COPYFILE_METADATA | COPYFILE_EXCL | COPYFILE_STAT | + COPYFILE_XATTR | COPYFILE_DATA) < 0) { return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath }; } # if KWSYS_CXX_HAS_UTIMENSAT @@ -3394,7 +3398,7 @@ Status SystemTools::ReadSymlink(std::string const& newName, // terminated by an empty string (0-0). We need the third string. size_t destLen; substituteNameData = GetAppExecLink(data, destLen); - if (substituteNameData == nullptr || destLen == 0) { + if (!substituteNameData || destLen == 0) { return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED); } substituteNameLength = static_cast(destLen); diff --git a/Templates/CTestScript.cmake.in b/Templates/CTestScript.cmake.in deleted file mode 100644 index 5fb3529e4..000000000 --- a/Templates/CTestScript.cmake.in +++ /dev/null @@ -1,33 +0,0 @@ -cmake_minimum_required(VERSION 2.4) - -# This is a template for the CTest script for this system - -set(CTEST_SITE "@SITE@") -set(CTEST_BUILD_NAME "@BUILDNAME@") - -# --- -set(CTEST_SOURCE_DIRECTORY "@CMAKE_SOURCE_DIR@") -set(CTEST_BINARY_DIRECTORY "@CMAKE_BINARY_DIR@") -set(CTEST_UPDATE_COMMAND "@UPDATE_COMMAND@") -set(CTEST_UPDATE_OPTIONS "@UPDATE_OPTIONS@") -set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") -set(CTEST_BUILD_CONFIGURATION "Release") -#set(CTEST_MEMORYCHECK_COMMAND "@MEMORYCHECK_COMMAND@") -#set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE "@MEMORYCHECK_SUPPRESSIONS_FILE@") -#set(CTEST_MEMORYCHECK_COMMAND_OPTIONS "@MEMORYCHECK_COMMAND_OPTIONS@") -#set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") -set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") - -#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY}) - -set(CTEST_DROP_METHOD "@DROP_METHOD@") - -CTEST_START(Experimental TRACK Weekly) -CTEST_UPDATE(SOURCE "${CTEST_SOURCE_DIRECTORY}") -CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}") -CTEST_READ_CUSTOM_FILES("${CTEST_BINARY_DIRECTORY}") -CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}") -CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}") -#CTEST_MEMCHECK(BUILD "${CTEST_BINARY_DIRECTORY}") -#CTEST_COVERAGE(BUILD "${CTEST_BINARY_DIRECTORY}") -CTEST_SUBMIT() diff --git a/Templates/MSBuild/FlagTables/v142_CL.json b/Templates/MSBuild/FlagTables/v142_CL.json index 1d8a70695..0566460bb 100644 --- a/Templates/MSBuild/FlagTables/v142_CL.json +++ b/Templates/MSBuild/FlagTables/v142_CL.json @@ -1090,7 +1090,7 @@ { "name": "GenerateModuleDependencies", "switch": "sourceDependencies:directives", - "comment": "Generate Module Dependenices File", + "comment": "Generate Module Dependencies File", "value": "true", "flags": [ "Continue" diff --git a/Templates/MSBuild/FlagTables/v143_CL.json b/Templates/MSBuild/FlagTables/v143_CL.json index 9d6e2b020..c5f94725e 100644 --- a/Templates/MSBuild/FlagTables/v143_CL.json +++ b/Templates/MSBuild/FlagTables/v143_CL.json @@ -1092,7 +1092,7 @@ { "name": "GenerateModuleDependencies", "switch": "sourceDependencies:directives", - "comment": "Generate Module Dependenices File", + "comment": "Generate Module Dependencies File", "value": "true", "flags": [ "Continue" diff --git a/Tests/AliasTarget/CMakeLists.txt b/Tests/AliasTarget/CMakeLists.txt index aa4abc458..ec5ebeb97 100644 --- a/Tests/AliasTarget/CMakeLists.txt +++ b/Tests/AliasTarget/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 2.8.11) -cmake_policy(SET CMP0054 NEW) +cmake_minimum_required(VERSION 3.10) project(AliasTarget) add_library(foo SHARED empty.cpp) diff --git a/Tests/Architecture/CMakeLists.txt b/Tests/Architecture/CMakeLists.txt index 8b3d0af6e..e4741432f 100644 --- a/Tests/Architecture/CMakeLists.txt +++ b/Tests/Architecture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Architecture C) if (CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12) diff --git a/Tests/ArgumentExpansion/CMakeLists.txt b/Tests/ArgumentExpansion/CMakeLists.txt index 9ab87b2cd..c3a8d7e8e 100644 --- a/Tests/ArgumentExpansion/CMakeLists.txt +++ b/Tests/ArgumentExpansion/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ArgumentExpansion) diff --git a/Tests/Assembler/CMakeLists.txt b/Tests/Assembler/CMakeLists.txt index 56de25734..d135547b7 100644 --- a/Tests/Assembler/CMakeLists.txt +++ b/Tests/Assembler/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.8) +cmake_minimum_required (VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt index 2fa2bc5c0..7f9e41e97 100644 --- a/Tests/BuildDepends/CMakeLists.txt +++ b/Tests/BuildDepends/CMakeLists.txt @@ -4,7 +4,7 @@ # are working the executable should relink with the new # value. The subdir Project contains the CMakelists.txt # and source files for the test project. -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.10) project(BuildDepends) # This entire test takes place during the initial diff --git a/Tests/BuildDepends/Project/CMakeLists.txt b/Tests/BuildDepends/Project/CMakeLists.txt index 2abda2ec3..cf07819c2 100644 --- a/Tests/BuildDepends/Project/CMakeLists.txt +++ b/Tests/BuildDepends/Project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(testRebuild) if(APPLE AND CMake_TEST_XCODE_VERSION) diff --git a/Tests/BuildDepends/Project/External/CMakeLists.txt b/Tests/BuildDepends/Project/External/CMakeLists.txt index c6015b651..fc998057e 100644 --- a/Tests/BuildDepends/Project/External/CMakeLists.txt +++ b/Tests/BuildDepends/Project/External/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(BuildDependsExternal NONE) if(NOT DEFINED external_in) message(FATAL_ERROR "Define external_in") diff --git a/Tests/BundleGeneratorTest/CMakeLists.txt b/Tests/BundleGeneratorTest/CMakeLists.txt index 069fb7746..37afecaa7 100644 --- a/Tests/BundleGeneratorTest/CMakeLists.txt +++ b/Tests/BundleGeneratorTest/CMakeLists.txt @@ -1,7 +1,6 @@ +cmake_minimum_required(VERSION 3.10) project(BundleGeneratorTest) -cmake_minimum_required(VERSION 3.5) - # Build a shared library and install it in lib/ add_library(Library SHARED Library.cxx) install(TARGETS Library DESTINATION lib) diff --git a/Tests/BundleTest/CMakeLists.txt b/Tests/BundleTest/CMakeLists.txt index c63461a2c..feb764b1d 100644 --- a/Tests/BundleTest/CMakeLists.txt +++ b/Tests/BundleTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.10) project(BundleTest) set(MACOSX_BUNDLE_INFO_STRING "bundle_info_string") set(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different ") diff --git a/Tests/BundleUtilities/CMakeLists.txt b/Tests/BundleUtilities/CMakeLists.txt index b53d499ec..e073695fd 100644 --- a/Tests/BundleUtilities/CMakeLists.txt +++ b/Tests/BundleUtilities/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(BundleUtilities) if(CMAKE_GENERATOR STREQUAL "Xcode" AND diff --git a/Tests/CFBundleTest/CMakeLists.txt b/Tests/CFBundleTest/CMakeLists.txt index 40dd88753..0294d6644 100644 --- a/Tests/CFBundleTest/CMakeLists.txt +++ b/Tests/CFBundleTest/CMakeLists.txt @@ -1,9 +1,8 @@ -#this is adapted from FireBreath (http://www.firebreath.org) - -cmake_minimum_required(VERSION 3.5) - +cmake_minimum_required(VERSION 3.10) project(CFBundleTest) +#this is adapted from FireBreath (http://www.firebreath.org) + include(PluginConfig.cmake) message ("Creating Mac Browser Plugin project ${PROJECT_NAME}") diff --git a/Tests/CMakeCommands/add_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/add_compile_definitions/CMakeLists.txt index 2eb887e00..32aa2de03 100644 --- a/Tests/CMakeCommands/add_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/add_compile_definitions/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(add_compile_definitions LANGUAGES CXX) diff --git a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt index 96f553a03..c0f8e5256 100644 --- a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index 0c1af9e73..9bdeff25e 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -1,5 +1,4 @@ - -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(target_compile_definitions) diff --git a/Tests/CMakeCommands/target_compile_features/CMakeLists.txt b/Tests/CMakeCommands/target_compile_features/CMakeLists.txt index 9664025a4..937e5305f 100644 --- a/Tests/CMakeCommands/target_compile_features/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_features/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(target_compile_features) set(CMAKE_VERBOSE_MAKEFILE ON) diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt index f20eb5dcf..4a7f78009 100644 --- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt @@ -1,5 +1,4 @@ - -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt index d5d4970ca..1043c412a 100644 --- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(target_include_directories) diff --git a/Tests/CMakeCommands/target_link_directories/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt index a5f69f30f..6211466f0 100644 --- a/Tests/CMakeCommands/target_link_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt @@ -1,4 +1,3 @@ - cmake_minimum_required(VERSION 3.12) project(target_link_directories LANGUAGES C) diff --git a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt index 8231a5cd9..9eeb532a1 100644 --- a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt +++ b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt @@ -1,7 +1,7 @@ # Using 2.8 will trigger a deprecation warning. In this case it's explicitly # intentional since the tests checks various policy implementations prior to -# 3.5 -cmake_minimum_required(VERSION 2.8) +# 3.10 +cmake_minimum_required(VERSION 2.8.10) # old enough to not set CMP0022 if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 4d43e0828..5b189e7e3 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -1,3 +1,6 @@ +# Render the header file with source path macro +configure_file(testConfig.h.in testConfig.h @ONLY) + include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMake_BINARY_DIR}/Source @@ -6,6 +9,7 @@ include_directories( ) set(CMakeLib_TESTS + testAssert.cxx testArgumentParser.cxx testCTestBinPacker.cxx testCTestResourceAllocator.cxx @@ -65,10 +69,8 @@ if(WIN32) list(APPEND CMakeLib_TESTS testVisualStudioSlnParser.cxx ) - configure_file(testVisualStudioSlnParser.h.in testVisualStudioSlnParser.h @ONLY) endif() -configure_file(testXMLParser.h.in testXMLParser.h @ONLY) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testUVProcessChainInput.txt" "HELLO WORLD!") create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS}) diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx index 2647fefee..d4e2bd2b5 100644 --- a/Tests/CMakeLib/testArgumentParser.cxx +++ b/Tests/CMakeLib/testArgumentParser.cxx @@ -1,9 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include -#include -#include #include #include #include @@ -16,6 +13,8 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "testCommon.h" + namespace { struct Result : public ArgumentParser::ParseResult @@ -97,6 +96,10 @@ struct Result : public ArgumentParser::ParseResult std::vector ParsedKeywords; }; +struct Derived : Result +{ +}; + std::initializer_list const args = { /* clang-format off */ "pos0", // position index 0 @@ -178,14 +181,6 @@ bool verifyResult(Result const& result, static std::vector const unparsed = { "pos2", "bar", "ign1", "ign2", "ign4" }; -#define ASSERT_TRUE(x) \ - do { \ - if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ - return false; \ - } \ - } while (false) - ASSERT_TRUE(!result); ASSERT_TRUE(result.Option1); @@ -312,41 +307,44 @@ static auto const parserStaticFunc4 = cm::string_view arg) -> ArgumentParser::Continue { return result.Func4(key, arg); }; -static auto const parserStatic = // - cmArgumentParser{} - .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 */; + +#define BIND_ALL(name, resultType) \ + static auto const name = \ + cmArgumentParser{} \ + .Bind(0, &resultType::Pos0) \ + .Bind(1, &resultType::Pos1) \ + .Bind(2, &resultType::Pos2) \ + .Bind("OPTION_1"_s, &resultType::Option1) \ + .Bind("OPTION_2"_s, &resultType::Option2) \ + .Bind("STRING_1"_s, &resultType::String1) \ + .Bind("STRING_2"_s, &resultType::String2) \ + .Bind("STRING_3"_s, &resultType::String3) \ + .Bind("STRING_4"_s, &resultType::String4) \ + .Bind("STRING_5"_s, &resultType::String5) \ + .Bind("STRING_6"_s, &resultType::String6) \ + .Bind("LIST_1"_s, &resultType::List1) \ + .Bind("LIST_2"_s, &resultType::List2) \ + .Bind("LIST_3"_s, &resultType::List3) \ + .Bind("LIST_4"_s, &resultType::List4) \ + .Bind("LIST_5"_s, &resultType::List5) \ + .Bind("LIST_6"_s, &resultType::List6) \ + .Bind("MULTI_1"_s, &resultType::Multi1) \ + .Bind("MULTI_2"_s, &resultType::Multi2) \ + .Bind("MULTI_3"_s, &resultType::Multi3) \ + .Bind("MULTI_4"_s, &resultType::Multi4) \ + .Bind("FUNC_0"_s, &resultType::Func0) \ + .Bind("FUNC_1"_s, &resultType::Func1) \ + .Bind("FUNC_2a"_s, &resultType::Func2) \ + .Bind("FUNC_2b"_s, &resultType::Func2) \ + .Bind("FUNC_3"_s, \ + [](resultType& result, cm::string_view arg) \ + -> ArgumentParser::Continue { return result.Func3(arg); }) \ + .Bind("FUNC_4a"_s, parserStaticFunc4) \ + .Bind("FUNC_4b"_s, parserStaticFunc4) \ + .BindParsedKeywords(&resultType::ParsedKeywords) + +BIND_ALL(parserStatic, Result); +BIND_ALL(parserDerivedStatic, Derived); bool testArgumentParserStatic() { @@ -355,6 +353,13 @@ bool testArgumentParserStatic() return verifyResult(result, unparsedArguments); } +bool testArgumentParserDerivedStatic() +{ + std::vector unparsedArguments; + Derived const result = parserDerivedStatic.Parse(args, &unparsedArguments); + return verifyResult(result, unparsedArguments); +} + bool testArgumentParserStaticBool() { std::vector unparsedArguments; @@ -377,6 +382,11 @@ int testArgumentParser(int /*unused*/, char* /*unused*/[]) return -1; } + if (!testArgumentParserDerivedStatic()) { + std::cout << "While executing testArgumentParserDerivedStatic().\n"; + return -1; + } + if (!testArgumentParserStaticBool()) { std::cout << "While executing testArgumentParserStaticBool().\n"; return -1; diff --git a/Tests/CMakeLib/testAssert.cxx b/Tests/CMakeLib/testAssert.cxx new file mode 100644 index 000000000..3d2e839f1 --- /dev/null +++ b/Tests/CMakeLib/testAssert.cxx @@ -0,0 +1,56 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include + +#include "testCommon.h" + +namespace { + +class WrapFailureInBlockFixture +{ +public: + WrapFailureInBlockFixture() + { + std::cout << "---[ BEGIN Expected Failure Output]---\n"; + } + ~WrapFailureInBlockFixture() + { + std::cout << "---[ END Expected Failure Output]---\n"; + } +}; + +bool testASSERT_EQUAL() +{ + ASSERT_EQUAL(7 == 7, 42 == 42); + { + std::string actual = "Hello Africa!"; + ASSERT_EQUAL(actual, "Hello Africa!"); + } + return true; +} + +bool testASSERT_EQUALFail() +{ + WrapFailureInBlockFixture fx; + static_cast(fx); + + auto fail_int = [](const int unexpected) -> bool { + ASSERT_EQUAL(unexpected, 42); + return true; + }; + + auto fail_string = [](const std::string& unexpected) -> bool { + ASSERT_EQUAL(unexpected, "Hello Africa!"); + return true; + }; + + return !(fail_int(7) || fail_string("Habari Afrika!")); +} + +} // anonymous namespace + +int testAssert(int /*unused*/, char* /*unused*/[]) +{ + return runTests({ testASSERT_EQUAL, testASSERT_EQUALFail }); +} diff --git a/Tests/CMakeLib/testCMFilesystemPath.cxx b/Tests/CMakeLib/testCMFilesystemPath.cxx index 52cb43a9a..66d2c1872 100644 --- a/Tests/CMakeLib/testCMFilesystemPath.cxx +++ b/Tests/CMakeLib/testCMFilesystemPath.cxx @@ -2,13 +2,14 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include -#include #include #include #include #include +#include "testCommon.h" + namespace { namespace fs = cm::filesystem; @@ -971,38 +972,9 @@ bool testNonMemberFunctions() int testCMFilesystemPath(int /*unused*/, char* /*unused*/[]) { - int result = 0; - - if (!testConstructors()) { - result = 1; - } - if (!testConcatenation()) { - result = 1; - } - if (!testModifiers()) { - result = 1; - } - if (!testObservers()) { - result = 1; - } - if (!testCompare()) { - result = 1; - } - if (!testGeneration()) { - result = 1; - } - if (!testDecomposition()) { - result = 1; - } - if (!testQueries()) { - result = 1; - } - if (!testIterators()) { - result = 1; - } - if (!testNonMemberFunctions()) { - result = 1; - } - - return result; + return runTests({ testConstructors, testConcatenation, testModifiers, + testObservers, testCompare, testGeneration, + testDecomposition, testQueries, testIterators, + testNonMemberFunctions }, + false); } diff --git a/Tests/CMakeLib/testCMakePath.cxx b/Tests/CMakeLib/testCMakePath.cxx index aa17e5043..d2f6661a4 100644 --- a/Tests/CMakeLib/testCMakePath.cxx +++ b/Tests/CMakeLib/testCMakePath.cxx @@ -3,7 +3,6 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include #include #include @@ -12,6 +11,8 @@ #include "cmCMakePath.h" +#include "testCommon.h" + namespace { void checkResult(bool success) @@ -422,20 +423,6 @@ bool testAppend() int testCMakePath(int /*unused*/, char* /*unused*/[]) { - int result = 0; - - if (!testConstructors()) { - result = 1; - } - if (!testAssign()) { - result = 1; - } - if (!testConcat()) { - result = 1; - } - if (!testAppend()) { - result = 1; - } - - return result; + return runTests({ testConstructors, testAssign, testConcat, testAppend }, + false); } diff --git a/Tests/CMakeLib/testCommon.h b/Tests/CMakeLib/testCommon.h index bd2d54e6d..de4a6895f 100644 --- a/Tests/CMakeLib/testCommon.h +++ b/Tests/CMakeLib/testCommon.h @@ -2,29 +2,50 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once -#include -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #define ASSERT_TRUE(x) \ do { \ if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ + std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << '\n'; \ return false; \ } \ } while (false) -inline int runTests(std::vector> const& tests) +#define ASSERT_EQUAL(actual, expected) \ + do { \ + if (!((actual) == (expected))) { \ + std::cout << "ASSERT_EQUAL(" #actual ", " #expected ") failed on line " \ + << __LINE__ << '\n'; \ + std::cout << " Actual: '" << (actual) << "'\n"; \ + std::cout << "Expected: '" << (expected) << "'\n"; \ + return false; \ + } \ + } while (false) + +#define BOOL_STRING(b) ((b) ? "TRUE" : "FALSE") + +namespace { + +inline int runTests(std::initializer_list> const& tests, + const bool fail_fast = true) { + int result = 0; for (auto const& test : tests) { if (!test()) { - return 1; + result = 1; + if (fail_fast) { + break; + } } - std::cout << "."; + std::cout << '.'; } - - std::cout << " Passed" << std::endl; - return 0; + if (!result) { + std::cout << " Passed\n"; + } + return result; } -#define BOOL_STRING(b) ((b) ? "TRUE" : "FALSE") +} diff --git a/Tests/CMakeLib/testConfig.h.in b/Tests/CMakeLib/testConfig.h.in new file mode 100644 index 000000000..f57f97e84 --- /dev/null +++ b/Tests/CMakeLib/testConfig.h.in @@ -0,0 +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 + +#define SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@" +#define BUILD_DIR "@CMAKE_CURRENT_BINARY_DIR@" diff --git a/Tests/CMakeLib/testDebuggerAdapter.cxx b/Tests/CMakeLib/testDebuggerAdapter.cxx index e66d9904a..a055cb7c6 100644 --- a/Tests/CMakeLib/testDebuggerAdapter.cxx +++ b/Tests/CMakeLib/testDebuggerAdapter.cxx @@ -3,11 +3,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -194,8 +192,6 @@ bool testThreadsRequestAfterThreadExitedEvent() int testDebuggerAdapter(int, char*[]) { - return runTests(std::vector>{ - testBasicProtocol, - testThreadsRequestAfterThreadExitedEvent, - }); + return runTests( + { testBasicProtocol, testThreadsRequestAfterThreadExitedEvent }); } diff --git a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx index c0f2e9bb7..364708855 100644 --- a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx +++ b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx @@ -3,13 +3,10 @@ #include #include -#include #include -#include #include #include #include -#include #include #include @@ -180,7 +177,5 @@ bool testProtocolWithPipes() int testDebuggerAdapterPipe(int, char*[]) { - return runTests(std::vector>{ - testProtocolWithPipes, - }); + return runTests({ testProtocolWithPipes }); } diff --git a/Tests/CMakeLib/testDebuggerBreakpointManager.cxx b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx index f654442aa..39738268a 100644 --- a/Tests/CMakeLib/testDebuggerBreakpointManager.cxx +++ b/Tests/CMakeLib/testDebuggerBreakpointManager.cxx @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -177,9 +176,7 @@ static bool testSourceFileLoadedAfterHandleBreakpointRequest() int testDebuggerBreakpointManager(int, char*[]) { - return runTests(std::vector>{ - testHandleBreakpointRequestBeforeFileIsLoaded, - testHandleBreakpointRequestAfterFileIsLoaded, - testSourceFileLoadedAfterHandleBreakpointRequest, - }); + return runTests({ testHandleBreakpointRequestBeforeFileIsLoaded, + testHandleBreakpointRequestAfterFileIsLoaded, + testSourceFileLoadedAfterHandleBreakpointRequest }); } diff --git a/Tests/CMakeLib/testDebuggerThread.cxx b/Tests/CMakeLib/testDebuggerThread.cxx index 0ea95b62d..3b7fe6e2f 100644 --- a/Tests/CMakeLib/testDebuggerThread.cxx +++ b/Tests/CMakeLib/testDebuggerThread.cxx @@ -1,4 +1,3 @@ -#include #include #include #include @@ -27,7 +26,5 @@ static bool testStackFrameFunctionName() int testDebuggerThread(int, char*[]) { - return runTests(std::vector>{ - testStackFrameFunctionName, - }); + return runTests({ testStackFrameFunctionName }); } diff --git a/Tests/CMakeLib/testDebuggerVariables.cxx b/Tests/CMakeLib/testDebuggerVariables.cxx index bb3f14d73..ca8bf39e1 100644 --- a/Tests/CMakeLib/testDebuggerVariables.cxx +++ b/Tests/CMakeLib/testDebuggerVariables.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include -#include #include #include #include @@ -204,11 +203,7 @@ static bool testNoSupportsVariableType() int testDebuggerVariables(int, char*[]) { - return runTests(std::vector>{ - testUniqueIds, - testConstructors, - testIgnoreEmptyStringEntries, - testSortTheResult, - testNoSupportsVariableType, - }); + return runTests({ testUniqueIds, testConstructors, + testIgnoreEmptyStringEntries, testSortTheResult, + testNoSupportsVariableType }); } diff --git a/Tests/CMakeLib/testDebuggerVariablesHelper.cxx b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx index d61b73b32..5fe1c634f 100644 --- a/Tests/CMakeLib/testDebuggerVariablesHelper.cxx +++ b/Tests/CMakeLib/testDebuggerVariablesHelper.cxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include #include #include #include @@ -571,18 +570,10 @@ static bool testCreateFromFileSets() int testDebuggerVariablesHelper(int, char*[]) { - return runTests(std::vector>{ - testCreateFromPolicyMap, - testCreateFromPairVector, - testCreateFromSet, - testCreateFromStringVector, - testCreateFromTarget, - testCreateFromGlobalGenerator, - testCreateFromMakefile, - testCreateFromStackFrame, - testCreateFromTests, - testCreateFromBTStringVector, - testCreateFromFileSet, - testCreateFromFileSets, - }); + return runTests({ testCreateFromPolicyMap, testCreateFromPairVector, + testCreateFromSet, testCreateFromStringVector, + testCreateFromTarget, testCreateFromGlobalGenerator, + testCreateFromMakefile, testCreateFromStackFrame, + testCreateFromTests, testCreateFromBTStringVector, + testCreateFromFileSet, testCreateFromFileSets }); } diff --git a/Tests/CMakeLib/testDebuggerVariablesManager.cxx b/Tests/CMakeLib/testDebuggerVariablesManager.cxx index 3013b9fea..a1881c076 100644 --- a/Tests/CMakeLib/testDebuggerVariablesManager.cxx +++ b/Tests/CMakeLib/testDebuggerVariablesManager.cxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include #include #include @@ -44,7 +43,5 @@ static bool testVariablesRegistration() int testDebuggerVariablesManager(int, char*[]) { - return runTests(std::vector>{ - testVariablesRegistration, - }); + return runTests({ testVariablesRegistration }); } diff --git a/Tests/CMakeLib/testJSONHelpers.cxx b/Tests/CMakeLib/testJSONHelpers.cxx index 50f038656..7de58bc3a 100644 --- a/Tests/CMakeLib/testJSONHelpers.cxx +++ b/Tests/CMakeLib/testJSONHelpers.cxx @@ -1,7 +1,8 @@ -#include -#include +#include #include #include +#include +#include #include #include @@ -12,13 +13,7 @@ #include "cmJSONHelpers.h" #include "cmJSONState.h" -#define ASSERT_TRUE(x) \ - do { \ - if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ - return false; \ - } \ - } while (false) +#include "testCommon.h" namespace { struct ObjectStruct @@ -44,6 +39,8 @@ ErrorGenerator ErrorGeneratorBuilder(std::string errorMessage) ErrorGenerator InvalidArray = ErrorGeneratorBuilder("Invalid Array"); ErrorGenerator MissingRequired = ErrorGeneratorBuilder("Missing Required"); ErrorGenerator InvalidMap = ErrorGeneratorBuilder("Invalid Map"); +ErrorGenerator FaultyObjectProperty = + ErrorGeneratorBuilder("Faulty Object Property"); ErrorGenerator InvalidObject(JsonErrors::ObjectError /*errorType*/, const Json::Value::Members& extraFields) { @@ -468,51 +465,72 @@ bool testRequired() return true; } -} -int testJSONHelpers(int /*unused*/, char* /*unused*/[]) +JSONHelperBuilder::FilterResult ObjectPropsFilter(const std::string& key, + const Json::Value* value, + cmJSONState* state) { - if (!testInt()) { - return 1; - } - if (!testUInt()) { - return 1; - } - if (!testBool()) { - return 1; - } - if (!testString()) { - return 1; - } - if (!testObject()) { - return 1; - } - if (!testObjectInherited()) { - return 1; - } - if (!testObjectNoExtra()) { - return 1; + if (key == "ignore") { + return JSONHelperBuilder::FilterResult::Skip; } - if (!testObjectOptional()) { - return 1; + if (value->isString() && value->asString() == "ignore") { + return JSONHelperBuilder::FilterResult::Skip; } - if (!testVector()) { - return 1; + if (key == "zerror") { + ErrorMessages::FaultyObjectProperty(value, state); + return JSONHelperBuilder::FilterResult::Error; } - if (!testVectorFilter()) { - return 1; - } - if (!testMap()) { - return 1; - } - if (!testMapFilter()) { - return 1; - } - if (!testOptional()) { - return 1; - } - if (!testRequired()) { - return 1; - } - return 0; + return JSONHelperBuilder::FilterResult::Continue; +} + +template +bool testFilteredObject() +{ + auto const FilteredObjectHelper = + JSONHelperBuilder::FilteredObject( + ErrorMessages::InvalidMap, StringHelper, ObjectPropsFilter); + + Json::Value v(Json::objectValue); + cmJSONState state; + v["field1"] = "Hello"; + v["field2"] = "world!"; + v["ignore"] = "any"; + v["any"] = "ignore"; + v["zerror"] = "error"; + + Container m{ { "key", "default" } }; + Container expected{ { "field1", "Hello" }, { "field2", "world!" } }; + + ASSERT_TRUE(!FilteredObjectHelper(m, &v, &state)); + ASSERT_TRUE(m == expected); + + auto error = state.GetErrorMessage(); + ASSERT_TRUE(error == "\nFaulty Object Property"); + + return true; +} +} + +int testJSONHelpers(int /*unused*/, char* /*unused*/[]) +{ + return runTests({ + testInt, + testUInt, + testBool, + testString, + testObject, + testObjectInherited, + testObjectNoExtra, + testObjectOptional, + testVector, + testVectorFilter, + testMap, + testMapFilter, + testOptional, + testRequired, + testFilteredObject>, + testFilteredObject>, + testFilteredObject>>, + testFilteredObject>>, + }); } diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx index 8822806c1..2864c9142 100644 --- a/Tests/CMakeLib/testList.cxx +++ b/Tests/CMakeLib/testList.cxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include #include #include #include @@ -11,6 +10,8 @@ #include "cmList.h" +#include "testCommon.h" + namespace { void checkResult(bool success) @@ -951,44 +952,9 @@ bool testStaticModifiers() int testList(int /*unused*/, char* /*unused*/[]) { - int result = 0; - - if (!testConstructors()) { - result = 1; - } - if (!testAssign()) { - result = 1; - } - if (!testConversions()) { - result = 1; - } - if (!testAccess()) { - result = 1; - } - if (!testModifiers()) { - result = 1; - } - if (!testRemoveItems()) { - result = 1; - } - if (!testRemoveDuplicates()) { - result = 1; - } - if (!testFilter()) { - result = 1; - } - if (!testReverse()) { - result = 1; - } - if (!testSort()) { - result = 1; - } - if (!testTransform()) { - result = 1; - } - if (!testStaticModifiers()) { - result = 1; - } - - return result; + return runTests({ testConstructors, testAssign, testConversions, testAccess, + testModifiers, testRemoveItems, testRemoveDuplicates, + testFilter, testReverse, testSort, testTransform, + testStaticModifiers }, + false); } diff --git a/Tests/CMakeLib/testOptional.cxx b/Tests/CMakeLib/testOptional.cxx index 933ab7050..0276ed30f 100644 --- a/Tests/CMakeLib/testOptional.cxx +++ b/Tests/CMakeLib/testOptional.cxx @@ -1,9 +1,10 @@ -#include #include #include #include +#include "testCommon.h" + class EventLogger; class Event @@ -93,14 +94,6 @@ public: NoMoveAssignEventLogger& operator=(NoMoveAssignEventLogger&&) = delete; }; -#define ASSERT_TRUE(x) \ - do { \ - if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ - return false; \ - } \ - } while (false) - // Certain builds of GCC generate false -Wmaybe-uninitialized warnings when // doing a release build with the system version of std::optional. These // warnings do not manifest when using our own cm::optional implementation. diff --git a/Tests/CMakeLib/testRange.cxx b/Tests/CMakeLib/testRange.cxx index 36c1e18e4..64bc3d756 100644 --- a/Tests/CMakeLib/testRange.cxx +++ b/Tests/CMakeLib/testRange.cxx @@ -1,19 +1,12 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include #include #include #include "cmRange.h" -#define ASSERT_TRUE(x) \ - do { \ - if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ - return -1; \ - } \ - } while (false) +#include "testCommon.h" int testRange(int /*unused*/, char* /*unused*/[]) { diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx index 350926620..b6723f564 100644 --- a/Tests/CMakeLib/testString.cxx +++ b/Tests/CMakeLib/testString.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include -#include #include #include #include @@ -14,13 +13,7 @@ #include "cmString.hxx" -#define ASSERT_TRUE(x) \ - do { \ - if (!(x)) { \ - std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ - return false; \ - } \ - } while (false) +#include "testCommon.h" static bool testConstructDefault() { @@ -1163,188 +1156,65 @@ static bool testStability() int testString(int /*unused*/, char* /*unused*/[]) { - if (!testConstructDefault()) { - return 1; - } - if (!testConstructFromNullPtr()) { - return 1; - } - if (!testConstructFromCStrNull()) { - return 1; - } - if (!testConstructFromCharArray()) { - return 1; - } - if (!testConstructFromCStr()) { - return 1; - } - if (!testConstructFromStdString()) { - return 1; - } - if (!testConstructFromView()) { - return 1; - } - if (!testConstructFromChar()) { - return 1; - } - if (!testConstructFromInitList()) { - return 1; - } - if (!testConstructFromBuffer()) { - return 1; - } - if (!testConstructFromInputIterator()) { - return 1; - } - if (!testConstructFromN()) { - return 1; - } - if (!testConstructFromStaticStringView()) { - return 1; - } - if (!testConstructCopy()) { - return 1; - } - if (!testConstructMove()) { - return 1; - } - if (!testAssignCopy()) { - return 1; - } - if (!testAssignMove()) { - return 1; - } - if (!testAssignFromChar()) { - return 1; - } - if (!testAssignFromView()) { - return 1; - } - if (!testAssignFromStdString()) { - return 1; - } - if (!testAssignFromCStr()) { - return 1; - } - if (!testAssignFromCharArray()) { - return 1; - } - if (!testAssignFromCStrNull()) { - return 1; - } - if (!testAssignFromNullPtr()) { - return 1; - } - if (!testAssignFromInitList()) { - return 1; - } - if (!testAssignFromStaticStringView()) { - return 1; - } - if (!testOperatorBool()) { - return 1; - } - if (!testOperatorIndex()) { - return 1; - } - if (!testOperatorPlusEqual()) { - return 1; - } - if (!testOperatorCompare()) { - return 1; - } - if (!testOperatorStream()) { - return 1; - } - if (!testOperatorStdStringPlusEqual()) { - return 1; - } - if (!testMethod_borrow()) { - return 1; - } - if (!testMethod_view()) { - return 1; - } - if (!testMethod_empty()) { - return 1; - } - if (!testMethod_length()) { - return 1; - } - if (!testMethod_at()) { - return 1; - } - if (!testMethod_front_back()) { - return 1; - } - if (!testMethod_clear()) { - return 1; - } - if (!testMethod_insert()) { - return 1; - } - if (!testMethod_erase()) { - return 1; - } - if (!testMethod_push_back()) { - return 1; - } - if (!testMethod_pop_back()) { - return 1; - } - if (!testMethod_replace()) { - return 1; - } - if (!testMethod_copy()) { - return 1; - } - if (!testMethod_resize()) { - return 1; - } - if (!testMethod_swap()) { - return 1; - } - if (!testMethodIterators()) { - return 1; - } - if (!testMethod_substr_AtEndBorrowed()) { - return 1; - } - if (!testMethod_substr_AtEndOwned()) { - return 1; - } - if (!testMethod_substr_AtStartBorrowed()) { - return 1; - } - if (!testMethod_substr_AtStartOwned()) { - return 1; - } - if (!testMethod_compare()) { - return 1; - } - if (!testMethod_find()) { - return 1; - } - if (!testMethod_rfind()) { - return 1; - } - if (!testMethod_find_first_of()) { - return 1; - } - if (!testMethod_find_first_not_of()) { - return 1; - } - if (!testMethod_find_last_of()) { - return 1; - } - if (!testMethod_find_last_not_of()) { - return 1; - } - if (!testAddition()) { - return 1; - } - if (!testStability()) { - return 1; - } - return 0; + return runTests({ testConstructDefault, + testConstructFromNullPtr, + testConstructFromCStrNull, + testConstructFromCharArray, + testConstructFromCStr, + testConstructFromStdString, + testConstructFromView, + testConstructFromChar, + testConstructFromInitList, + testConstructFromBuffer, + testConstructFromInputIterator, + testConstructFromN, + testConstructFromStaticStringView, + testConstructCopy, + testConstructMove, + testAssignCopy, + testAssignMove, + testAssignFromChar, + testAssignFromView, + testAssignFromStdString, + testAssignFromCStr, + testAssignFromCharArray, + testAssignFromCStrNull, + testAssignFromNullPtr, + testAssignFromInitList, + testAssignFromStaticStringView, + testOperatorBool, + testOperatorIndex, + testOperatorPlusEqual, + testOperatorCompare, + testOperatorStream, + testOperatorStdStringPlusEqual, + testMethod_borrow, + testMethod_view, + testMethod_empty, + testMethod_length, + testMethod_at, + testMethod_front_back, + testMethod_clear, + testMethod_insert, + testMethod_erase, + testMethod_push_back, + testMethod_pop_back, + testMethod_replace, + testMethod_copy, + testMethod_resize, + testMethod_swap, + testMethodIterators, + testMethod_substr_AtEndBorrowed, + testMethod_substr_AtEndOwned, + testMethod_substr_AtStartBorrowed, + testMethod_substr_AtStartOwned, + testMethod_compare, + testMethod_find, + testMethod_rfind, + testMethod_find_first_of, + testMethod_find_first_not_of, + testMethod_find_last_of, + testMethod_find_last_not_of, + testAddition, + testStability }); } diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.cxx b/Tests/CMakeLib/testVisualStudioSlnParser.cxx index 3485bac4d..3bab2ba58 100644 --- a/Tests/CMakeLib/testVisualStudioSlnParser.cxx +++ b/Tests/CMakeLib/testVisualStudioSlnParser.cxx @@ -1,10 +1,12 @@ -#include "testVisualStudioSlnParser.h" - #include #include "cmVisualStudioSlnData.h" #include "cmVisualStudioSlnParser.h" +#include "testConfig.h" + +#define SLN_EXTENSION "sln-file" + static bool parsedRight(cmVisualStudioSlnParser& parser, const std::string& file, cmSlnData& data, cmVisualStudioSlnParser::ParseResult expected = diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.h.in b/Tests/CMakeLib/testVisualStudioSlnParser.h.in deleted file mode 100644 index 62c3f4dcc..000000000 --- a/Tests/CMakeLib/testVisualStudioSlnParser.h.in +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef testVisualStudioSlnParser_h -#define testVisualStudioSlnParser_h - -#define SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@" -#define SLN_EXTENSION "sln-file" - -#endif diff --git a/Tests/CMakeLib/testXMLParser.cxx b/Tests/CMakeLib/testXMLParser.cxx index 32ee3ec0d..3b7af3aef 100644 --- a/Tests/CMakeLib/testXMLParser.cxx +++ b/Tests/CMakeLib/testXMLParser.cxx @@ -1,9 +1,9 @@ -#include "testXMLParser.h" - #include #include "cmXMLParser.h" +#include "testConfig.h" + int testXMLParser(int /*unused*/, char* /*unused*/[]) { // TODO: Derive from parser and check attributes. diff --git a/Tests/CMakeLib/testXMLParser.h.in b/Tests/CMakeLib/testXMLParser.h.in deleted file mode 100644 index da0b2757b..000000000 --- a/Tests/CMakeLib/testXMLParser.h.in +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef testXMLParser_h -#define testXMLParser_h - -#define SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@" - -#endif diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 730a7b413..428ec8be1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -32,11 +32,14 @@ endmacro() include(${CMAKE_CURRENT_SOURCE_DIR}/CheckFortran.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/CheckSwift.cmake) +# Isolate tests from user-wide configuration. +set(TEST_HOME "${CMake_BINARY_DIR}/Tests/CMakeFiles/TestHome") +set(TEST_CONFIG_ENV_CODE "# Isolate tests from user-wide configuration. +set(ENV{CMAKE_CONFIG_DIR} \"${TEST_HOME}/.config/cmake\")\n") +file(MAKE_DIRECTORY "${TEST_HOME}/.config/cmake") + # Fake a user home directory to avoid polluting the real one. if(NOT CTEST_NO_TEST_HOME AND (NOT WIN32 OR DEFINED ENV{HOME})) - set(TEST_HOME "${CMake_BINARY_DIR}/Tests/CMakeFiles/TestHome") - file(MAKE_DIRECTORY "${TEST_HOME}") - file(WRITE "${TEST_HOME}/.cvspass" ":pserver:anoncvs@www.cmake.org:/cvsroot/KWSys A\n") set(TEST_HOME_ENV_CODE "# Fake a user home directory to avoid polluting the real one. # But provide original ENV{HOME} value in ENV{CTEST_REAL_HOME} for tests that # need access to the real HOME directory. @@ -47,13 +50,6 @@ set(ENV{HOME} \"${TEST_HOME}\") ") endif() -# Suppress generator deprecation warnings in test suite. -if(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013") - set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS12} OFF)") -else() - set(TEST_WARN_VS_CODE "") -endif() - # 3.9 or later provides a definitive answer to whether we are multi-config # through a global property. Prior to 3.9, CMAKE_CONFIGURATION_TYPES being set # is assumed to mean multi-config, but developers might modify it so it is @@ -182,39 +178,11 @@ if(BUILD_TESTING) endif() if(WIN32) - # Macro to search for available Windows CE SDKs in the windows Registry - macro(select_wince_sdk selected_reg selected_sdk) - if(CMAKE_HOST_WIN32) - execute_process(COMMAND reg QUERY "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows CE Tools\\SDKs" - OUTPUT_VARIABLE sdk_reg - 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) - 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() - # Build a key to be used by get_filename_component that is pointing to the SDK directory - set(_reg "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows CE Tools\\SDKs\\${_sdk}]") - # Set return values - set(${selected_reg} ${_reg}) - set(${selected_sdk} ${_sdk}) - endif(CMAKE_HOST_WIN32) - endmacro(select_wince_sdk) - - set(reg_vs10 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]") - set(reg_vs11 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]") - set(reg_vs12 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]") set(reg_vs14 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]") - set(reg_ws80 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v8.0;InstallationFolder]") - set(reg_ws81 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v8.1;InstallationFolder]") set(reg_ws10_0 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\Setup\\Build Tools for Windows 10;srcPath]") - set(reg_wp80 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\WindowsPhone\\v8.0;InstallationFolder]") - set(reg_wp81 "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\WindowsPhone\\v8.1;InstallationFolder]") - 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 IN ITEMS vs10 vs11 vs12 vs14 ws80 ws81 ws10_0 wp80 wp81 wince tegra nasm) + foreach(reg IN ITEMS vs14 ws10_0 tegra nasm) get_filename_component(r "${reg_${reg}}" ABSOLUTE) if(IS_DIRECTORY "${r}" AND NOT "${r}" STREQUAL "/registry") set(${reg} 1) @@ -494,6 +462,7 @@ if(BUILD_TESTING) ADD_TEST_MACRO(OutDir runtime/OutDir) ADD_TEST_MACRO(OutName exe.OutName.exe) ADD_TEST_MACRO(ObjectLibrary UseCshared) + ADD_TEST_MACRO(SharedLibraryArchive UseSLA) ADD_TEST_MACRO(NewlineArgs NewlineArgs) ADD_TEST_MACRO(SetLang SetLangX) ADD_TEST_MACRO(EmptyProperty EmptyProperty) @@ -1040,6 +1009,32 @@ if(BUILD_TESTING) "${CMake_BINARY_DIR}/Tests/CPackNSISGenerator/_CPack_Packages/win32/NSIS/NSISOutput.log") endif() + find_program(IFW_BINARYCREATOR_EXECUTABLE NAMES binarycreator + DOC "IFW binarycreator program location" + ) + + if(IFW_BINARYCREATOR_EXECUTABLE) + add_test(CPackIFWGenerator ${CMAKE_CTEST_COMMAND} + -C \${CTEST_CONFIGURATION_TYPE} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/CPackIFWGenerator" + "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator" + ${build_generator_args} + --build-project CPackIFWGenerator + --build-options + --test-command ${CMAKE_CMAKE_COMMAND} + "-DCPackIFWGenerator_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackIFWGenerator" + "-Dconfig=\${CTEST_CONFIGURATION_TYPE}" + -P "${CMake_SOURCE_DIR}/Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake") + + set_property(TEST CPackIFWGenerator PROPERTY + ATTACHED_FILES_ON_FAIL + "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/Linux/IFW/IFWOutput.log" + "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/Darwin/IFW/IFWOutput.log" + "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/win32/IFW/IFWOutput.log" + ) + endif() + if(CTEST_TEST_CPACK) add_test(CPackUseDefaultVersion ${CMAKE_CTEST_COMMAND} --build-and-test @@ -2221,7 +2216,7 @@ if(BUILD_TESTING) ADD_TEST_MACRO(VSMASM VSMASM) endif() - if(${CMAKE_GENERATOR} MATCHES "Visual Studio") + if(CMAKE_GENERATOR MATCHES "Visual Studio") if(NOT MSVC60) ADD_TEST_MACRO(SBCS SBCS) endif() @@ -2283,20 +2278,17 @@ if(BUILD_TESTING) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSExcludeFromDefaultBuild") endif() - if(CMAKE_GENERATOR MATCHES "Visual Studio ([0-5]|[6-9][0-9])") - # This is Visual Studio 10 or above, so the default build tool is MSBuild. - add_test(NAME VSProjectInSubdir COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Tests/VSProjectInSubdir" - "${CMake_BINARY_DIR}/Tests/VSProjectInSubdir" - --build-two-config - --build-generator ${CMAKE_GENERATOR} - --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" - --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" - --build-project VSProjectInSubdir - --build-target test) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSProjectInSubdir") - endif() + add_test(NAME VSProjectInSubdir COMMAND ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/VSProjectInSubdir" + "${CMake_BINARY_DIR}/Tests/VSProjectInSubdir" + --build-two-config + --build-generator ${CMAKE_GENERATOR} + --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" + --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" + --build-project VSProjectInSubdir + --build-target test) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSProjectInSubdir") endif() get_filename_component(ntver "[HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion;CurrentVersion]" NAME) @@ -2319,22 +2311,21 @@ if(BUILD_TESTING) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}") endmacro() - if(vs12 AND ws81) - add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1 Win32) - add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013" WindowsStore 8.1 ARM) - add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013" WindowsStore 8.1 x64) - + # FIXME(#26248): Update this test to work with newer VS and Win 10.0. + # It previously ran with Visual Studio 12 2013 targeting Win 8.1. + if(FALSE AND CMAKE_GENERATOR MATCHES "Visual Studio") add_test(NAME VSXaml COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMake_SOURCE_DIR}/Tests/VSXaml" "${CMake_BINARY_DIR}/Tests/VSXaml" - --build-generator "Visual Studio 12 2013" + --build-generator "${CMAKE_GENERATOR}" --build-project VSXaml --build-config $ --build-options -DCMAKE_SYSTEM_NAME=WindowsStore - -DCMAKE_SYSTEM_VERSION=8.1 + -DCMAKE_SYSTEM_VERSION=10.0 ) endif() + if(CMake_TEST_VSWinStorePhone_VS_2017 AND ws10_0) add_test_VSWinStorePhone(vs15-store10_0-X86 "Visual Studio 15 2017" WindowsStore 10.0 Win32) add_test_VSWinStorePhone(vs15-store10_0-ARM "Visual Studio 15 2017" WindowsStore 10.0 ARM) @@ -2346,35 +2337,6 @@ if(BUILD_TESTING) add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015" WindowsStore 10.0 ARM) add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015" WindowsStore 10.0 x64) endif() - if(vs12 AND wp81) - add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1 Win32) - add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013" WindowsPhone 8.1 ARM) - endif() - endif() - - if(WIN32 AND wince) - macro(add_test_VSWinCE name generator systemName systemVersion generatorPlatform) - # TODO: Fix the tutorial to make it work in cross compile - # currently the MakeTable is build for target and can not be used on the host - # This happens in part 5 so we build only through part 4 of the tutorial. - foreach(STP RANGE 2 4) - add_test(NAME "TutorialStep${STP}.${name}" COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMake_SOURCE_DIR}/Help/guide/tutorial/Step${STP}" - "${CMake_BINARY_DIR}/Tests/Tutorial/Step${STP}_${name}" - --build-generator "${generator}" - --build-project Tutorial - --build-config $ - --build-options -DCMAKE_SYSTEM_NAME=${systemName} - -DCMAKE_SYSTEM_VERSION=${systemVersion} - -DCMAKE_GENERATOR_PLATFORM=${generatorPlatform}) - list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Tutorial/Step${STP}_${name}") - endforeach() - endmacro() - - if(vs12) - add_test_VSWinCE(vs12-ce80-ARM "Visual Studio 12 2013" WindowsCE 8.0 ${wince_sdk}) - endif() endif() if(CMAKE_GENERATOR MATCHES "Visual Studio" AND nasm) @@ -2498,9 +2460,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(vs12) - add_test_VSAndroid(vs12 "Visual Studio 12 2013" "Tegra-Android") - endif() if(vs14) add_test_VSAndroid(vs14 "Visual Studio 14 2015" "Tegra-Android") endif() @@ -3078,11 +3037,6 @@ if(BUILD_TESTING) --output-on-failure -C "\${CTestTest_CONFIG}") endif() - add_test(CTestTestPrintLabels ${CMAKE_CTEST_COMMAND} --print-labels) - set_tests_properties(CTestTestPrintLabels PROPERTIES LABELS "Label1;Label2") - set_tests_properties(CTestTestPrintLabels PROPERTIES PASS_REGULAR_EXPRESSION - "All Labels:.* Label1.* Label2") - configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestLabelRegExp/test.cmake.in" "${CMake_BINARY_DIR}/Tests/CTestTestLabelRegExp/test.cmake" @@ -3335,7 +3289,10 @@ if(BUILD_TESTING) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Fortran") set_property(TEST Fortran APPEND PROPERTY LABELS "Fortran") - if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) + if(CMAKE_Fortran_COMPILER_SUPPORTS_F90 + # FIXME(lfortran): The compiler fails on the test's modules. + AND NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran" + ) add_test(FortranModules ${CMAKE_CTEST_COMMAND} --build-and-test "${CMake_SOURCE_DIR}/Tests/FortranModules" diff --git a/Tests/CMakeOnly/AllFindModules/CMakeLists.txt b/Tests/CMakeOnly/AllFindModules/CMakeLists.txt index c990eb42b..8e70eb1a3 100644 --- a/Tests/CMakeOnly/AllFindModules/CMakeLists.txt +++ b/Tests/CMakeOnly/AllFindModules/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.4) # new enough for CMP0017 +cmake_minimum_required(VERSION 3.10) project(AllFindModules) # Avoid ctest truncation of output diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt index 727c7fcbc..4ceb768e1 100644 --- a/Tests/CMakeOnly/CMakeLists.txt +++ b/Tests/CMakeOnly/CMakeLists.txt @@ -35,29 +35,11 @@ endif() add_CMakeOnly_test(CheckStructHasMember) -add_CMakeOnly_test(CompilerIdC) -add_CMakeOnly_test(CompilerIdCXX) - if (APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") - add_CMakeOnly_test(CompilerIdOBJC) add_CMakeOnly_test(CheckOBJCCompilerFlag) - add_CMakeOnly_test(CompilerIdOBJCXX) add_CMakeOnly_test(CheckOBJCXXCompilerFlag) endif() -if(CMake_TEST_CUDA) - add_CMakeOnly_test(CompilerIdCUDA) -endif() - - -if(CMAKE_Fortran_COMPILER) - add_CMakeOnly_test(CompilerIdFortran) - set_property(TEST CMakeOnly.CompilerIdFortran APPEND PROPERTY LABELS "Fortran") -endif() -if(CMAKE_GENERATOR MATCHES "Visual Studio") - add_CMakeOnly_test(CompilerIdCSharp) -endif() - add_test(CMakeOnly.AllFindModules ${CMAKE_CMAKE_COMMAND} -DTEST=AllFindModules -DCMAKE_ARGS=-DCMake_TEST_CMakeOnly.AllFindModules_NO_VERSION=${CMake_TEST_CMakeOnly.AllFindModules_NO_VERSION} diff --git a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt index 0f3bd4cb8..3e19447df 100644 --- a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt index 24a0a865e..380dad754 100644 --- a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt @@ -7,10 +7,9 @@ # If you change this test do not forget to change the CheckSymbolExists # test, too. +cmake_minimum_required(VERSION 3.10) project(CheckCXXSymbolExists CXX) -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../CheckSymbolExists") include(CheckCXXSymbolExists) diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt index f00584338..97c75d496 100644 --- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CheckLanguage NONE) include(CheckLanguage) diff --git a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt index 6ecd19420..a9e99ece6 100644 --- a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CheckStructHasMember) diff --git a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt index b6ed9e961..9d97ea7fe 100644 --- a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt @@ -7,10 +7,9 @@ # If you change this test do not forget to change the CheckCXXSymbolExists # test, too. +cmake_minimum_required(VERSION 3.10) project(CheckSymbolExists C) -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) - set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") include(CheckSymbolExists) diff --git a/Tests/CMakeOnly/CompilerIdC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdC/CMakeLists.txt deleted file mode 100644 index 6fea73eb8..000000000 --- a/Tests/CMakeOnly/CompilerIdC/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 2.8.9) -project(CompilerIdC C) - -foreach(v - CMAKE_C_COMPILER - CMAKE_C_COMPILER_ID - CMAKE_C_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() - -# Version numbers may only contain numbers and periods. -if(NOT CMAKE_C_COMPILER_VERSION MATCHES - "^([0-9]+)(\\.([0-9]+))?(\\.([0-9]+))?(\\.([0-9]+))?$" - ) - message(SEND_ERROR "Compiler version is not numeric!") -endif() diff --git a/Tests/CMakeOnly/CompilerIdCSharp/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdCSharp/CMakeLists.txt deleted file mode 100644 index 6c0703745..000000000 --- a/Tests/CMakeOnly/CompilerIdCSharp/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.7.0) -project(CompilerIdCSharp CSharp) - -foreach(v - CMAKE_CSharp_COMPILER - CMAKE_CSharp_COMPILER_ID - CMAKE_CSharp_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() - -# Version numbers may only contain numbers and periods. -if(NOT CMAKE_CSharp_COMPILER_VERSION MATCHES - "^([0-9]+)(\\.([0-9]+))?(\\.([0-9]+))?(\\.([0-9]+))?$" - ) - message(SEND_ERROR "Compiler version is not numeric!") -endif() diff --git a/Tests/CMakeOnly/CompilerIdCUDA/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdCUDA/CMakeLists.txt deleted file mode 100644 index da1400024..000000000 --- a/Tests/CMakeOnly/CompilerIdCUDA/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.18) -project(CompilerIdCUDA CUDA) - -foreach(v - CMAKE_CUDA_COMPILER - CMAKE_CUDA_COMPILER_ID - CMAKE_CUDA_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() diff --git a/Tests/CMakeOnly/CompilerIdCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdCXX/CMakeLists.txt deleted file mode 100644 index 05e6bb222..000000000 --- a/Tests/CMakeOnly/CompilerIdCXX/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 2.8.9) -project(CompilerIdCXX CXX) - -foreach(v - CMAKE_CXX_COMPILER - CMAKE_CXX_COMPILER_ID - CMAKE_CXX_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() - -# Version numbers may only contain numbers and periods. -if(NOT CMAKE_CXX_COMPILER_VERSION MATCHES - "^([0-9]+)(\\.([0-9]+))?(\\.([0-9]+))?(\\.([0-9]+))?$" - ) - message(SEND_ERROR "Compiler version is not numeric!") -endif() diff --git a/Tests/CMakeOnly/CompilerIdFortran/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdFortran/CMakeLists.txt deleted file mode 100644 index 067fb8cd3..000000000 --- a/Tests/CMakeOnly/CompilerIdFortran/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 2.8.9) -project(CompilerIdFortran Fortran) - -foreach(v - CMAKE_Fortran_COMPILER - CMAKE_Fortran_COMPILER_ID - CMAKE_Fortran_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() - -# Version numbers may only contain numbers and periods. -if(NOT CMAKE_Fortran_COMPILER_VERSION MATCHES - "^([0-9]+)(\\.([0-9]+))?(\\.([0-9]+))?(\\.([0-9]+))?$" - ) - message(SEND_ERROR "Compiler version is not numeric!") -endif() diff --git a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt deleted file mode 100644 index 18a1ff658..000000000 --- a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(CompilerIdOBJC OBJC) - -foreach(v - CMAKE_OBJC_COMPILER - CMAKE_OBJC_COMPILER_ID - CMAKE_OBJC_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() diff --git a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt deleted file mode 100644 index 76c1e4b0e..000000000 --- a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(CompilerIdOBJCXX OBJCXX) - -foreach(v - CMAKE_OBJCXX_COMPILER - CMAKE_OBJCXX_COMPILER_ID - CMAKE_OBJCXX_COMPILER_VERSION - ) - if(${v}) - message(STATUS "${v}=[${${v}}]") - else() - message(SEND_ERROR "${v} not set!") - endif() -endforeach() diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt index 77dadcf27..b516f8c77 100644 --- a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt +++ b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(LinkInterfaceLoop C) # Add a shared library that incorrectly names itself as a diff --git a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt index 664682598..26de7c4bd 100644 --- a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt +++ b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if (NOT MAJOR_TEST_MODULE OR NOT MAJOR_TEST_VERSION) message(FATAL_ERROR "test selection variables not set up") diff --git a/Tests/CMakeOnly/SelectLibraryConfigurations/CMakeLists.txt b/Tests/CMakeOnly/SelectLibraryConfigurations/CMakeLists.txt index 3676b1792..e701aaa7e 100644 --- a/Tests/CMakeOnly/SelectLibraryConfigurations/CMakeLists.txt +++ b/Tests/CMakeOnly/SelectLibraryConfigurations/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(SelectLibraryConfigurations NONE) diff --git a/Tests/CMakeOnly/TargetScope/CMakeLists.txt b/Tests/CMakeOnly/TargetScope/CMakeLists.txt index 3bcbb00a7..f384fc39e 100644 --- a/Tests/CMakeOnly/TargetScope/CMakeLists.txt +++ b/Tests/CMakeOnly/TargetScope/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TargetScope NONE) add_subdirectory(Sub) diff --git a/Tests/CMakeOnly/find_library/CMakeLists.txt b/Tests/CMakeOnly/find_library/CMakeLists.txt index 2d487e3ff..09c32e4b7 100644 --- a/Tests/CMakeOnly/find_library/CMakeLists.txt +++ b/Tests/CMakeOnly/find_library/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FindLibraryTest NONE) set(CMAKE_FIND_DEBUG_MODE 1) diff --git a/Tests/CMakeOnly/find_path/CMakeLists.txt b/Tests/CMakeOnly/find_path/CMakeLists.txt index 7cc08add9..5bf31ba2b 100644 --- a/Tests/CMakeOnly/find_path/CMakeLists.txt +++ b/Tests/CMakeOnly/find_path/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FindPathTest NONE) set(CMAKE_FIND_DEBUG_MODE 1) diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt index 16e631b57..c7699524a 100644 --- a/Tests/CMakeTests/CMakeLists.txt +++ b/Tests/CMakeTests/CMakeLists.txt @@ -7,7 +7,6 @@ macro(AddCMakeTest TestName PreArgs) add_test(NAME CMake.${TestName} COMMAND ${CMAKE_EXECUTABLE} ${PreArgs} -P "${CMAKE_CURRENT_BINARY_DIR}/${TestName}Test.cmake" ${ARGN}) - set_tests_properties("CMake.${TestName}" PROPERTIES LABELS "CMake;command") endmacro() diff --git a/Tests/CMakeTests/EndStuffTestScript.cmake b/Tests/CMakeTests/EndStuffTestScript.cmake index bd8924669..517f425e3 100644 --- a/Tests/CMakeTests/EndStuffTestScript.cmake +++ b/Tests/CMakeTests/EndStuffTestScript.cmake @@ -22,7 +22,7 @@ elseif(testname STREQUAL bad_endfunction) # fail do_end("endfunction()\n") elseif(testname STREQUAL bad_endif) # fail - do_end("cmake_minimum_required(VERSION 3.5)\nendif()\n") + do_end("cmake_minimum_required(VERSION 3.10)\nendif()\n") elseif(testname STREQUAL endif_low_min_version) # fail do_end("cmake_minimum_required(VERSION 1.2)\nendif()\n") diff --git a/Tests/CMakeTests/PushCheckStateTest.cmake.in b/Tests/CMakeTests/PushCheckStateTest.cmake.in index cbd879def..3423dc7ab 100644 --- a/Tests/CMakeTests/PushCheckStateTest.cmake.in +++ b/Tests/CMakeTests/PushCheckStateTest.cmake.in @@ -1,10 +1,11 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) include(CMakePushCheckState) set(CMAKE_EXTRA_INCLUDE_FILES file1) set(CMAKE_REQUIRED_INCLUDES dir1) set(CMAKE_REQUIRED_DEFINITIONS defs1 ) set(CMAKE_REQUIRED_LIBRARIES lib1) +set(CMAKE_REQUIRED_LINK_DIRECTORIES linkdir1) set(CMAKE_REQUIRED_FLAGS flag1) set(CMAKE_REQUIRED_QUIET 1) @@ -14,16 +15,17 @@ set(CMAKE_EXTRA_INCLUDE_FILES file2) set(CMAKE_REQUIRED_INCLUDES dir2) set(CMAKE_REQUIRED_DEFINITIONS defs2) set(CMAKE_REQUIRED_LIBRARIES lib2) +set(CMAKE_REQUIRED_LINK_DIRECTORIES linkdir2) set(CMAKE_REQUIRED_FLAGS flag2) set(CMAKE_REQUIRED_QUIET 2) cmake_push_check_state() set(CMAKE_EXTRA_INCLUDE_FILES file3) -set(CMAKE_REQUIRED_DEFINITIONS defs3) set(CMAKE_REQUIRED_INCLUDES dir3) set(CMAKE_REQUIRED_DEFINITIONS defs3) set(CMAKE_REQUIRED_LIBRARIES lib3) +set(CMAKE_REQUIRED_LINK_DIRECTORIES linkdir3) set(CMAKE_REQUIRED_FLAGS flag3) set(CMAKE_REQUIRED_QUIET 3) @@ -34,6 +36,7 @@ foreach(pair IN ITEMS REQUIRED_INCLUDES| REQUIRED_DEFINITIONS| REQUIRED_LIBRARIES| + REQUIRED_LINK_DIRECTORIES| REQUIRED_FLAGS| REQUIRED_QUIET| ) @@ -55,6 +58,7 @@ foreach(pair IN ITEMS REQUIRED_INCLUDES|dir2 REQUIRED_DEFINITIONS|defs2 REQUIRED_LIBRARIES|lib2 + REQUIRED_LINK_DIRECTORIES|linkdir2 REQUIRED_FLAGS|flag2 REQUIRED_QUIET|2 ) @@ -74,6 +78,7 @@ foreach(pair IN ITEMS REQUIRED_INCLUDES|dir1 REQUIRED_DEFINITIONS|defs1 REQUIRED_LIBRARIES|lib1 + REQUIRED_LINK_DIRECTORIES|linkdir1 REQUIRED_FLAGS|flag1 REQUIRED_QUIET|1 ) diff --git a/Tests/CMakeTests/VersionTest.cmake.in b/Tests/CMakeTests/VersionTest.cmake.in index 0fddab6e0..c6f30d86e 100644 --- a/Tests/CMakeTests/VersionTest.cmake.in +++ b/Tests/CMakeTests/VersionTest.cmake.in @@ -1,4 +1,4 @@ -set(min_ver 2.7.20090305) +set(min_ver 3.10) cmake_minimum_required(VERSION ${min_ver}) if("${CMAKE_VERSION}" VERSION_LESS "${min_ver}") diff --git a/Tests/COnly/CMakeLists.txt b/Tests/COnly/CMakeLists.txt index 728ec5b67..610d4dad1 100644 --- a/Tests/COnly/CMakeLists.txt +++ b/Tests/COnly/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (COnly C) set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix") diff --git a/Tests/CPackComponents/CMakeLists.txt b/Tests/CPackComponents/CMakeLists.txt index a886b3df6..f7178cf2e 100644 --- a/Tests/CPackComponents/CMakeLists.txt +++ b/Tests/CPackComponents/CMakeLists.txt @@ -4,7 +4,7 @@ # application (mylibapp). We create a binary installer that allows # users to select which pieces will be installed: the example # application, the library binaries, and/or the header file. -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CPackComponents) # Create the mylib library diff --git a/Tests/CPackComponentsPrefix/CMakeLists.txt b/Tests/CPackComponentsPrefix/CMakeLists.txt index 581d3b378..b9b519b85 100644 --- a/Tests/CPackComponentsPrefix/CMakeLists.txt +++ b/Tests/CPackComponentsPrefix/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) project(CPackComponentsPrefix NONE) install(FILES file-runtime.txt diff --git a/Tests/CPackIFWGenerator/ApplicationIcon.png b/Tests/CPackIFWGenerator/ApplicationIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..c715e1bc7336344cad881cf44e80beb61c6eddf6 GIT binary patch literal 2335 zcmV+)3E=jLP)k(&zS4oUq8ekI=8tJNiaj zfL&(Z7~2Wk0z;@z;ktzwRy4a4hR_Dk1rmaUAdnCw1c8JgAqXS{2|*wsKMqI;0trDv z5J<@3Pfl#|O=Ax+&8&}|*|9DX@*^_rrk-rIWv=}uvEv@iiLPD0fZDNx&C4fMRvzCnEC$HFAz2n#}!-zD3R~k&rC^uJG)`a1v0EQ(H`hi!pWttb$y^ z6Ww##)h(EKfFtPFU;rOprVcz9+Euq-geIZbpV48ESmDNL8M?K=$PWM9K7qNwyc~^`bwRp&f-tCQ?h8^?zddi40 zOYAD3GO>)1z%FuZfGN4If1|fMqi2nJsmDBVcEaK&-!?>(HRYpURWu#2G{$Wq`m^R4 z{La71Bdk^{eg{yQ1R)E#u4B`i-*WEJX2tJ=IZvG6>Ci$V=^^6JUp~NBl|7B~SgYW30sR81s6eG#>{#WHvy*r@O@P z=kI%W`DeN3&<7fV$GzMUIrZJ%_<7}H$t&t6(l>S3@q6Lq;9{Je1Ukg*CO+=6XMWXY zCGM=6OkMZcQ1sHbx?|@&+ZsN}R1-M*Po;qauX}xA3rz{BnaEtr@)D3hej!OPspRAT ztP1a-wI-{;7Nm+no&o#C7Hj%uP4*$bkr$Ctlo$bdggwMmz{056PQ7s|em$HD&j(VJ zplGVJJIX3>gs4hjaA2Y9oWt+Z#jy9G8t*P?N{9jjGaq5=VBynE9@1({-)hvR3oidY z-Q?d*H;Ez4YM+x=0pX@LU_fj^y7j51z=4_8iWMbil<8@= zW{Un$JhQcCgzQ{o-ne=^b^Tn3sijzv26;$=6ewGeDJ~w4UkF%i~Pq!2iv=udLp`M5x34btzy^DLSXQZtb{Cq)T9kjKb{arB*@@ZKs5 zF%=Fh5PyWHm;eH#NFmm0B`;NN>SWYIzbyeo&JjDQWj6IMRcgrHZ+mv`pNF%%+!$<2 z^BIbRv%l<6hc)vv`8>?lst8f8f4tloJ8sfTy_8Rrjj#{~lAPP(As>FKDR!V1^N{Rx zd>l^4*D;*P8g#myw1wyoKexm$fQ3;POSq5M`*ww$oH5tanR*NZ1v=W>3kn=zrqb9u zPKdj7Bc#!3F{;E}miZc@j5@^SJ!^L!LO}U5v4Ft|&X=KMhmwE}>2(nQFx(l#GoP|=tJfML*MwHX-3HV+o4yU`6y+@rh7mFwq~8ZRrqq(S z%rHV=`x{8IE}&y(g7n*l5%O4F=rB%@G=7>^t;+*c5F#)0{%wWZcYr6sI;R5ks~vNh zS5@yPXfgBNP~xU(Klw&5#kq15Tv(uB!7_vpN0@GeB``+t3>>laIfy?#)fzl_=}NuY ziS&&?W#WX8fESiwkfPVu`&*^=upt-qS7(&zAQ+KUTtK65A#m-z-G)C=Hk!Qr^2cqP ztrYUvNctk6GNCjVfu9vyfs;%#TbGX~ul{s6eqUZ(g861w>}(D65$&*=MhC| z^>v3~H}Mhpp5T?16q6}7Cr%BfIUC3U&QD=kkdWHNy(w1wO0DRa!|zmzr9DJ{`f!4U^<6#DW8}VL48|-(nHD5OaX+pr#wu#X%Gtg@$vSiTh2*<~ zk^NSBkc$#I*Lh|29sBhgjJQbCxR0tvLKHtXe1^x@g_&LC{U8g+i&C2%ziOqpNiX%{ zQk)zHUYD;I#J+~}pPb(w3Hg@5M^3;;PXL)Q5Bcy4A5MT=UzamHQ5V?}cJe=Rc8!FTjwCLAq&~RaH;Jtri=X_jy1*Vt2m%Q~LJ&xZ;Xy)> z5Cjr}gdmU*Bm{wkAR!1O1PNIelSBzCG9xKkU>$z*{{y_Vblu<5zDEE6002ovPDHLk FV1ju)WxfCa literal 0 HcmV?d00001 diff --git a/Tests/CPackIFWGenerator/BundleIcon.icns b/Tests/CPackIFWGenerator/BundleIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..8808dd62db64540292417e8865bdf627aaf8792e GIT binary patch literal 33452 zcmeHOdvsOhmH+ND3E@$o&e9H@%&P5L{1ctwU<;)Zs`WM6I?kfg89Hmm+L^0ZX+AG~=i2H|SAQD9-)Q>fD~)-u>8^1*OsmxG8t_@661U4VtWdGrGhpEVfg-oZ zqz$XkEe8CC%M4iq1#V%=&5)H3X~1xsA&B#&7*PI@uyUoj)9l2a!x{)kozrJp0Vw@$ zC(r}JVVM4q+Zu4Y%>v;y{8lLB9w;$fKH!xNh!7DoiI`z9F<~+>VJKokm_#gKI3Z$Q ze#nScuHh08YE~x6ZC2QG7+~cz4WHii!!JArk@6Qm@Z-~z0+ypwGxAy%0&d!jEZRb> zBqPgTEL@=DD>WQUX61qvKSB=E#n69L1QCB+6=6ZlKJ7*x5wp*`VS|``y{ipk_QShu z5VIe-O9nCf(Yu59?z#OtxF&QQ{MhW|m+O)c^h*1HUo)->L9eW9{L1-M2zsrCPkMzy zetIFio%C{rLYL^377BepuNk4xReELd>sn4IbZvDg^eO!>>_7jc`CtARUDNI_?ib>X zO~Tpd`=5=1E<01W`wY|SuM=*c;R{){!rN!iM*jxk_0d+yS}#Ojz`)sB{WU`Pu!#%O zs)jVzaMNc0I*4-(T)?=9t02#l+YYOLEfm->>FTb8B42Jpu?7n4_;aDCfT943T&xKF z?hq;l#yT4n40=c~BT@NsANh$2w&h z)j%^X%P$=LW-Xi#AjMbf1{#G9v>^kH3m0s$W^kZ!vbAy_%M${PD;^7#@ToxKx>kV( zn2+NO2ZjnWu6rK`nN~^_;#>$Rzy)&oRN>8okb+xiPyJbQ3_7b1jzvnGCY!#wJBMPpa?$>z0Eli;ar4&cJqDK>(xZ z2dQHKM$r$Ujsh4(KcqSWU=;l@Y8zk_{jh3vtqiZ11|z6RKayH99LYdmcEkU_{D+U+ zD1`IwXbXp*_Ni~u@6N z^1d77I@6@ny+r~|se!m1@Zr`|h)9+WaNPaa7 zZ=tJMcne*FbLkRPhy432KC$sx{s=Xj-Ut3D90Q$ug)`{u+e1y1xm&n{XqWnS2{*Nt z=1$=a%5Jl7hrr~BX47mIBFN3A)g*EaG+O3%h*YO(Z4+oGsr@pyLdx3?%i1EivlM0{ z#JbD08U**1!mNiF+dk?st<9p4+7y6nf|$1<-YAM7M&n1#TCmuluF|R#C2&YWbZkN1 zh8V5dKq(aHG)-(nfv%8xNa~9*t2%w17FtXbdq8sGK8{^zadRFh#$b%5$tr>#`@tf^ zu?1}aT2`_^cQH_i zAN>@1g)N zXnA$NObs-K|5|B4;VeL|4Fi(}X7&JiK=*+my#dV$CVJ|J99xX7Xqo+_5{1n;iT*sX zW8jjoYsH{iLBl6&#So|!zu^$Rozk}xP16oRW4-WkrTEce%0hvJ3k5BjEE8(gxJb~Q z$qK>$KB|ub^uq$hL4C3CQE3#SPZFq$BKqG)RZ&F!DM|um00|caPFL0g{`YY;pygGi zK+Uf%1cStO9r#EUt^?USN0b5WQ$KH)fe+nwbQPG?e@9dSB`T^2pr#_oH$0d{WEo&o zQdNM4)N;xy5SVOaKZ+!yg|}R&1A`19MxKSQ)Ixe_$btYDq41ScBTg1n2@sf-5D-5i zjZ#2;TNsJxbh#9Sxq&PwWU>}~n=FT4Mq5^buYt5k85&f_7IPaZVO!X24z2^aW|%%% z2bPcOz)j;S3c(wsj-Cp^i_^sxmx9HmfMJuixEQ#RM>$Xw=mQ*864<6P*#bG5ec9E^ zg#+7M8u(a-w`F;t#iw2#1%et^eZ5>du+3$Hi^H-DMGlsAuq}oklFDMiHWko%EQ)3c z@5qWN#Woj=R5&{Af+?|_ot>&)pu=ZPST@4-VmX><*--TYgF=iMv})M(QfOh#P}KrG zsMC@GLb6&c4@R$uYJmk$K3F!IXxWG;7%GH(eAq;5hpHKvWB!0I%+pQbvO(Ao8dMDQ z7(#rA8CMHVvjGaMKm$mdfq0`vTtQ50xJulaxahVr~N92H#kuE0Z z&u;$4=pOgOF(mW0aaoQ{MF*}IU-ZmejgE;LY%*W&)PWveHF*u0R zKE`}h2XWdDG9Sf3oc2SQkLVyy`ytJTcQB0kuvh+Y_QM=7ta;q_Lmn_Z{Lp7SlKl`4 zMl_Gxe$W9U!w)`#(d=U!jA|aYebfP?!$+UNSnMMljLAH1d;5T~!H3UatoF*mn9Y-J zAFqSBZu#RgUj9=WtiODejt<%X4hm1`^;b7n?fJ9tod3;#ZK*5%vq(Gl>z6mKYdRGA zlSnIDTk*utf*prG6d9q{cRlmePyX|{Bdypu|C`=#-(6H)UtjZ~$UOJ!6L&2sHyayw z9SRMI%(AtGIZMhLy1I{b9C}|&qvKY7V@dgz-j|OZX@!7}{l@$y<;_>F?5VGLU(BH6 z4I`(p@qa(u+t9Rs&lNF)Fbw1CzSUQ*o_n$7;P!XLOvW*cg%AGx51;g%Ykjr}LdIfY z=;JH9HrBi=T!a@eGRZI$V#s%diy&<&qh6-7Cw3%Xwi4D13^@bi(}ej89=MDr$`boB zV;30C`!0R#4|y2-ZN@GzGVeOp_X$4lk&OE_Hsp66`PIi78*;`jFkB?p*!_%M#L|EU ze~ZDxVn%;cqlXnt9SVk5id;qgO@_yl@H<-1UCq<*Z!kQ>OV;iE5MqYEsNqRb-b@OH zhl1giB45E@WcV^RFVOI>F??9e@O>H{3Q9Zd86FCThk}w{sNnk;zQS-(Qj0YFs|+6& zGyDY&A676t6b!Ev#R~oc!&d?xRZybg&oey4R0I$+{L31?%5dEGJBS$^Vg{GuQU(1o zLs!d^Db>j57`fVTQOVfIy+A(SgnER6YQx5c;{3&vxN4L_qxS;+m1B3UXY(=*-vju{ zf}F6JiJ#W+P*4@Xo~c8@@K8`4lq>S58NQaYTA|@jGJIIf@Fz5USi$g6FuYQ%QSc`i z-sB>v)bL#l4>6Sh#0-C2!$Uz;00qNC!SG74R>2=<_)TnHrQtgmJ}hSVb`2j^Fgz3t zuN3PPe7lDC%wMo%dEmWj?cj)Z;90nAd1LTTP_T>VwF}coyJhj6k3F@g{Ua#Z4V3yU z(iR7*4zvc_KdMo#p4WlOxFzSl>TO3mTG~HauidrjaAo8a)*d?AMh8O4E?czAT4U-h zIp40^+1}9-47PvpagFv13!NM<0G@AeYxM_RhhFOYM0;;x@9T|>`3t^&+x;7wfe~O6 zo5XWWV!e^RaM>dpo1Q(`+L{}5!p{cHJ=do_b9!^7b(vk{mAESN%-;g&flXM;M$U@tSM zLi$u56oRQa>}C#C2E~$+p%7reBz9^N6*8PT_sNu0upd}45m+&i3JU0d6s7 z2c2!lKH98EZfBAeF!dIQDW16to-S=@3bqD=f!6$>qy3|LO%)&Q(wZeL3&nJA`pg?{ zy$6>_i=>>VY?*MQCR}28=G~N?J=aqrrhDc_(e|q&m^6-H()h4Fd#c}CCT4k~dE=`1 zs6nxAWY)OH&AVx?r&^@jyd`R*=3LL5@sWM@RPTB*)h10~uwu5GG+W$~3D2ZB5ZC&x z```PxLB|zlOtDD9Z4y(M;jQ28?E6GB+az2kt@b+=(pHh-nY(z~kxRcH_~WN`(oNxd z^rtHpG0b+6?#=j%tUJHA@!%irgx3t6r;djnVLqUEr`5S;E= zFh7sOwi~3+ELuM6VR?kPV(svhpy@o=C7LJ165zWiKRaP-CJQhF-k4F)M z4JWWtWOBF+gP=}Eu`1N8D#a9LWf*`u9mPraF_j{NI5AFdG$p$ys}SjwKM8X#ijm!) zm7yJ%2_!xj#B8chl{?wO9_al;s6?bQAFz29hbSfzSr}>6Thh6vKjl|U9s!c z-@oR5^U`no{~OPi`mek%`pfVH_Yd#B=Rgy#rjt+XDJ3L^nN!ZKE!?E$l-wJ?vcsNJ zR$TX&=Kdx%r!2bpZ*Ke6LN%vQ=x=_^o>M5uUucS$Qy}nOhhyzIg#_uherw_TSQF%2 zk(TZC%)RMh8dKCv0g3QlUq@4lJx`EKe16TA&5&_h7%-$PebH?T)r^v7WRMl3y~Aj9 zp?UqnE%7dEJShzb7Xd=ZfLch1_GE&i@x@MtvPC{f|3bWgsSEUlK?6- zQ8h(Kp+-HSQKht!K_O&NEhI=aJ8UAzN)4$d2Ps^uh#uFFQi|oCG6W$*Y9T?Yd7+*l zt2CsV7Nl^Uf@}{%VlteUhC2;`LaaeCM=qTTF~LU92@g%j-HVeSJttIOSEJyc*YKv1 z&VB0UIo=xf0iBLT22TlF7gL8KJ=tSIrfyHS-Jb2W{n>*;rXCbXt-ZErueHXs@T5Q% zxOLO%!GK-Y%H~!L3d(>^w1vB-*g0X_U4IJ*boHu!L{RG z@1BA#gJ?j{H0(YfW5TW<|I<|HiNz38!mh`cL8}<-=VG>x#*{#MeCbmzxqG$y$RU9Y z&{dIp=-GY5h@jNueaH!c^~(Ql_8&DNkikYpV5cU4#{QPY_IR*+)q~T}{1F9MFojCP zyEvdngJ(DRXTg9-sL6mQj5{8v*?v^b1_w45&fcU0p=N^$W7@)HKYI4)$?ik;bU=2S z6`$>zPnj`m{x`n$*e|;*tLO0Z$Gf_YxAJswps{2&I$@#)W2RPScozM5^`Wk_e#_C* zu|->3&MfJMPkndoa>#|lvB+$4}9~luAezQ-CM_mU)t0H{_0$hx)kvrk;0I{`8{BzcUSrEUvdLz)#%Ks!2m{_*#lNaZ}*EwgOZqD zoUn#5F^mSB(Y7dJYnd3#m>A4Bm2J_)a7UqNVZ9_`Hdsdlv2sn!o(vRJMRJHfPq75Pt4ywHjeEj1nB<0NFTmw@WaRJiPB zsX}DXaKI_-C74qubhLEqq^5)KFD9G|QH1a_q|dL}dWxol45GC7`&0@Stl5-4O-G3I zMc?gH^TDxyxO2rXyI!+qgxO@yz2iL12Wd~|7L=_!(sdTv2p*YV+^!~sXKEU{-a3`h z)v)H?g|q*9BTWeIM^`;lWA-fU%70mJs|XLu0Kfv9uV*#Yeq4W&b%EtNawm-#vaTU!^st!FA!c-2 ztl;0$@JgYFk~cLxzF0=^YAk`6;lpBrhnV3ZK6>xGv^>rJ4Gj;)ajcE{dbp_JOCU$K z)QAEx!-vHL4>7~rV#WS74X+f1Y8CI(@MZXt6>=XrSWr?Sru?gm9M%jEF;zg3X8)>& zSBj_+gECve2Sg4T1RoX?`wJSL#HfRaQA4E`3aW&taf9HiEJw=?k>dt|Yau~n24Ut^ zvS>;*@pGDZ6&H<0mQ%+*AXjr%6}Fr^eg*8Xn9w0U_ew7=A6u-#-UIl`S`@re=%x2G z!hW9M*K--Du?FJv7YQC>Dgt{1QSc;24MdC~3SKFqMiGLq=9WLqSy#HI@*($ytpYOb9+KCivqT-WIFibm-tHMbwBw@S7MOcRacgwd>%7#RLyA z$DYL7N@n9esz(&X9*Q$98>2@Qg5PX;(1pd!C&V9ZOz(@wTcf5E<)jvOtutrN_H=f% zP-kU3Yt!cfgERa09Xo!gRW?<+324y?ueGN2p6TAZ=LLF}p#~5-I~Z{4Yc21Y{d4dA#iNo>P zdDLj4+~a3yI8sd>=%zT>`}O%(Lw&fdbcn%(+~Qz_9G~IGP9@|P*9e6@tfkY&%)+N# ziKM8=+ZM|CZtc91Vkd)-M;C(z6CzM$dCu&k;Nz)9>{vn^sx0@}WBj}#YA8`8%Fsul z**J3i__3kK5|w~5YZ|ql_pNw(-|&M8@hP)r^>(+fd3r(A=l=+O>|jFV%BYFJUHQA93w=FIg}MLo(;;Hm=Zbp;ez?)L93 zo{kGM{1j%0xrMkFprDXTg07`cvtbZ;J^t@$$Mh%LhH$jnUhZdEs9 zTuq_#ZFP+R@=#T*`rJ8Q`k**}@j`=C$O>>mL9U@-*8!bu-00XJKDJ|lGBOaE{mJkj8U4wqfLQ;jt$^6< zr#b_%*-y3q*yyLa0*3ORx(XO7`>D*pP}xtV|DmFv$_g0He`+dVxa_AY1H)xMRsM&I zeyS>9B>%~;fRVDFiVTdD{Z#lLDf+3XfYJOXw*p4XezG$#TK1Fef3)Z)y8_1IKY0}} zmh2}p17pd4GX0Mw`pK+-vHDL=1&lTO$;!Z3v!5*gV~u{YDj**JiLZcovY(6$#FPDG z_>U+0$*6#M{U^2p;>~`dGZ1g~6YW3V=qI`Y#^XP66)>LcCo%)$$$ldJk0<(xtbp?gwiM53RF3Ye(>iB-Tvvp>-c zOf>rw^*_<*PqYFO;eY%UkcjM0Bm;@a{zUvIBKi}lfJFHpUj-y8`{T_(qOw0;|A~tJ zcq<^0{^PHJL}q_H8AxRI$KyYd(H~C*B-(#`6_Duc$D4sfXFp#5iH?4}6_5=7W3PZ@ zWIvt^BqRIr_)kXk+`e{%{#cRrZJTpQ`8&R{^Q?Kez%?nf;+6kjm_n|JVqmGWz5{_BS)1`RAyl?mhZ9 tOR28E*!-vZ`iu211pSsWm4C6nTBP5Srt)v{ezlppUr2uD>HmEM{tp8ay}tke literal 0 HcmV?d00001 diff --git a/Tests/CPackIFWGenerator/CMakeLists.txt b/Tests/CPackIFWGenerator/CMakeLists.txt new file mode 100644 index 000000000..342c83f14 --- /dev/null +++ b/Tests/CPackIFWGenerator/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.16) + +project(CPackIFWGenerator) + +add_executable(hello main.cpp) + +install(TARGETS hello + ARCHIVE DESTINATION . + RUNTIME DESTINATION . + LIBRARY DESTINATION . + BUNDLE DESTINATION .) + +# Component that is a reserved name on Windows. +# See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file +install( + DIRECTORY . + DESTINATION txt + COMPONENT CON + FILES_MATCHING PATTERN *.txt) +# Component name that is similar to a reserved name on Windows. +install( + DIRECTORY . + DESTINATION txt + COMPONENT Console + FILES_MATCHING PATTERN *.txt) +# Component name that is strongly discouraged on Windows. +install( + DIRECTORY . + DESTINATION txt + COMPONENT EndsWithDot. + FILES_MATCHING PATTERN *.txt) + +set(CPACK_IFW_PRODUCT_URL "https://cmake.org/") +if(WIN32) + set(CPACK_IFW_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/install.ico") +else() + set(CPACK_IFW_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/BundleIcon.icns") +endif() + +set(CPACK_IFW_PACKAGE_WINDOW_ICON "${PROJECT_SOURCE_DIR}/install.ico") +set(CPACK_GENERATOR "IFW") + +set(CPACK_IFW_PACKAGE_PRODUCT_IMAGES + "${PROJECT_SOURCE_DIR}/ApplicationIcon.png" + "${PROJECT_SOURCE_DIR}/SplashScreen.png" +) + +set(CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS + "https://www.ApplicationIcon.org" + "https://www.SplashScreen.org" +) + +include(CPack) +include(CPackIFW) diff --git a/Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake b/Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake new file mode 100644 index 000000000..4ae6d41ea --- /dev/null +++ b/Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake @@ -0,0 +1,93 @@ +message(STATUS "=============================================================") +message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)") +message(STATUS "") + +if(NOT CPackIFWGenerator_BINARY_DIR) + message(FATAL_ERROR "CPackIFWGenerator_BINARY_DIR not set") +endif() + +message(STATUS "CMAKE_COMMAND: ${CMAKE_COMMAND}") +message(STATUS "CMAKE_CPACK_COMMAND: ${CMAKE_CPACK_COMMAND}") +message(STATUS "CPackIFWGenerator_BINARY_DIR: ${CPackIFWGenerator_BINARY_DIR}") + +if(config) + set(_C_config -C ${config}) +endif() + +execute_process(COMMAND "${CMAKE_CPACK_COMMAND}" + ${_C_config} + RESULT_VARIABLE CPack_result + OUTPUT_VARIABLE CPack_output + ERROR_VARIABLE CPack_error + WORKING_DIRECTORY "${CPackIFWGenerator_BINARY_DIR}") + +if(CPack_result) + message(FATAL_ERROR "CPack execution went wrong!, CPack_output=${CPack_output}, CPack_error=${CPack_error}") +else () + message(STATUS "CPack_output=${CPack_output}") +endif() + +set(expected_file_mask "${CPackIFWGenerator_BINARY_DIR}/_CPack_Packages/*/IFW/*/config/config.xml") +file(GLOB project_file "${expected_file_mask}") + +message(STATUS "project_file='${project_file}'") +message(STATUS "expected_file_mask='${expected_file_mask}'") + +if(NOT project_file) + message(FATAL_ERROR "project_file does not exist.") +endif() + +# should match !define MUI_HEADERIMAGE_BITMAP "${PROJECT_SOURCE_DIR}\header-image.bmp" +file(STRINGS "${project_file}" lines) + +file(STRINGS "${project_file}" tag_start REGEX [[]]) +file(STRINGS "${project_file}" tag_end REGEX [[]]) +list(LENGTH tag_start tag_start_size) +list(LENGTH tag_end tag_end_size) + +if(NOT tag_start_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_start_size}") +endif() +if(NOT tag_end_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_end_size}") +endif() + +file(STRINGS "${project_file}" tag_start REGEX [[]]) +file(STRINGS "${project_file}" tag_end REGEX [[]]) +list(LENGTH tag_start tag_start_size) +list(LENGTH tag_end tag_end_size) + +if(NOT tag_start_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_start_size}") +endif() +if(NOT tag_end_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_end_size}") +endif() + +file(STRINGS "${project_file}" tag_start REGEX [[]]) +file(STRINGS "${project_file}" tag_end REGEX [[]]) +list(LENGTH tag_start tag_start_size) +list(LENGTH tag_end tag_end_size) + +if(NOT tag_start_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_start_size}") +endif() +if(NOT tag_end_size EQUAL 2) + message(FATAL_ERROR "Expected to have 2 not ${tag_end_size}") +endif() + +set(TO_SEARCHES + "" + "ApplicationIcon.png" + "https://www.ApplicationIcon.org" + "SplashScreen.png" + "https://www.SplashScreen.org" + "" +) +foreach(TO_SEARCH ${TO_SEARCHES}) + string(FIND "${lines}" "${TO_SEARCH}" output_index) + message(STATUS "Found the ${TO_SEARCH} at index ${output_index}") + if("${output_index}" EQUAL "-1") + message(FATAL_ERROR "${TO_SEARCH} not found in the project") + endif() +endforeach() diff --git a/Tests/CPackIFWGenerator/SplashScreen.png b/Tests/CPackIFWGenerator/SplashScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..83425658095012eba18a92d2d1a3038a46239d9e GIT binary patch literal 909 zcmeAS@N?(olHy`uVBq!ia0y~yV9H@&VANq^28u9yv%3Q+&H$efS0Mc#42mn3CWGWk zg8YIR_LqlyO)>-W3_M*NLn;{GoHJx(U|?Y|n5DU}UR3f!9;etFpa2j|s13}^yFYvO z?Asr_j++QDIWVy3-&bVuZV}R)ap1l7j3=vNS)K zsuet#whzc(n$N_Z_j2}u#^_&c1rM(6W0cvkHJ^b!@2xaY=r>lON7ME(&H-vp^UFR?il zr76!_X)VolbY^eWw0AiMEa_&SBzJ9ocwzR<7wON>sJY~&DKD65JX>h%)U!=t>v95) zq?rjx?cA7nQ0hj8$KwlZY+32bN}b-)QcLFcc8T!i7BrNZ83|czGL$)Y?pVPW-drB` z&6^EnDy^jtZ;Vb)wkYGyGcmyP&P)+o8EvWBK!!+?)16?Y%y8{egey*k>}Qn5M=|;GMS8Ni4RF|E~L7 z!IIq4DRtyy8Xl8ulb%uXKkYU<^XGcw#xTT!A-xQuP+wjhk=|$L+B{NP>W^R2K zac=I;F3Wi@jy)5Z-97hk-@E^}b~l64BU|IYYnEU3akv#EtJ!2XT{O%URy(t~M8f5A zUQ(Q`cF#t$T&rdGHw#Ekx_mEXn%(QE6RmR-)jn@tBl+O6U-~)w)%TAd{L}Vf<;Q&< zH9O9{m!9wVKKrEm)!*Mfb(-yaQuE>Nd#CyR|Fth4fAK% #include #include #include diff --git a/Tests/CudaOnly/SeparateCompilationPTX/kernels.cu b/Tests/CudaOnly/SeparateCompilationPTX/kernels.cu index f4a52d421..fbe0d26d7 100644 --- a/Tests/CudaOnly/SeparateCompilationPTX/kernels.cu +++ b/Tests/CudaOnly/SeparateCompilationPTX/kernels.cu @@ -5,10 +5,3 @@ __global__ void kernelA(float* r, float* x, float* y, float* z, int size) r[i] = x[i] * y[i] + z[i]; } } - -__global__ void kernelB(float* r, float* x, float* y, float* z, int size) -{ - for (int i = threadIdx.x; i < size; i += blockDim.x) { - r[i] = x[i] * y[i] + z[i]; - } -} diff --git a/Tests/CudaOnly/SharedRuntimeViaCUDAFlags/CMakeLists.txt b/Tests/CudaOnly/SharedRuntimeViaCUDAFlags/CMakeLists.txt index cf6eef244..89a8de871 100644 --- a/Tests/CudaOnly/SharedRuntimeViaCUDAFlags/CMakeLists.txt +++ b/Tests/CudaOnly/SharedRuntimeViaCUDAFlags/CMakeLists.txt @@ -1,10 +1,6 @@ cmake_minimum_required(VERSION 3.17) -project(SharedRuntimeViaCUDAFlags NONE) - -set(CMAKE_CUDA_FLAGS "") -string(APPEND CMAKE_CUDA_FLAGS "-cudart shared") - -enable_language(CUDA) +set(ENV{CUDAFLAGS} "$ENV{CUDAFLAGS} -cudart shared") +project(SharedRuntimeViaCUDAFlags CUDA) add_executable(CudaOnlySharedRuntimeViaCUDAFlags main.cu) diff --git a/Tests/CudaOnly/Unity/CMakeLists.txt b/Tests/CudaOnly/Unity/CMakeLists.txt new file mode 100644 index 000000000..0bdc1f6df --- /dev/null +++ b/Tests/CudaOnly/Unity/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.30) +project(Unity CUDA) + +set(CMAKE_UNITY_BUILD 1) + +add_library(UnityObjects STATIC a.cu b.cu) + +add_executable(CudaOnlyUnity main.cu) +target_link_libraries(CudaOnlyUnity PRIVATE UnityObjects) + +if(APPLE) + # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime. + set_property(TARGET CudaOnlyUnity PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) +endif() diff --git a/Tests/CudaOnly/Unity/a.cu b/Tests/CudaOnly/Unity/a.cu new file mode 100644 index 000000000..7f3752c7e --- /dev/null +++ b/Tests/CudaOnly/Unity/a.cu @@ -0,0 +1,12 @@ + +__device__ int function(int a, int b); + +__global__ void kernel() +{ + function(2, 3); +} + +void test_unity_functions() +{ + kernel<<<1, 1, 1>>>(); +} diff --git a/Tests/CudaOnly/Unity/b.cu b/Tests/CudaOnly/Unity/b.cu new file mode 100644 index 000000000..69d799aa4 --- /dev/null +++ b/Tests/CudaOnly/Unity/b.cu @@ -0,0 +1,5 @@ + +__device__ int function(int a, int b) +{ + return b * b * a + 1; +} diff --git a/Tests/CudaOnly/Unity/main.cu b/Tests/CudaOnly/Unity/main.cu new file mode 100644 index 000000000..1cbbbe6ea --- /dev/null +++ b/Tests/CudaOnly/Unity/main.cu @@ -0,0 +1,8 @@ + +void test_unity_functions(); + +int main(int argc, char** argv) +{ + test_unity_functions(); + return 0; +} diff --git a/Tests/CudaOnly/utils/bin2c_wrapper.cmake b/Tests/CudaOnly/utils/bin2c_wrapper.cmake index 0baf93434..d598eda9f 100644 --- a/Tests/CudaOnly/utils/bin2c_wrapper.cmake +++ b/Tests/CudaOnly/utils/bin2c_wrapper.cmake @@ -6,7 +6,7 @@ foreach(obj ${OBJECTS}) get_filename_component(obj_dir ${obj} DIRECTORY) if(obj_ext MATCHES ".ptx") - set(args --name ${obj_name} ${obj}) + set(args -p 0x0 --name ${obj_name} ${obj}) execute_process(COMMAND "${BIN_TO_C_COMMAND}" ${args} WORKING_DIRECTORY ${obj_dir} RESULT_VARIABLE result diff --git a/Tests/CustComDepend/CMakeLists.txt b/Tests/CustComDepend/CMakeLists.txt index 777cdcc31..5f6fc7c16 100644 --- a/Tests/CustComDepend/CMakeLists.txt +++ b/Tests/CustComDepend/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(CustComDepend) include_directories("${CustComDepend_SOURCE_DIR}") add_definitions(-D_CRT_SECURE_NO_DEPRECATE=1) diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt index c145907a1..aaba11fe4 100644 --- a/Tests/CustomCommand/CMakeLists.txt +++ b/Tests/CustomCommand/CMakeLists.txt @@ -1,7 +1,7 @@ # # Wrapping # -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (CustomCommand) add_subdirectory(GeneratedHeader) @@ -412,6 +412,10 @@ add_custom_target(do_check_command_line ALL COMMENT "Checking custom target command line escapes ($dollar-signs$)" ) add_dependencies(do_check_command_line check_command_line) +add_custom_command(TARGET do_check_command_line POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -E echo "POST_BUILD command with $dollar-signs$" + COMMENT "Checking custom target POST_BUILD command line escapes ($dollar-signs$)" +) add_custom_target(pre_check_command_line COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_CURRENT_BINARY_DIR}/check_mark.txt diff --git a/Tests/CustomCommandByproducts/CMakeLists.txt b/Tests/CustomCommandByproducts/CMakeLists.txt index e391a6f28..73a7fccc1 100644 --- a/Tests/CustomCommandByproducts/CMakeLists.txt +++ b/Tests/CustomCommandByproducts/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0058 OLD) project(CustomCommandByproducts C) diff --git a/Tests/CustomCommandByproducts/External/CMakeLists.txt b/Tests/CustomCommandByproducts/External/CMakeLists.txt index 81e072bc4..12715f44d 100644 --- a/Tests/CustomCommandByproducts/External/CMakeLists.txt +++ b/Tests/CustomCommandByproducts/External/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(External C) add_library(ExternalLibrary STATIC ExternalLibrary.c) diff --git a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt index 531690ae1..2145478ad 100644 --- a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt +++ b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestWorkingDir) add_custom_command( diff --git a/Tests/CxxDialect/CMakeLists.txt b/Tests/CxxDialect/CMakeLists.txt index c88641b83..349a96b4a 100644 --- a/Tests/CxxDialect/CMakeLists.txt +++ b/Tests/CxxDialect/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CxxDialect) add_executable(use_typeof use_typeof.cxx) diff --git a/Tests/CxxOnly/CMakeLists.txt b/Tests/CxxOnly/CMakeLists.txt index 6cd3a8ecd..0159e3d0c 100644 --- a/Tests/CxxOnly/CMakeLists.txt +++ b/Tests/CxxOnly/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple CXX only test case -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (CxxOnly CXX) set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix") diff --git a/Tests/CxxSubdirC/CMakeLists.txt b/Tests/CxxSubdirC/CMakeLists.txt index 52474f8dc..1fd775cea 100644 --- a/Tests/CxxSubdirC/CMakeLists.txt +++ b/Tests/CxxSubdirC/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(CxxSubdirC CXX) add_subdirectory(Cdir) add_executable(CxxSubdirC main.cxx $) diff --git a/Tests/Dependency/CMakeLists.txt b/Tests/Dependency/CMakeLists.txt index cae108ea8..e9160a0f8 100644 --- a/Tests/Dependency/CMakeLists.txt +++ b/Tests/Dependency/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project( Dependency ) # to test directories with only one character One was changed to 1 diff --git a/Tests/EmptyDepends/CMakeLists.txt b/Tests/EmptyDepends/CMakeLists.txt index 5ba0b49ce..9fbe2535f 100644 --- a/Tests/EmptyDepends/CMakeLists.txt +++ b/Tests/EmptyDepends/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(EmptyDepends) include(CTest) diff --git a/Tests/EmptyLibrary/CMakeLists.txt b/Tests/EmptyLibrary/CMakeLists.txt index baddbbf5e..0c03e1df7 100644 --- a/Tests/EmptyLibrary/CMakeLists.txt +++ b/Tests/EmptyLibrary/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(TestEmptyLibrary) add_subdirectory(subdir) diff --git a/Tests/EnforceConfig.cmake.in b/Tests/EnforceConfig.cmake.in index a652efcd0..f151dfd6f 100644 --- a/Tests/EnforceConfig.cmake.in +++ b/Tests/EnforceConfig.cmake.in @@ -38,5 +38,6 @@ unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS}) # Verify that our module implementations do not recurse too much. set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} 100) +@TEST_CONFIG_ENV_CODE@ @TEST_HOME_ENV_CODE@ @TEST_WARN_VS_CODE@ diff --git a/Tests/Environment/CMakeLists.txt b/Tests/Environment/CMakeLists.txt index abcf33c53..8a03548f0 100644 --- a/Tests/Environment/CMakeLists.txt +++ b/Tests/Environment/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(EnvironmentProj) add_executable(Environment main.cxx) diff --git a/Tests/ExportImport/CMakeLists.txt b/Tests/ExportImport/CMakeLists.txt index e3f32c2cf..82b21c4f1 100644 --- a/Tests/ExportImport/CMakeLists.txt +++ b/Tests/ExportImport/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(ExportImport C CXX) if(NOT DEFINED CMake_TEST_NESTED_MAKE_PROGRAM AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") set(CMake_TEST_NESTED_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM}") diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index 28118bae9..2cc235469 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_DEPENDENCIES "1942b4fa-b2c5-4546-9385-83f254070067") -cmake_minimum_required (VERSION 2.7.20090711) +cmake_minimum_required(VERSION 2.8.11) # old enough to not set CMP0022 if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() @@ -482,6 +482,9 @@ add_library(testMod1 MODULE empty.cpp) add_library(testMod2 MODULE empty.cpp) set_property(TARGET testMod2 PROPERTY BUNDLE 1) +add_library(testSharedLibArchiveAIX SHARED testSharedLibArchiveAIX.c) +set_property(TARGET testSharedLibArchiveAIX PROPERTY AIX_SHARED_LIBRARY_ARCHIVE 1) + install(TARGETS testLibRequired EXPORT RequiredExp DESTINATION lib INCLUDES DESTINATION @@ -622,6 +625,7 @@ install( systemlib testInterfaceIncludeUser testInterfaceIncludeUser2 + testSharedLibArchiveAIX EXPORT exp RUNTIME DESTINATION $<1:bin>$<0:/wrong> LIBRARY DESTINATION $<1:lib>$<0:/wrong> NAMELINK_SKIP @@ -699,6 +703,7 @@ export(TARGETS testExe2 testLib4 testLib5 testLib6 testLib7 testExe3 testExe4 te testExeWithPluginHelper testExePluginHelperObj testMod1 testMod2 testLibPerConfigDest + testSharedLibArchiveAIX NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake ) diff --git a/Tests/ExportImport/Export/testSharedLibArchiveAIX.c b/Tests/ExportImport/Export/testSharedLibArchiveAIX.c new file mode 100644 index 000000000..25a29e002 --- /dev/null +++ b/Tests/ExportImport/Export/testSharedLibArchiveAIX.c @@ -0,0 +1,10 @@ +#if defined(_WIN32) || defined(__CYGWIN__) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT +#endif + +EXPORT int testSharedLibArchiveAIX(void) +{ + return 0; +} diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 632825d80..84a50a668 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -68,6 +68,7 @@ target_link_libraries(imp_testExe1 exp_testLib7 exp_testLibCycleA exp_testLibPerConfigDest + exp_testSharedLibArchiveAIX exp_testStaticLibWithPlugin ) @@ -123,6 +124,7 @@ target_link_libraries(imp_testExe1b bld_testLib7 bld_testLibCycleA bld_testLibPerConfigDest + bld_testSharedLibArchiveAIX bld_testStaticLibWithPlugin ) diff --git a/Tests/ExportImport/Import/A/imp_testExe1.c b/Tests/ExportImport/Import/A/imp_testExe1.c index e409b1c34..7f30c5dad 100644 --- a/Tests/ExportImport/Import/A/imp_testExe1.c +++ b/Tests/ExportImport/Import/A/imp_testExe1.c @@ -11,6 +11,7 @@ extern int testLib7(void); extern int testLibCycleA1(void); extern int testLibPerConfigDest(void); extern int testStaticLibPlugin(void); +extern int testSharedLibArchiveAIX(void); /* Switch a symbol between debug and optimized builds to make sure the proper library is found from the testLib4 link interface. */ @@ -26,6 +27,6 @@ int main(void) return (testLib2() + generated_by_testExe1() + testLib3() + testLib4() + testLib5() + testLib6() + testLib7() + testLibCycleA1() + testLibPerConfigDest() + testStaticLibPlugin() + - generated_by_testExe3() + generated_by_testExe4() + testLib4lib() + - testLib4libcfg()); + testSharedLibArchiveAIX() + generated_by_testExe3() + + generated_by_testExe4() + testLib4lib() + testLib4libcfg()); } diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt index 83c87a8d5..f3fff532d 100644 --- a/Tests/ExportImport/Import/CMakeLists.txt +++ b/Tests/ExportImport/Import/CMakeLists.txt @@ -1,6 +1,5 @@ -cmake_minimum_required (VERSION 2.7.20090711) -cmake_policy(SET CMP0025 NEW) -cmake_policy(SET CMP0028 NEW) +cmake_minimum_required (VERSION 3.10) +cmake_policy(SET CMP0043 OLD) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/ExportImport/Import/try_compile/CMakeLists.txt b/Tests/ExportImport/Import/try_compile/CMakeLists.txt index bb390f927..7c3068be5 100644 --- a/Tests/ExportImport/Import/try_compile/CMakeLists.txt +++ b/Tests/ExportImport/Import/try_compile/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) find_package(testLibRequired 2.5 REQUIRED) diff --git a/Tests/ExternalOBJ/CMakeLists.txt b/Tests/ExternalOBJ/CMakeLists.txt index 141977c81..9d8a240da 100644 --- a/Tests/ExternalOBJ/CMakeLists.txt +++ b/Tests/ExternalOBJ/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (ExternalOBJ) if(APPLE) diff --git a/Tests/ExternalOBJ/Object/CMakeLists.txt b/Tests/ExternalOBJ/Object/CMakeLists.txt index a886da018..962451136 100644 --- a/Tests/ExternalOBJ/Object/CMakeLists.txt +++ b/Tests/ExternalOBJ/Object/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(Object) if(APPLE) # set _CMAKE_OSX_MACHINE to umame -m diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt index d1ab7ace7..3d0067319 100644 --- a/Tests/ExternalProject/CMakeLists.txt +++ b/Tests/ExternalProject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/ExternalProject/Example/CMakeLists.txt b/Tests/ExternalProject/Example/CMakeLists.txt index b6785a619..569402c36 100644 --- a/Tests/ExternalProject/Example/CMakeLists.txt +++ b/Tests/ExternalProject/Example/CMakeLists.txt @@ -1,5 +1,5 @@ # This is the canonical simplest ExternalProject example CMakeLists.txt file: -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectExample NONE) include(ExternalProject) diff --git a/Tests/ExternalProjectLocal/CMakeLists.txt b/Tests/ExternalProjectLocal/CMakeLists.txt index 2dfc4252f..f9abe11be 100644 --- a/Tests/ExternalProjectLocal/CMakeLists.txt +++ b/Tests/ExternalProjectLocal/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectLocalTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/ExternalProjectLocal/Step5/CMakeLists.txt b/Tests/ExternalProjectLocal/Step5/CMakeLists.txt index 93b3880f0..bde264123 100644 --- a/Tests/ExternalProjectLocal/Step5/CMakeLists.txt +++ b/Tests/ExternalProjectLocal/Step5/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (Tutorial) # The version number. diff --git a/Tests/ExternalProjectSourceSubdir/CMakeLists.txt b/Tests/ExternalProjectSourceSubdir/CMakeLists.txt index 4688acfb0..1245c0dbd 100644 --- a/Tests/ExternalProjectSourceSubdir/CMakeLists.txt +++ b/Tests/ExternalProjectSourceSubdir/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectSourceSubdir NONE) include(ExternalProject) diff --git a/Tests/ExternalProjectSourceSubdir/Example/subdir/CMakeLists.txt b/Tests/ExternalProjectSourceSubdir/Example/subdir/CMakeLists.txt index bbc3ca0a2..f6ce1a765 100644 --- a/Tests/ExternalProjectSourceSubdir/Example/subdir/CMakeLists.txt +++ b/Tests/ExternalProjectSourceSubdir/Example/subdir/CMakeLists.txt @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(empty) diff --git a/Tests/ExternalProjectSourceSubdirNotCMake/CMakeLists.txt b/Tests/ExternalProjectSourceSubdirNotCMake/CMakeLists.txt index f64df1a77..43e6efbd8 100644 --- a/Tests/ExternalProjectSourceSubdirNotCMake/CMakeLists.txt +++ b/Tests/ExternalProjectSourceSubdirNotCMake/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectSourceSubdirNotCMake NONE) include(ExternalProject) diff --git a/Tests/ExternalProjectSubdir/CMakeLists.txt b/Tests/ExternalProjectSubdir/CMakeLists.txt index 3a67ea018..8f09f3ecd 100644 --- a/Tests/ExternalProjectSubdir/CMakeLists.txt +++ b/Tests/ExternalProjectSubdir/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectSubdir NONE) include(ExternalProject) diff --git a/Tests/ExternalProjectSubdir/Subdir1/CMakeLists.txt b/Tests/ExternalProjectSubdir/Subdir1/CMakeLists.txt index 2303c3e88..b12ee8df4 100644 --- a/Tests/ExternalProjectSubdir/Subdir1/CMakeLists.txt +++ b/Tests/ExternalProjectSubdir/Subdir1/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) project(Subdir1 NONE) if(NOT "${NORMAL_VAR}" STREQUAL "NORMAL_VALUE") diff --git a/Tests/ExternalProjectUpdate/CMakeLists.txt b/Tests/ExternalProjectUpdate/CMakeLists.txt index e8c67b819..cbddd2f96 100644 --- a/Tests/ExternalProjectUpdate/CMakeLists.txt +++ b/Tests/ExternalProjectUpdate/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExternalProjectUpdateTest NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) cmake_policy(SET CMP0114 NEW) diff --git a/Tests/FindBLAS/Test/CMakeLists.txt b/Tests/FindBLAS/Test/CMakeLists.txt index 9909d2ea3..3183776f2 100644 --- a/Tests/FindBLAS/Test/CMakeLists.txt +++ b/Tests/FindBLAS/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindBLAS C) include(CTest) diff --git a/Tests/FindBZip2/Test/CMakeLists.txt b/Tests/FindBZip2/Test/CMakeLists.txt index 136b3fdab..281339a23 100644 --- a/Tests/FindBZip2/Test/CMakeLists.txt +++ b/Tests/FindBZip2/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindBZip2 C) include(CTest) diff --git a/Tests/FindBoost/Test/CMakeLists.txt b/Tests/FindBoost/Test/CMakeLists.txt index 39fa53206..0ee439a16 100644 --- a/Tests/FindBoost/Test/CMakeLists.txt +++ b/Tests/FindBoost/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindBoost CXX) include(CTest) diff --git a/Tests/FindBoost/TestFail/CMakeLists.txt b/Tests/FindBoost/TestFail/CMakeLists.txt index 3db1a4f4d..b65991dca 100644 --- a/Tests/FindBoost/TestFail/CMakeLists.txt +++ b/Tests/FindBoost/TestFail/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindBoost CXX) include(CTest) diff --git a/Tests/FindBoost/TestHeaders/CMakeLists.txt b/Tests/FindBoost/TestHeaders/CMakeLists.txt index 7dd12e9bb..db55db4b9 100644 --- a/Tests/FindBoost/TestHeaders/CMakeLists.txt +++ b/Tests/FindBoost/TestHeaders/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindBoostHeaders CXX) include(CTest) diff --git a/Tests/FindBoost/TestPython/CMakeLists.txt b/Tests/FindBoost/TestPython/CMakeLists.txt index 494c9a3c6..166a411b0 100644 --- a/Tests/FindBoost/TestPython/CMakeLists.txt +++ b/Tests/FindBoost/TestPython/CMakeLists.txt @@ -2,10 +2,10 @@ cmake_minimum_required(VERSION 3.14) project(TestFindBoostPython CXX) include(CTest) -find_package(Boost OPTIONAL_COMPONENTS python27 python34 python35 python36 python37 python38 python39 python310 python311 python312) +find_package(Boost OPTIONAL_COMPONENTS python27 python34 python35 python36 python37 python38 python39 python310 python311 python312 python313 python314) set(FAILTEST TRUE) -foreach (v IN ITEMS 27 34 35 36 37 38 39 310 311 312) +foreach (v IN ITEMS 27 34 35 36 37 38 39 310 311 312 313 314) if (Boost_PYTHON${v}_FOUND) set(FAILTEST FALSE) break() diff --git a/Tests/FindDevIL/Test/CMakeLists.txt b/Tests/FindDevIL/Test/CMakeLists.txt index db80ccb85..2b72cf8bf 100644 --- a/Tests/FindDevIL/Test/CMakeLists.txt +++ b/Tests/FindDevIL/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindDevIL C) include(CTest) diff --git a/Tests/FindDoxygen/DotComponentTestTest/CMakeLists.txt b/Tests/FindDoxygen/DotComponentTestTest/CMakeLists.txt index 586f0ff66..7fa818fad 100644 --- a/Tests/FindDoxygen/DotComponentTestTest/CMakeLists.txt +++ b/Tests/FindDoxygen/DotComponentTestTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindDoxygenDot VERSION 1.0 LANGUAGES NONE) # Testing a new signature w/ components diff --git a/Tests/FindDoxygen/SimpleTest/CMakeLists.txt b/Tests/FindDoxygen/SimpleTest/CMakeLists.txt index deec4fd89..9e43bf7f7 100644 --- a/Tests/FindDoxygen/SimpleTest/CMakeLists.txt +++ b/Tests/FindDoxygen/SimpleTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindDoxygen VERSION 1.0 LANGUAGES NONE) # Testing backward compatible signature diff --git a/Tests/FindDoxygen/StampFile/CMakeLists.txt b/Tests/FindDoxygen/StampFile/CMakeLists.txt index ed2bfbb39..b3616493d 100644 --- a/Tests/FindDoxygen/StampFile/CMakeLists.txt +++ b/Tests/FindDoxygen/StampFile/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindDoxygen VERSION 1.0 LANGUAGES NONE) find_package(Doxygen REQUIRED) diff --git a/Tests/FindEXPAT/Test/CMakeLists.txt b/Tests/FindEXPAT/Test/CMakeLists.txt index 5681f7471..a77545d07 100644 --- a/Tests/FindEXPAT/Test/CMakeLists.txt +++ b/Tests/FindEXPAT/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(TestFindEXPAT C) include(CTest) diff --git a/Tests/FindFreetype/Test/CMakeLists.txt b/Tests/FindFreetype/Test/CMakeLists.txt index bc869a13f..857777e75 100644 --- a/Tests/FindFreetype/Test/CMakeLists.txt +++ b/Tests/FindFreetype/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(TestFindFreetype C) include(CTest) diff --git a/Tests/FindGIF/Test/CMakeLists.txt b/Tests/FindGIF/Test/CMakeLists.txt index eb0291e99..730efee48 100644 --- a/Tests/FindGIF/Test/CMakeLists.txt +++ b/Tests/FindGIF/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindGIF C) include(CTest) diff --git a/Tests/FindGLEW/Test/CMakeLists.txt b/Tests/FindGLEW/Test/CMakeLists.txt index de8d97ded..b16f7b6e2 100644 --- a/Tests/FindGLEW/Test/CMakeLists.txt +++ b/Tests/FindGLEW/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindGLEW LANGUAGES CXX) include(CTest) diff --git a/Tests/FindGSL/rng/CMakeLists.txt b/Tests/FindGSL/rng/CMakeLists.txt index 497b6c385..463dab5bd 100644 --- a/Tests/FindGSL/rng/CMakeLists.txt +++ b/Tests/FindGSL/rng/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FindGSL_rng CXX) include(CTest) diff --git a/Tests/FindGTK2/atk/CMakeLists.txt b/Tests/FindGTK2/atk/CMakeLists.txt index b80c6fca9..6a754a44a 100644 --- a/Tests/FindGTK2/atk/CMakeLists.txt +++ b/Tests/FindGTK2/atk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(atk C) diff --git a/Tests/FindGTK2/atkmm/CMakeLists.txt b/Tests/FindGTK2/atkmm/CMakeLists.txt index cea87cdb3..53d2dc694 100644 --- a/Tests/FindGTK2/atkmm/CMakeLists.txt +++ b/Tests/FindGTK2/atkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(atkmm CXX) diff --git a/Tests/FindGTK2/cairo/CMakeLists.txt b/Tests/FindGTK2/cairo/CMakeLists.txt index 42d371a24..3b6b419d7 100644 --- a/Tests/FindGTK2/cairo/CMakeLists.txt +++ b/Tests/FindGTK2/cairo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(cairo C) diff --git a/Tests/FindGTK2/cairomm/CMakeLists.txt b/Tests/FindGTK2/cairomm/CMakeLists.txt index 5d957eede..4a983cc39 100644 --- a/Tests/FindGTK2/cairomm/CMakeLists.txt +++ b/Tests/FindGTK2/cairomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(cairomm CXX) diff --git a/Tests/FindGTK2/gdk/CMakeLists.txt b/Tests/FindGTK2/gdk/CMakeLists.txt index 2b8f533b4..4d7c6028c 100644 --- a/Tests/FindGTK2/gdk/CMakeLists.txt +++ b/Tests/FindGTK2/gdk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gdk C) diff --git a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt index 3524f065d..6c90e5b82 100644 --- a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt +++ b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gdk_pixbuf C) diff --git a/Tests/FindGTK2/gdkmm/CMakeLists.txt b/Tests/FindGTK2/gdkmm/CMakeLists.txt index be1ccebf9..67338b6b5 100644 --- a/Tests/FindGTK2/gdkmm/CMakeLists.txt +++ b/Tests/FindGTK2/gdkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gdkmm CXX) diff --git a/Tests/FindGTK2/gio/CMakeLists.txt b/Tests/FindGTK2/gio/CMakeLists.txt index b420f48d2..4abc53621 100644 --- a/Tests/FindGTK2/gio/CMakeLists.txt +++ b/Tests/FindGTK2/gio/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gio C) diff --git a/Tests/FindGTK2/giomm/CMakeLists.txt b/Tests/FindGTK2/giomm/CMakeLists.txt index bae34ffa6..e51be2a8c 100644 --- a/Tests/FindGTK2/giomm/CMakeLists.txt +++ b/Tests/FindGTK2/giomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(giomm CXX) diff --git a/Tests/FindGTK2/glib/CMakeLists.txt b/Tests/FindGTK2/glib/CMakeLists.txt index 8efde3d00..8133a8bdd 100644 --- a/Tests/FindGTK2/glib/CMakeLists.txt +++ b/Tests/FindGTK2/glib/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(glib C) diff --git a/Tests/FindGTK2/glibmm/CMakeLists.txt b/Tests/FindGTK2/glibmm/CMakeLists.txt index f0785dc1a..293eb9eba 100644 --- a/Tests/FindGTK2/glibmm/CMakeLists.txt +++ b/Tests/FindGTK2/glibmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(glibmm CXX) diff --git a/Tests/FindGTK2/gmodule/CMakeLists.txt b/Tests/FindGTK2/gmodule/CMakeLists.txt index 9c686a679..ae6f55362 100644 --- a/Tests/FindGTK2/gmodule/CMakeLists.txt +++ b/Tests/FindGTK2/gmodule/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gmodule C) diff --git a/Tests/FindGTK2/gobject/CMakeLists.txt b/Tests/FindGTK2/gobject/CMakeLists.txt index 83d954640..5330801a8 100644 --- a/Tests/FindGTK2/gobject/CMakeLists.txt +++ b/Tests/FindGTK2/gobject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gobject C) diff --git a/Tests/FindGTK2/gthread/CMakeLists.txt b/Tests/FindGTK2/gthread/CMakeLists.txt index d97585c48..2ffb8ba0b 100644 --- a/Tests/FindGTK2/gthread/CMakeLists.txt +++ b/Tests/FindGTK2/gthread/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gthread C) diff --git a/Tests/FindGTK2/gtk/CMakeLists.txt b/Tests/FindGTK2/gtk/CMakeLists.txt index 2b5a56dd4..d9ef34a19 100644 --- a/Tests/FindGTK2/gtk/CMakeLists.txt +++ b/Tests/FindGTK2/gtk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gtk C) diff --git a/Tests/FindGTK2/gtkmm/CMakeLists.txt b/Tests/FindGTK2/gtkmm/CMakeLists.txt index 179f5dbf8..31c04371c 100644 --- a/Tests/FindGTK2/gtkmm/CMakeLists.txt +++ b/Tests/FindGTK2/gtkmm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(gtkmm CXX) diff --git a/Tests/FindGTK2/pango/CMakeLists.txt b/Tests/FindGTK2/pango/CMakeLists.txt index e9d6b9d9c..c8d492ce9 100644 --- a/Tests/FindGTK2/pango/CMakeLists.txt +++ b/Tests/FindGTK2/pango/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(pango C) diff --git a/Tests/FindGTK2/pangocairo/CMakeLists.txt b/Tests/FindGTK2/pangocairo/CMakeLists.txt index bbb1f279f..8e4a880d6 100644 --- a/Tests/FindGTK2/pangocairo/CMakeLists.txt +++ b/Tests/FindGTK2/pangocairo/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(pangocairo C) diff --git a/Tests/FindGTK2/pangoft2/CMakeLists.txt b/Tests/FindGTK2/pangoft2/CMakeLists.txt index 801629cd2..d8a24afa6 100644 --- a/Tests/FindGTK2/pangoft2/CMakeLists.txt +++ b/Tests/FindGTK2/pangoft2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(pangoft2 C) diff --git a/Tests/FindGTK2/pangomm/CMakeLists.txt b/Tests/FindGTK2/pangomm/CMakeLists.txt index 853a1ddd2..68a78bc93 100644 --- a/Tests/FindGTK2/pangomm/CMakeLists.txt +++ b/Tests/FindGTK2/pangomm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(pangomm CXX) diff --git a/Tests/FindGTK2/pangoxft/CMakeLists.txt b/Tests/FindGTK2/pangoxft/CMakeLists.txt index f267d6c42..7074fa712 100644 --- a/Tests/FindGTK2/pangoxft/CMakeLists.txt +++ b/Tests/FindGTK2/pangoxft/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(pangoxft C) diff --git a/Tests/FindGTK2/sigc++/CMakeLists.txt b/Tests/FindGTK2/sigc++/CMakeLists.txt index f09ea66f1..ff1f5be6c 100644 --- a/Tests/FindGTK2/sigc++/CMakeLists.txt +++ b/Tests/FindGTK2/sigc++/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(sigc++ CXX) diff --git a/Tests/FindGTest/Test/CMakeLists.txt b/Tests/FindGTest/Test/CMakeLists.txt index 582cbaa19..d957f7c5c 100644 --- a/Tests/FindGTest/Test/CMakeLists.txt +++ b/Tests/FindGTest/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindGTest CXX) include(CTest) diff --git a/Tests/FindGnuTLS/Test/CMakeLists.txt b/Tests/FindGnuTLS/Test/CMakeLists.txt index e17987daa..2a012989e 100644 --- a/Tests/FindGnuTLS/Test/CMakeLists.txt +++ b/Tests/FindGnuTLS/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindGnuTLS C) include(CTest) diff --git a/Tests/FindICU/Test/CMakeLists.txt b/Tests/FindICU/Test/CMakeLists.txt index 066d1733d..4beb6a08e 100644 --- a/Tests/FindICU/Test/CMakeLists.txt +++ b/Tests/FindICU/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindICU LANGUAGES CXX) include(CTest) diff --git a/Tests/FindImageMagick/Test/CMakeLists.txt b/Tests/FindImageMagick/Test/CMakeLists.txt index 4e4f14dbb..6bbb373f9 100644 --- a/Tests/FindImageMagick/Test/CMakeLists.txt +++ b/Tests/FindImageMagick/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FindImageMagick C CXX) include(CTest) diff --git a/Tests/FindJPEG/Test/CMakeLists.txt b/Tests/FindJPEG/Test/CMakeLists.txt index 390b3a747..1765e2588 100644 --- a/Tests/FindJPEG/Test/CMakeLists.txt +++ b/Tests/FindJPEG/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindJPEG C) include(CTest) diff --git a/Tests/FindJsonCpp/Test/CMakeLists.txt b/Tests/FindJsonCpp/Test/CMakeLists.txt index 5f100ea5c..7bb55593f 100644 --- a/Tests/FindJsonCpp/Test/CMakeLists.txt +++ b/Tests/FindJsonCpp/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindJsonCpp CXX) include(CTest) diff --git a/Tests/FindLAPACK/Test/CMakeLists.txt b/Tests/FindLAPACK/Test/CMakeLists.txt index ce9af9851..55cd1ee07 100644 --- a/Tests/FindLAPACK/Test/CMakeLists.txt +++ b/Tests/FindLAPACK/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindLAPACK C) include(CTest) diff --git a/Tests/FindLTTngUST/Test/CMakeLists.txt b/Tests/FindLTTngUST/Test/CMakeLists.txt index e5bd9f366..c9084d747 100644 --- a/Tests/FindLTTngUST/Test/CMakeLists.txt +++ b/Tests/FindLTTngUST/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindLTTngUST C) include(CTest) diff --git a/Tests/FindLibLZMA/Test/CMakeLists.txt b/Tests/FindLibLZMA/Test/CMakeLists.txt index 438938e54..ae8d340b7 100644 --- a/Tests/FindLibLZMA/Test/CMakeLists.txt +++ b/Tests/FindLibLZMA/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindLZMA C) include(CTest) diff --git a/Tests/FindLibRHash/Test/CMakeLists.txt b/Tests/FindLibRHash/Test/CMakeLists.txt index 37e062ae9..055b00250 100644 --- a/Tests/FindLibRHash/Test/CMakeLists.txt +++ b/Tests/FindLibRHash/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(TestFindLibRHash C) include(CTest) diff --git a/Tests/FindLibUV/Test/CMakeLists.txt b/Tests/FindLibUV/Test/CMakeLists.txt index 257ddf3cc..3d5f129ec 100644 --- a/Tests/FindLibUV/Test/CMakeLists.txt +++ b/Tests/FindLibUV/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(TestFindLibUV C) include(CTest) diff --git a/Tests/FindLibXml2/Test/CMakeLists.txt b/Tests/FindLibXml2/Test/CMakeLists.txt index 9b866ab08..3551daff2 100644 --- a/Tests/FindLibXml2/Test/CMakeLists.txt +++ b/Tests/FindLibXml2/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindLibXml2 C) include(CTest) diff --git a/Tests/FindLibXslt/Test/CMakeLists.txt b/Tests/FindLibXslt/Test/CMakeLists.txt index 32a157f66..0f2350019 100644 --- a/Tests/FindLibXslt/Test/CMakeLists.txt +++ b/Tests/FindLibXslt/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindLibXslt C) include(CTest) diff --git a/Tests/FindMPI/Test/CMakeLists.txt b/Tests/FindMPI/Test/CMakeLists.txt index efe8c6261..cb347ade6 100644 --- a/Tests/FindMPI/Test/CMakeLists.txt +++ b/Tests/FindMPI/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindMPI NONE) include(CTest) diff --git a/Tests/FindMatlab/basic_checks/CMakeLists.txt b/Tests/FindMatlab/basic_checks/CMakeLists.txt index e9b696c76..c1a813cd5 100644 --- a/Tests/FindMatlab/basic_checks/CMakeLists.txt +++ b/Tests/FindMatlab/basic_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(basic_checks) diff --git a/Tests/FindMatlab/components_checks/CMakeLists.txt b/Tests/FindMatlab/components_checks/CMakeLists.txt index efb99aefa..af952ca87 100644 --- a/Tests/FindMatlab/components_checks/CMakeLists.txt +++ b/Tests/FindMatlab/components_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(component_checks) diff --git a/Tests/FindMatlab/failure_reports/CMakeLists.txt b/Tests/FindMatlab/failure_reports/CMakeLists.txt index 45e48d7d2..fd94938d4 100644 --- a/Tests/FindMatlab/failure_reports/CMakeLists.txt +++ b/Tests/FindMatlab/failure_reports/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(failure_reports) diff --git a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt index 7e1bbaeab..b4572e8f8 100644 --- a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt +++ b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(no_implicit_links_checks) diff --git a/Tests/FindMatlab/r2018a_check/CMakeLists.txt b/Tests/FindMatlab/r2018a_check/CMakeLists.txt index 8b2188871..610a647c5 100644 --- a/Tests/FindMatlab/r2018a_check/CMakeLists.txt +++ b/Tests/FindMatlab/r2018a_check/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(r2018a_checks) diff --git a/Tests/FindMatlab/targets_checks/CMakeLists.txt b/Tests/FindMatlab/targets_checks/CMakeLists.txt index c709bbd90..ee7267ecf 100644 --- a/Tests/FindMatlab/targets_checks/CMakeLists.txt +++ b/Tests/FindMatlab/targets_checks/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(targets_checks) diff --git a/Tests/FindMatlab/versions_checks/CMakeLists.txt b/Tests/FindMatlab/versions_checks/CMakeLists.txt index 9a9e9a3ca..6c44d623c 100644 --- a/Tests/FindMatlab/versions_checks/CMakeLists.txt +++ b/Tests/FindMatlab/versions_checks/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) enable_testing() project(versions_checks) diff --git a/Tests/FindODBC/Test/CMakeLists.txt b/Tests/FindODBC/Test/CMakeLists.txt index 1e3a2390d..7131945d6 100644 --- a/Tests/FindODBC/Test/CMakeLists.txt +++ b/Tests/FindODBC/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindODBC C) include(CTest) diff --git a/Tests/FindOpenCL/Test/CMakeLists.txt b/Tests/FindOpenCL/Test/CMakeLists.txt index 4a1f6bde6..f08d4e579 100644 --- a/Tests/FindOpenCL/Test/CMakeLists.txt +++ b/Tests/FindOpenCL/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindOpenCL C) include(CTest) diff --git a/Tests/FindOpenMP/CMakeLists.txt b/Tests/FindOpenMP/CMakeLists.txt index e64885d09..c8d684aee 100644 --- a/Tests/FindOpenMP/CMakeLists.txt +++ b/Tests/FindOpenMP/CMakeLists.txt @@ -1,4 +1,4 @@ -foreach(c C CXX Fortran) +foreach(c C CXX CUDA Fortran) if(CMake_TEST_FindOpenMP_${c}) set(CMake_TEST_FindOpenMP_FLAG_${c} 1) else() @@ -16,6 +16,13 @@ add_test(NAME FindOpenMP.Test COMMAND --build-options ${build_options} -DOpenMP_TEST_C=${CMake_TEST_FindOpenMP_FLAG_C} -DOpenMP_TEST_CXX=${CMake_TEST_FindOpenMP_FLAG_CXX} + -DOpenMP_TEST_CUDA=${CMake_TEST_FindOpenMP_FLAG_CUDA} -DOpenMP_TEST_Fortran=${CMake_TEST_FindOpenMP_FLAG_Fortran} --test-command ${CMAKE_CTEST_COMMAND} -V -C $ ) +if(CMake_TEST_FindOpenMP_FLAG_CUDA) + set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "CUDA") +endif() +if(CMake_TEST_FindOpenMP_FLAG_Fortran) + set_property(TEST FindOpenMP.Test APPEND PROPERTY LABELS "Fortran") +endif() diff --git a/Tests/FindOpenMP/Test/CMakeLists.txt b/Tests/FindOpenMP/Test/CMakeLists.txt index 7ead83505..7b05372e5 100644 --- a/Tests/FindOpenMP/Test/CMakeLists.txt +++ b/Tests/FindOpenMP/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.30) project(TestFindOpenMP NONE) include(CTest) @@ -8,6 +8,9 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME) elseif("${LANG_NAME}" STREQUAL "CXX") configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cxx" COPYONLY) set(OpenMPTEST_SOURCE_FILE "${SRC_FILE_NAME}.cxx") + elseif("${LANG_NAME}" STREQUAL "CUDA") + configure_file("${SRC_FILE_NAME}.c" "${SRC_FILE_NAME}.cu" COPYONLY) + set(OpenMPTEST_SOURCE_FILE "${SRC_FILE_NAME}.cu") elseif("${LANG_NAME}" STREQUAL "Fortran") set(OpenMPTEST_SOURCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SRC_FILE_NAME}.f90") if(OpenMP_Fortran_HAVE_OMPLIB_MODULE) @@ -19,7 +22,7 @@ macro(source_code_mapper_helper LANG_NAME SRC_FILE_NAME) endif() endmacro() -foreach(c C CXX Fortran) +foreach(c C CXX CUDA Fortran) if("${OpenMP_TEST_${c}}") message("Testing ${c}") enable_language(${c}) @@ -41,7 +44,7 @@ if(test_msvc_runtime) endif() endif() -foreach(c C CXX Fortran) +foreach(c C CXX CUDA Fortran) if(NOT "${OpenMP_TEST_${c}}") continue() endif() @@ -54,7 +57,7 @@ foreach(c C CXX Fortran) add_executable(test_var_${c} ${OpenMPTEST_SOURCE_FILE}) separate_arguments(_OpenMP_${c}_OPTIONS NATIVE_COMMAND "${OpenMP_${c}_FLAGS}") target_compile_options(test_var_${c} PRIVATE "${_OpenMP_${c}_OPTIONS}") - target_link_libraries(test_var_${c} PRIVATE "${OpenMP_${c}_FLAGS}") + target_link_libraries(test_var_${c} PRIVATE ${OpenMP_${c}_LIBRARIES}) target_include_directories(test_var_${c} PRIVATE ${OpenMP_${c}_INCLUDE_DIRS}) set_property(TARGET test_var_${c} PROPERTY LINKER_LANGUAGE ${c}) add_test(NAME test_var_${c} COMMAND test_var_${c}) @@ -65,11 +68,11 @@ foreach(c C CXX Fortran) set_property(TARGET scalprod_${c} PROPERTY LINKER_LANGUAGE ${c}) endforeach() -foreach(c C CXX Fortran) +foreach(c C CXX CUDA Fortran) if(NOT "${OpenMP_TEST_${c}}") continue() endif() - foreach(d C CXX Fortran) + foreach(d C CXX CUDA Fortran) if(NOT "${OpenMP_TEST_${d}}") continue() endif() diff --git a/Tests/FindOpenMP/Test/main.c b/Tests/FindOpenMP/Test/main.c index 9fb67e43d..f60fce8b3 100644 --- a/Tests/FindOpenMP/Test/main.c +++ b/Tests/FindOpenMP/Test/main.c @@ -1,7 +1,10 @@ #include int main(void) { -#ifndef _OPENMP - breaks_on_purpose +#ifdef _OPENMP + omp_get_num_threads(); +#elif !defined(__CUDA_ARCH__) && !defined(__HIP_DEVICE_COMPILE__) +# error "_OPENMP not defined!" #endif + return 0; } diff --git a/Tests/FindOpenSP/Test/CMakeLists.txt b/Tests/FindOpenSP/Test/CMakeLists.txt index 266a459eb..a2b24b4c5 100644 --- a/Tests/FindOpenSP/Test/CMakeLists.txt +++ b/Tests/FindOpenSP/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindOpenSP CXX) include(CTest) diff --git a/Tests/FindOpenSSL/rand/CMakeLists.txt b/Tests/FindOpenSSL/rand/CMakeLists.txt index c4432219f..b3251c6ce 100644 --- a/Tests/FindOpenSSL/rand/CMakeLists.txt +++ b/Tests/FindOpenSSL/rand/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FindOpenSSL_rand CXX) include(CTest) diff --git a/Tests/FindPNG/Test/CMakeLists.txt b/Tests/FindPNG/Test/CMakeLists.txt index 66cdda19f..6cc9ca2d1 100644 --- a/Tests/FindPNG/Test/CMakeLists.txt +++ b/Tests/FindPNG/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindPNG C) include(CTest) diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt index e4143b940..73d3fb483 100644 --- a/Tests/FindPackageTest/CMakeLists.txt +++ b/Tests/FindPackageTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(FindPackageTest) # Protect tests from running inside the default install prefix. @@ -571,6 +571,14 @@ endif() set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE) unset(SortLib_VERSION) + +set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE) +FIND_PACKAGE(SortLib 4.0 CONFIG) +IF (NOT "${SortLib_VERSION}" STREQUAL "4.0.0") + message(SEND_ERROR "FIND_PACKAGE_SORT_ORDER gave up too soon! ${SortLib_VERSION}") +endif() +unset(SortLib_VERSION) + unset(CMAKE_FIND_PACKAGE_SORT_ORDER) unset(CMAKE_FIND_PACKAGE_SORT_DIRECTION) set(CMAKE_PREFIX_PATH ) diff --git a/Tests/FindPackageTest/Exporter/CMakeLists.txt b/Tests/FindPackageTest/Exporter/CMakeLists.txt index d25a2e74c..57cd4c932 100644 --- a/Tests/FindPackageTest/Exporter/CMakeLists.txt +++ b/Tests/FindPackageTest/Exporter/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.7.20090831) +cmake_minimum_required(VERSION 3.10) project(CMakeTestExportPackage C) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeTestExportPackageConfig.cmake.in diff --git a/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfig.cmake b/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfig.cmake new file mode 100644 index 000000000..845e6c7ac --- /dev/null +++ b/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfig.cmake @@ -0,0 +1,2 @@ +set(SORT_LIB_VERSION 4.0.0) +message("SortLib 4.0.0 config reached") diff --git a/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfigVersion.cmake b/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfigVersion.cmake new file mode 100644 index 000000000..b642dabab --- /dev/null +++ b/Tests/FindPackageTest/lib/SortLib-4.0.0/SortLibConfigVersion.cmake @@ -0,0 +1,9 @@ +set(PACKAGE_VERSION 4.0.0) +if(PACKAGE_FIND_VERSION_MAJOR EQUAL 4) + if(PACKAGE_FIND_VERSION_MINOR EQUAL 0) + set(PACKAGE_VERSION_COMPATIBLE 1) + if(PACKAGE_FIND_VERSION_PATCH EQUAL 0) + set(PACKAGE_VERSION_EXACT 1) + endif() + endif() +endif() diff --git a/Tests/FindPatch/Test/CMakeLists.txt b/Tests/FindPatch/Test/CMakeLists.txt index 66c672c55..6e959eab5 100644 --- a/Tests/FindPatch/Test/CMakeLists.txt +++ b/Tests/FindPatch/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindPatch VERSION 1.0 LANGUAGES NONE) macro(_check) diff --git a/Tests/FindProtobuf/Test/CMakeLists.txt b/Tests/FindProtobuf/Test/CMakeLists.txt index 84041eaf4..2859c4897 100644 --- a/Tests/FindProtobuf/Test/CMakeLists.txt +++ b/Tests/FindProtobuf/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestFindProtobuf CXX) include(CTest) @@ -55,8 +55,9 @@ add_test(NAME test_desc COMMAND test_desc ${DESC_PROTO_DESC}) if(CMake_TEST_FindProtobuf_gRPC) find_program(gRPC_CPP_PLUGIN grpc_cpp_plugin) - add_library(msgs_grpc msgs/example_service.proto) - target_include_directories(msgs_grpc PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + add_library(msgs_grpc msgs/grpc/example_service.proto) + # NOTE: by default generated files will be placed under ${CMAKE_CURRENT_BINARY_DIR}/msgs/grpc/ + target_include_directories(msgs_grpc PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/msgs/grpc/) target_link_libraries(msgs_grpc PUBLIC ${Protobuf_LIBRARIES}) protobuf_generate(TARGET msgs_grpc LANGUAGE cpp) protobuf_generate(TARGET msgs_grpc LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${gRPC_CPP_PLUGIN}") @@ -64,4 +65,24 @@ if(CMake_TEST_FindProtobuf_gRPC) add_executable(test_generate_grpc main-generate-grpc.cxx) target_link_libraries(test_generate_grpc PRIVATE msgs_grpc) add_test(NAME test_generate_grpc COMMAND test_generate_grpc) + + add_library(msgs_grpc_IMPORT_DIRS msgs/grpc/example_service.proto) + # NOTE: with IMPORT_DIRS msgs/, generated files will be placed under ${CMAKE_CURRENT_BINARY_DIR}/grpc/ + target_include_directories(msgs_grpc_IMPORT_DIRS PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/grpc/) + target_link_libraries(msgs_grpc_IMPORT_DIRS PUBLIC ${Protobuf_LIBRARIES}) + protobuf_generate(TARGET msgs_grpc_IMPORT_DIRS LANGUAGE cpp IMPORT_DIRS msgs/) + protobuf_generate(TARGET msgs_grpc_IMPORT_DIRS LANGUAGE grpc IMPORT_DIRS msgs/ GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${gRPC_CPP_PLUGIN}") + add_executable(test_generate_grpc_IMPORT_DIRS main-generate-grpc.cxx) + target_link_libraries(test_generate_grpc_IMPORT_DIRS PRIVATE msgs_grpc_IMPORT_DIRS) + add_test(NAME test_generate_grpc_IMPORT_DIRS COMMAND test_generate_grpc_IMPORT_DIRS) + + add_library(msgs_grpc_APPEND_PATH msgs/grpc/example_service.proto) + # NOTE: with APPEND_PATH, generated files will be placed under ${CMAKE_CURRENT_BINARY_DIR}/ + target_include_directories(msgs_grpc_APPEND_PATH PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(msgs_grpc_APPEND_PATH PUBLIC ${Protobuf_LIBRARIES}) + protobuf_generate(TARGET msgs_grpc_APPEND_PATH LANGUAGE cpp APPEND_PATH) + protobuf_generate(TARGET msgs_grpc_APPEND_PATH LANGUAGE grpc APPEND_PATH GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${gRPC_CPP_PLUGIN}") + add_executable(test_generate_grpc_APPEND_PATH main-generate-grpc.cxx) + target_link_libraries(test_generate_grpc_APPEND_PATH PRIVATE msgs_grpc_APPEND_PATH) + add_test(NAME test_generate_grpc_APPEND_PATH COMMAND test_generate_grpc_APPEND_PATH) endif() diff --git a/Tests/FindProtobuf/Test/main-generate-grpc.cxx b/Tests/FindProtobuf/Test/main-generate-grpc.cxx index 070c6b46e..3251f6a20 100644 --- a/Tests/FindProtobuf/Test/main-generate-grpc.cxx +++ b/Tests/FindProtobuf/Test/main-generate-grpc.cxx @@ -1,4 +1,4 @@ -#include +#include int main() { diff --git a/Tests/FindProtobuf/Test/msgs/example_service.proto b/Tests/FindProtobuf/Test/msgs/grpc/example_service.proto similarity index 100% rename from Tests/FindProtobuf/Test/msgs/example_service.proto rename to Tests/FindProtobuf/Test/msgs/grpc/example_service.proto diff --git a/Tests/FindSDL/Test/CMakeLists.txt b/Tests/FindSDL/Test/CMakeLists.txt index 0dd104703..b3d84a123 100644 --- a/Tests/FindSDL/Test/CMakeLists.txt +++ b/Tests/FindSDL/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindSDL C) include(CTest) diff --git a/Tests/FindSQLite3/Test/CMakeLists.txt b/Tests/FindSQLite3/Test/CMakeLists.txt index 43cdd37fa..95b84824b 100644 --- a/Tests/FindSQLite3/Test/CMakeLists.txt +++ b/Tests/FindSQLite3/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindSQLite3 C) include(CTest) diff --git a/Tests/FindTIFF/Test/CMakeLists.txt b/Tests/FindTIFF/Test/CMakeLists.txt index 54404d0a9..c734b253a 100644 --- a/Tests/FindTIFF/Test/CMakeLists.txt +++ b/Tests/FindTIFF/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindTIFF) include(CTest) diff --git a/Tests/FindThreads/C-only/CMakeLists.txt b/Tests/FindThreads/C-only/CMakeLists.txt index 9bc50a48d..5f6308780 100644 --- a/Tests/FindThreads/C-only/CMakeLists.txt +++ b/Tests/FindThreads/C-only/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) project(FindThreads_C-only C) find_package(Threads REQUIRED) diff --git a/Tests/FindThreads/CXX-only/CMakeLists.txt b/Tests/FindThreads/CXX-only/CMakeLists.txt index 11ae60ccb..1c614024c 100644 --- a/Tests/FindThreads/CXX-only/CMakeLists.txt +++ b/Tests/FindThreads/CXX-only/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) project(FindThreads_CXX-only CXX) find_package(Threads REQUIRED) diff --git a/Tests/FindVulkan/Test/CMakeLists.txt b/Tests/FindVulkan/Test/CMakeLists.txt index 7198d2249..520a3ac54 100644 --- a/Tests/FindVulkan/Test/CMakeLists.txt +++ b/Tests/FindVulkan/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0091 NEW) project(TestFindVulkan C CXX) include(CTest) diff --git a/Tests/FindXalanC/Test/CMakeLists.txt b/Tests/FindXalanC/Test/CMakeLists.txt index eb45802aa..11dc08f74 100644 --- a/Tests/FindXalanC/Test/CMakeLists.txt +++ b/Tests/FindXalanC/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindXalanC CXX) include(CTest) diff --git a/Tests/FindXercesC/Test/CMakeLists.txt b/Tests/FindXercesC/Test/CMakeLists.txt index 38ae6e405..60ec2faaf 100644 --- a/Tests/FindXercesC/Test/CMakeLists.txt +++ b/Tests/FindXercesC/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestFindXercesC CXX) include(CTest) diff --git a/Tests/ForceInclude/CMakeLists.txt b/Tests/ForceInclude/CMakeLists.txt index e2310543d..129ced341 100644 --- a/Tests/ForceInclude/CMakeLists.txt +++ b/Tests/ForceInclude/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.3.20110103) +cmake_minimum_required(VERSION 3.10) project(ForceInclude C) # Make sure the proper compiler is in use. diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt index 41d3b4efe..d488efff6 100644 --- a/Tests/Fortran/CMakeLists.txt +++ b/Tests/Fortran/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.1) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() @@ -37,6 +37,10 @@ if(WIN32 AND NOT CYGWIN) endif() endif() +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran") + add_compile_options("$<$:--implicit-interface>") +endif() + add_library(hello STATIC hello.f) add_library(world ${_SHARED} world.f ${world_def}) add_executable(testf testf.f) @@ -49,7 +53,7 @@ function(test_fortran_c_interface_module) FortranCInterface_VERIFY() FortranCInterface_VERIFY(CXX) if(CMAKE_Fortran_COMPILER_SUPPORTS_F90) - if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu|LCC") + if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu|LCC|LFortran") set(module_expected 1) endif() if(FortranCInterface_MODULE_FOUND OR module_expected) @@ -123,7 +127,7 @@ endfunction() # if the id's match or the compilers are compatible, then # call the test_fortran_c_interface_module function if("${CMAKE_Fortran_COMPILER_ID}:${CMAKE_C_COMPILER_ID}" MATCHES - "(Intel(LLVM)?:MSVC|Absoft:GNU|LLVMFlang:(GNU|Clang))" + "(Intel(LLVM)?:MSVC|Absoft:GNU|(LLVMFlang|LFortran):(GNU|Clang))" OR ("${CMAKE_Fortran_COMPILER_ID}" STREQUAL "${CMAKE_C_COMPILER_ID}" )) test_fortran_c_interface_module() else() diff --git a/Tests/FortranC/CMakeLists.txt b/Tests/FortranC/CMakeLists.txt index f5e056b05..0c7d9c320 100644 --- a/Tests/FortranC/CMakeLists.txt +++ b/Tests/FortranC/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FortranC C Fortran) # Skip this test for compilers not known to be compatible. if(NOT (CMAKE_C_COMPILER_ID STREQUAL CMAKE_Fortran_COMPILER_ID OR - "${CMAKE_C_COMPILER_ID}-${CMAKE_Fortran_COMPILER_ID}" MATCHES "^(MSVC-Intel|(GNU|Clang)-LLVMFlang)$")) + "${CMAKE_C_COMPILER_ID}-${CMAKE_Fortran_COMPILER_ID}" MATCHES "^(MSVC-Intel|(GNU|Clang)-(LLVMFlang|LFortran))$")) message(STATUS "${CMAKE_C_COMPILER_ID} C and ${CMAKE_Fortran_COMPILER_ID} Fortran not known to be compatible!") return() endif() diff --git a/Tests/FortranModules/CMakeLists.txt b/Tests/FortranModules/CMakeLists.txt index 61d06d9aa..555c994c6 100644 --- a/Tests/FortranModules/CMakeLists.txt +++ b/Tests/FortranModules/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(FortranModules Fortran) if("${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" MATCHES "^Intel(LLVM)?;MSVC$") diff --git a/Tests/FortranModules/Submodules/obfuscated_parent.f90 b/Tests/FortranModules/Submodules/obfuscated_parent.f90 index f3e68be1e..9368f16cd 100644 --- a/Tests/FortranModules/Submodules/obfuscated_parent.f90 +++ b/Tests/FortranModules/Submodules/obfuscated_parent.f90 @@ -1,8 +1,8 @@ ! This module has two procedures from the "parent" module ! but it has different combinations 'module ' phrases -! in breaked lines for test of modules dependencies detection +! in broken lines for test of modules dependencies detection -! Module declaration on breaked line with reminder +! Module declaration on broken line with reminder module & obfuscated_parent; implicit none diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index 73312a0e4..fa1012b91 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5...3.25) # Enable CMP0091 and CMP0141. +cmake_minimum_required(VERSION 3.10...3.25) # Enable CMP0091 and CMP0141. project(FortranOnly Fortran) message("CTEST_FULL_OUTPUT ") @@ -7,6 +7,10 @@ if("${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" MATCHES "^Intel(L string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO " -Z7") endif() +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran") + add_compile_options(--implicit-interface) +endif() + # create a library with hello and world functions add_library(FortranOnlylib hello.f world.f) set_property(TARGET FortranOnlylib PROPERTY Fortran_FORMAT FIXED) diff --git a/Tests/Framework/CMakeLists.txt b/Tests/Framework/CMakeLists.txt index 629deebe5..1578a7110 100644 --- a/Tests/Framework/CMakeLists.txt +++ b/Tests/Framework/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Framework) add_library(foo SHARED diff --git a/Tests/FunctionTest/CMakeLists.txt b/Tests/FunctionTest/CMakeLists.txt index a5a8b115c..4883a5123 100644 --- a/Tests/FunctionTest/CMakeLists.txt +++ b/Tests/FunctionTest/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (FunctionTest) function(FAILED testname) diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index be750e180..708cfc80b 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.8) +cmake_minimum_required(VERSION 3.10) project(GeneratorExpression) include(CTest) @@ -112,6 +112,15 @@ target_link_libraries(empty3 LINK_PUBLIC empty2 empty4) add_library(empty5 empty.cpp) target_include_directories(empty5 PRIVATE /empty5/private1 /empty5/private2) +add_library(interface1 INTERFACE) +target_sources(interface1 INTERFACE foo.c bar.cpp) + +set(interface1Sources $) +set(interface1MergeSources $,$>) + +add_library(interface2 INTERFACE) +target_sources(interface2 INTERFACE ${interface1MergeSources}) + add_custom_target(check-part2 ALL COMMAND ${msys2_no_conv} ${CMAKE_COMMAND} -Dtest_incomplete_1=$< @@ -147,6 +156,8 @@ add_custom_target(check-part2 ALL -Dtest_target_includes6=$ -Dtest_target_includes7=$ -Dtest_target_includes8=$ + -Dtest_target_closure1=$,$> + -Dtest_target_closure2=$>,$> -Dtest_arbitrary_content_comma_1=$<1:a,> -Dtest_arbitrary_content_comma_2=$<1:,a> -Dtest_arbitrary_content_comma_3=$<1:a,,> @@ -493,3 +504,5 @@ if(NOT CMAKE_GENERATOR STREQUAL Xcode OR NOT CMAKE_OSX_ARCHITECTURES MATCHES "[; DEPENDS execobjs ) endif() + +cmake_policy(VERSION 2.8.12) # old enough to not set CMP0044 diff --git a/Tests/GeneratorExpression/check-part2.cmake b/Tests/GeneratorExpression/check-part2.cmake index a1db5f63d..e9c5e5c06 100644 --- a/Tests/GeneratorExpression/check-part2.cmake +++ b/Tests/GeneratorExpression/check-part2.cmake @@ -34,6 +34,8 @@ check(test_target_includes5 "/empty2/public;/empty3/public;/empty2/public;/empty check(test_target_includes6 "/empty3/public;/empty3/private;/empty2/public;/empty3/public;/empty4/public") check(test_target_includes7 "/empty1/public;/empty2/public;/empty3/public;/empty4/public") check(test_target_includes8 "/empty5/private1;/empty5/private2") +check(test_target_closure1 "bar.cpp,foo.c") +check(test_target_closure2 "bar.cpp,foo.c") check(test_arbitrary_content_comma_1 "a,") check(test_arbitrary_content_comma_2 ",a") check(test_arbitrary_content_comma_3 "a,,") diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/CMakeLists.txt index a1f152f29..4b39c75ed 100644 --- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(test C) add_library(libdemo diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/CMakeLists.txt index 92254e673..cd7ec274e 100644 --- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(test) add_subdirectory(App) diff --git a/Tests/GoogleTest/Test/CMakeLists.txt b/Tests/GoogleTest/Test/CMakeLists.txt index 201c39e15..073549653 100644 --- a/Tests/GoogleTest/Test/CMakeLists.txt +++ b/Tests/GoogleTest/Test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(TestGoogleTest) include(CTest) diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt index 99cad4149..e9c1c6363 100644 --- a/Tests/IncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt index d4720be12..03265d574 100644 --- a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(SystemIncludeDirectories) diff --git a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt index 681226717..cf1f8c594 100644 --- a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt +++ b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TargetIncludeDirectories) diff --git a/Tests/InterfaceLibrary/CMakeLists.txt b/Tests/InterfaceLibrary/CMakeLists.txt index d57eccc44..f6e55784b 100644 --- a/Tests/InterfaceLibrary/CMakeLists.txt +++ b/Tests/InterfaceLibrary/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(InterfaceLibrary) diff --git a/Tests/InterfaceLinkLibraries/CMakeLists.txt b/Tests/InterfaceLinkLibraries/CMakeLists.txt index 07a747b34..5b83b14bb 100644 --- a/Tests/InterfaceLinkLibraries/CMakeLists.txt +++ b/Tests/InterfaceLinkLibraries/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0022 NEW) diff --git a/Tests/JCTest/CMakeLists.txt b/Tests/JCTest/CMakeLists.txt index adbdf9a33..e4d968e30 100644 --- a/Tests/JCTest/CMakeLists.txt +++ b/Tests/JCTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestTime) enable_testing() add_executable(TestTime TestTime.cxx) diff --git a/Tests/Java/CMakeLists.txt b/Tests/Java/CMakeLists.txt index c1c68177c..702aedcf4 100644 --- a/Tests/Java/CMakeLists.txt +++ b/Tests/Java/CMakeLists.txt @@ -1,6 +1,6 @@ project(hello Java) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) include(CTest) diff --git a/Tests/JavaExportImport/BuildExport/CMakeLists.txt b/Tests/JavaExportImport/BuildExport/CMakeLists.txt index fa7e5016d..deba5ba4d 100644 --- a/Tests/JavaExportImport/BuildExport/CMakeLists.txt +++ b/Tests/JavaExportImport/BuildExport/CMakeLists.txt @@ -1,6 +1,6 @@ project(foo Java) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) find_package(Java COMPONENTS Development) diff --git a/Tests/JavaExportImport/CMakeLists.txt b/Tests/JavaExportImport/CMakeLists.txt index 7a2d020a1..c7fa3ecbc 100644 --- a/Tests/JavaExportImport/CMakeLists.txt +++ b/Tests/JavaExportImport/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(JavaExportImport) if(NOT DEFINED CMake_TEST_NESTED_MAKE_PROGRAM AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") set(CMake_TEST_NESTED_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM}") diff --git a/Tests/JavaExportImport/Import/CMakeLists.txt b/Tests/JavaExportImport/Import/CMakeLists.txt index 13ec05dc4..182d97f67 100644 --- a/Tests/JavaExportImport/Import/CMakeLists.txt +++ b/Tests/JavaExportImport/Import/CMakeLists.txt @@ -1,6 +1,6 @@ project(import Java) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) find_package(Java COMPONENTS Development) diff --git a/Tests/JavaExportImport/InstallExport/CMakeLists.txt b/Tests/JavaExportImport/InstallExport/CMakeLists.txt index 2923bebb2..6b82f1718 100644 --- a/Tests/JavaExportImport/InstallExport/CMakeLists.txt +++ b/Tests/JavaExportImport/InstallExport/CMakeLists.txt @@ -1,6 +1,6 @@ project(bar Java) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) find_package(Java COMPONENTS Development) diff --git a/Tests/JavaJavah/CMakeLists.txt b/Tests/JavaJavah/CMakeLists.txt index 06fc06a88..c98cf97fe 100644 --- a/Tests/JavaJavah/CMakeLists.txt +++ b/Tests/JavaJavah/CMakeLists.txt @@ -1,6 +1,6 @@ project(helloJavah Java CXX) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) include(CTest) diff --git a/Tests/JavaNativeHeaders/CMakeLists.txt b/Tests/JavaNativeHeaders/CMakeLists.txt index 8a2e4603b..bb6b1f812 100644 --- a/Tests/JavaNativeHeaders/CMakeLists.txt +++ b/Tests/JavaNativeHeaders/CMakeLists.txt @@ -1,6 +1,6 @@ project(helloJavaNativeHeaders Java CXX) -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CMAKE_VERBOSE_MAKEFILE 1) include (CTest) diff --git a/Tests/Jump/CMakeLists.txt b/Tests/Jump/CMakeLists.txt index 4c5ad303f..bab4aa4d9 100644 --- a/Tests/Jump/CMakeLists.txt +++ b/Tests/Jump/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(Jump) set(CMAKE_IGNORE_DEPENDENCIES_ORDERING 1) diff --git a/Tests/LinkDirectory/CMakeLists.txt b/Tests/LinkDirectory/CMakeLists.txt index 2c2e488a3..98479c5a2 100644 --- a/Tests/LinkDirectory/CMakeLists.txt +++ b/Tests/LinkDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(LinkDirectory C) # Put the subproject source tree in our build tree so it can refer to diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt index 431fd8977..b85e351a0 100644 --- a/Tests/LinkDirectory/External/CMakeLists.txt +++ b/Tests/LinkDirectory/External/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(LinkDirectoryExternal C) diff --git a/Tests/LinkFlags/CMakeLists.txt b/Tests/LinkFlags/CMakeLists.txt index c25c4b2bc..d2d4149c4 100644 --- a/Tests/LinkFlags/CMakeLists.txt +++ b/Tests/LinkFlags/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(LinkFlags C) string(TOUPPER "${TEST_CONFIG}" TEST_CONFIG_UPPER) diff --git a/Tests/LinkLanguage/CMakeLists.txt b/Tests/LinkLanguage/CMakeLists.txt index b4e53925d..0a26b9171 100644 --- a/Tests/LinkLanguage/CMakeLists.txt +++ b/Tests/LinkLanguage/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.7.20090708) +cmake_minimum_required(VERSION 3.10) project(LinkLanguage C CXX) add_library(foo STATIC foo.cxx) diff --git a/Tests/LinkStatic/CMakeLists.txt b/Tests/LinkStatic/CMakeLists.txt index ad3b11169..0938dda5a 100644 --- a/Tests/LinkStatic/CMakeLists.txt +++ b/Tests/LinkStatic/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.4.20110303 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt index 3313c572b..11dce36a7 100644 --- a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt +++ b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CMAKE_LOADED_COMMANDS) if (MUDSLIDE_TYPE MATCHES MUCHO) diff --git a/Tests/LoadCommand/CMakeLists.txt b/Tests/LoadCommand/CMakeLists.txt index c0dc24720..d89270348 100644 --- a/Tests/LoadCommand/CMakeLists.txt +++ b/Tests/LoadCommand/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0031 OLD) # testing the old behavior project(LoadCommand) diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt index 74a1f550e..9f29c430e 100644 --- a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt +++ b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CMAKE_LOADED_COMMANDS) if (MUDSLIDE_TYPE MATCHES MUCHO) diff --git a/Tests/LoadCommandOneConfig/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeLists.txt index 35dc0fe59..ac304e37b 100644 --- a/Tests/LoadCommandOneConfig/CMakeLists.txt +++ b/Tests/LoadCommandOneConfig/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0031 OLD) # testing the old behavior project(LoadCommand) diff --git a/Tests/MFC/CMakeLists.txt b/Tests/MFC/CMakeLists.txt index 3f78a8111..1a895dd2c 100644 --- a/Tests/MFC/CMakeLists.txt +++ b/Tests/MFC/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(mfc_driver) include(CTest) diff --git a/Tests/MFC/CMakeLists.txt.in b/Tests/MFC/CMakeLists.txt.in index bae3d2fb1..dd5ed77db 100644 --- a/Tests/MFC/CMakeLists.txt.in +++ b/Tests/MFC/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(mfc1) macro(replace_flags var these those) diff --git a/Tests/MFC/try_compile/CMakeLists.txt b/Tests/MFC/try_compile/CMakeLists.txt index d22b8b699..56381bc20 100644 --- a/Tests/MFC/try_compile/CMakeLists.txt +++ b/Tests/MFC/try_compile/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(try_compile_mfc) set(files diff --git a/Tests/MSManifest/CMakeLists.txt b/Tests/MSManifest/CMakeLists.txt index 631c59806..d6cb1073d 100644 --- a/Tests/MSManifest/CMakeLists.txt +++ b/Tests/MSManifest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(MSManifest C) include(CTest) diff --git a/Tests/MSManifest/Subdir2/check.cmake b/Tests/MSManifest/Subdir2/check.cmake index 4a1705bef..385190b8d 100644 --- a/Tests/MSManifest/Subdir2/check.cmake +++ b/Tests/MSManifest/Subdir2/check.cmake @@ -14,7 +14,7 @@ else() endif() # Verify Third Manifest Content is inside Executable. -file(STRINGS "${exe}" manifest_content3 REGEX "true") +file(STRINGS "${exe}" manifest_content3 REGEX "PerMonitorV2") if(manifest_content3) message(STATUS "Expected manifest content found:\n ${manifest_content3}") else() diff --git a/Tests/MacRuntimePath/A/CMakeLists.txt b/Tests/MacRuntimePath/A/CMakeLists.txt index 7af746c5f..50ac71f38 100644 --- a/Tests/MacRuntimePath/A/CMakeLists.txt +++ b/Tests/MacRuntimePath/A/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(MacRuntimePath_A) # a shared library diff --git a/Tests/MacRuntimePath/B/CMakeLists.txt b/Tests/MacRuntimePath/B/CMakeLists.txt index e88433cae..c2ff262f4 100644 --- a/Tests/MacRuntimePath/B/CMakeLists.txt +++ b/Tests/MacRuntimePath/B/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(MacRuntimePath_B) include(${MacRuntimePath_B_BINARY_DIR}/../Root/lib/exp.cmake) diff --git a/Tests/MacRuntimePath/CMakeLists.txt b/Tests/MacRuntimePath/CMakeLists.txt index 9f1bf1a13..4a972a3a6 100644 --- a/Tests/MacRuntimePath/CMakeLists.txt +++ b/Tests/MacRuntimePath/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(MacRuntimePath) if(NOT DEFINED CMake_TEST_NESTED_MAKE_PROGRAM AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") set(CMake_TEST_NESTED_MAKE_PROGRAM "${CMAKE_MAKE_PROGRAM}") diff --git a/Tests/MacroTest/CMakeLists.txt b/Tests/MacroTest/CMakeLists.txt index 6c6dfb625..1ae94c9d9 100644 --- a/Tests/MacroTest/CMakeLists.txt +++ b/Tests/MacroTest/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (MacroTest) macro(FAILED testname) diff --git a/Tests/MakeClean/CMakeLists.txt b/Tests/MakeClean/CMakeLists.txt index b7b9602c2..0b85d0ea9 100644 --- a/Tests/MakeClean/CMakeLists.txt +++ b/Tests/MakeClean/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(MakeClean) # Build the to-clean project. diff --git a/Tests/MathTest/CMakeLists.txt b/Tests/MathTest/CMakeLists.txt index 396f633c6..6cafcc02b 100644 --- a/Tests/MathTest/CMakeLists.txt +++ b/Tests/MathTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(MathTest) diff --git a/Tests/MissingInstall/CMakeLists.txt b/Tests/MissingInstall/CMakeLists.txt index 365b31f62..34a820e6e 100644 --- a/Tests/MissingInstall/CMakeLists.txt +++ b/Tests/MissingInstall/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(TestMissingInstall) set(CMAKE_SKIP_INSTALL_RULES ON) diff --git a/Tests/MissingSourceFile/CMakeLists.txt b/Tests/MissingSourceFile/CMakeLists.txt index f4fd8b0fb..6b1ab234d 100644 --- a/Tests/MissingSourceFile/CMakeLists.txt +++ b/Tests/MissingSourceFile/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(MissingSourceFile C) add_executable(MissingSourceFile DoesNotExist/MissingSourceFile.c) diff --git a/Tests/Module/CheckIPOSupported-C/CMakeLists.txt b/Tests/Module/CheckIPOSupported-C/CMakeLists.txt index c5cd03e06..50b8c30f1 100644 --- a/Tests/Module/CheckIPOSupported-C/CMakeLists.txt +++ b/Tests/Module/CheckIPOSupported-C/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(CheckIPOSupported-C LANGUAGES C) cmake_policy(SET CMP0069 NEW) diff --git a/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt b/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt index 9dd670e5a..a029be590 100644 --- a/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt +++ b/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(CheckIPOSupported-CUDA LANGUAGES CUDA) cmake_policy(SET CMP0069 NEW) diff --git a/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt b/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt index 237bf1d6d..14175d7f2 100644 --- a/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt +++ b/Tests/Module/CheckIPOSupported-CXX/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(CheckIPOSupported-CXX LANGUAGES CXX) cmake_policy(SET CMP0069 NEW) diff --git a/Tests/Module/CheckIPOSupported-Fortran/CMakeLists.txt b/Tests/Module/CheckIPOSupported-Fortran/CMakeLists.txt index 3872b567b..a44a75dc3 100644 --- a/Tests/Module/CheckIPOSupported-Fortran/CMakeLists.txt +++ b/Tests/Module/CheckIPOSupported-Fortran/CMakeLists.txt @@ -1,8 +1,12 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(CheckIPOSupported-Fortran LANGUAGES Fortran) cmake_policy(SET CMP0069 NEW) +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran") + add_compile_options(--implicit-interface) +endif() + include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_output) if(ipo_supported) diff --git a/Tests/Module/CheckTypeSize/CMakeLists.txt b/Tests/Module/CheckTypeSize/CMakeLists.txt index 102cf0c17..397196d62 100644 --- a/Tests/Module/CheckTypeSize/CMakeLists.txt +++ b/Tests/Module/CheckTypeSize/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) project(CheckTypeSize) # Check C types diff --git a/Tests/Module/ExternalData/CMakeLists.txt b/Tests/Module/ExternalData/CMakeLists.txt index 737e17a68..c09c69300 100644 --- a/Tests/Module/ExternalData/CMakeLists.txt +++ b/Tests/Module/ExternalData/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.10.20130115) +cmake_minimum_required(VERSION 3.10) project(ExternalDataTest NONE) include(CTest) diff --git a/Tests/Module/FindDependency/CMakeLists.txt b/Tests/Module/FindDependency/CMakeLists.txt index 06d7dce9a..8b0dad681 100644 --- a/Tests/Module/FindDependency/CMakeLists.txt +++ b/Tests/Module/FindDependency/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(FindDependency) set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/packages") diff --git a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt index 0f2067a92..3105e9bd3 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/ModuleDefinition/CMakeLists.txt b/Tests/ModuleDefinition/CMakeLists.txt index 49577a79b..089495eb5 100644 --- a/Tests/ModuleDefinition/CMakeLists.txt +++ b/Tests/ModuleDefinition/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ModuleDefinition C) # Test .def file source recognition for DLLs. diff --git a/Tests/NewlineArgs/CMakeLists.txt b/Tests/NewlineArgs/CMakeLists.txt index c82211365..e42c8c079 100644 --- a/Tests/NewlineArgs/CMakeLists.txt +++ b/Tests/NewlineArgs/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple CXX only test case -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (NewlineArgs CXX) add_definitions("-DTEST_FLAG_1 diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt index 2bc23274a..39a3e42af 100644 --- a/Tests/ObjectLibrary/CMakeLists.txt +++ b/Tests/ObjectLibrary/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ObjectLibrary C) add_subdirectory(A) diff --git a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt index f19d5a47b..448a4c715 100644 --- a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt +++ b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExportLanguages CXX) add_library(ExportLanguagesA OBJECT a.cxx) add_library(ExportLanguagesB STATIC a.c $) diff --git a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt index 093aca698..dabf3295f 100644 --- a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt +++ b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(ExportLanguagesTest) diff --git a/Tests/OutDir/CMakeLists.txt b/Tests/OutDir/CMakeLists.txt index e7bc3abff..981244e00 100644 --- a/Tests/OutDir/CMakeLists.txt +++ b/Tests/OutDir/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(OutDir C) get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) diff --git a/Tests/OutOfSource/CMakeLists.txt b/Tests/OutOfSource/CMakeLists.txt index c82d077a7..2a501466f 100644 --- a/Tests/OutOfSource/CMakeLists.txt +++ b/Tests/OutOfSource/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple test case -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (OutOfSource) add_subdirectory(SubDir) diff --git a/Tests/PDBDirectoryAndName/CMakeLists.txt b/Tests/PDBDirectoryAndName/CMakeLists.txt index 81207bcad..de287d821 100644 --- a/Tests/PDBDirectoryAndName/CMakeLists.txt +++ b/Tests/PDBDirectoryAndName/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(PDBDirectoryAndName C) # Make sure the proper compiler is in use. diff --git a/Tests/Plugin/CMakeLists.txt b/Tests/Plugin/CMakeLists.txt index a62e53f60..16561011f 100644 --- a/Tests/Plugin/CMakeLists.txt +++ b/Tests/Plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Plugin) # Test per-target output directory properties. diff --git a/Tests/Plugin/PluginTest/CMakeLists.txt b/Tests/Plugin/PluginTest/CMakeLists.txt index 4100683f8..6acf5193a 100644 --- a/Tests/Plugin/PluginTest/CMakeLists.txt +++ b/Tests/Plugin/PluginTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(PluginTest) diff --git a/Tests/Policy0002/CMakeLists.txt b/Tests/Policy0002/CMakeLists.txt index 0f6d3319a..a408e7676 100644 --- a/Tests/Policy0002/CMakeLists.txt +++ b/Tests/Policy0002/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(Policy0002 C) cmake_policy(SET CMP0002 OLD) add_subdirectory(A) diff --git a/Tests/PolicyScope/Bar.cmake b/Tests/PolicyScope/Bar.cmake index cf1904c48..5202cb5e1 100644 --- a/Tests/PolicyScope/Bar.cmake +++ b/Tests/PolicyScope/Bar.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6.3) +cmake_minimum_required(VERSION 3.10) # Make sure a policy set differently by our includer is now correct. cmake_policy(GET CMP0003 cmp) diff --git a/Tests/PolicyScope/FindFoo.cmake b/Tests/PolicyScope/FindFoo.cmake index 5b441e20e..e364b6d95 100644 --- a/Tests/PolicyScope/FindFoo.cmake +++ b/Tests/PolicyScope/FindFoo.cmake @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 2.6.3) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0003 OLD) diff --git a/Tests/PositionIndependentTargets/CMakeLists.txt b/Tests/PositionIndependentTargets/CMakeLists.txt index 4f7e285fc..78c81fb6b 100644 --- a/Tests/PositionIndependentTargets/CMakeLists.txt +++ b/Tests/PositionIndependentTargets/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(PositionIndependentTargets) diff --git a/Tests/PrecompiledHeader/CMakeLists.txt b/Tests/PrecompiledHeader/CMakeLists.txt index 58f4863b4..dd61d6434 100644 --- a/Tests/PrecompiledHeader/CMakeLists.txt +++ b/Tests/PrecompiledHeader/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(PrecompiledHeader C) # Make sure the proper compiler is in use. diff --git a/Tests/Preprocess/CMakeLists.txt b/Tests/Preprocess/CMakeLists.txt index 63a7b5e60..78ba2939f 100644 --- a/Tests/Preprocess/CMakeLists.txt +++ b/Tests/Preprocess/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0043 OLD) # testing the old behavior project(Preprocess) diff --git a/Tests/Properties/CMakeLists.txt b/Tests/Properties/CMakeLists.txt index a1158c6be..78486ac2d 100644 --- a/Tests/Properties/CMakeLists.txt +++ b/Tests/Properties/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple CXX only test case -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (Properties) # these first three tests really test both properties and the management of diff --git a/Tests/Qt4And5Automoc/CMakeLists.txt b/Tests/Qt4And5Automoc/CMakeLists.txt index 12dc99b2a..6ae09efec 100644 --- a/Tests/Qt4And5Automoc/CMakeLists.txt +++ b/Tests/Qt4And5Automoc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Qt4And5Automoc) diff --git a/Tests/Qt4Deploy/CMakeLists.txt b/Tests/Qt4Deploy/CMakeLists.txt index c73a38c0f..f221feefd 100644 --- a/Tests/Qt4Deploy/CMakeLists.txt +++ b/Tests/Qt4Deploy/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(Qt4Deploy) set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install) diff --git a/Tests/Qt4Targets/CMakeLists.txt b/Tests/Qt4Targets/CMakeLists.txt index 83cd44f18..47a640812 100644 --- a/Tests/Qt4Targets/CMakeLists.txt +++ b/Tests/Qt4Targets/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Qt4Targets) diff --git a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt index d0e96170b..f8b177aa7 100644 --- a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt +++ b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(IncrementalMoc) find_package(Qt4 REQUIRED) diff --git a/Tests/QtAutogen/LowMinimumVersion/CMakeLists.txt b/Tests/QtAutogen/LowMinimumVersion/CMakeLists.txt index e1af3d863..7622f7cbf 100644 --- a/Tests/QtAutogen/LowMinimumVersion/CMakeLists.txt +++ b/Tests/QtAutogen/LowMinimumVersion/CMakeLists.txt @@ -1,5 +1,5 @@ # Use a low minimum version -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(LowMinimumVersion) include("../AutogenGuiTest.cmake") diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake index d37be1c13..bd0c59849 100644 --- a/Tests/QtAutogen/Tests.cmake +++ b/Tests/QtAutogen/Tests.cmake @@ -47,9 +47,7 @@ ADD_AUTOGEN_TEST(UnityMocSource) if(QT_TEST_ALLOW_QT_MACROS) ADD_AUTOGEN_TEST(MocCMP0071) - set_property(TEST "Qt${QT_TEST_VERSION}Autogen.MocCMP0071" APPEND PROPERTY LABELS "policy") ADD_AUTOGEN_TEST(MocCMP0100) - set_property(TEST "Qt${QT_TEST_VERSION}Autogen.MocCMP0100" APPEND PROPERTY LABELS "policy") ADD_AUTOGEN_TEST(MocInclude) ADD_AUTOGEN_TEST(MocIncludeSymlink) ADD_AUTOGEN_TEST(MocSkipSource) diff --git a/Tests/QtAutomocNoQt/CMakeLists.txt b/Tests/QtAutomocNoQt/CMakeLists.txt index 4e2ceeb32..4e4daf98a 100644 --- a/Tests/QtAutomocNoQt/CMakeLists.txt +++ b/Tests/QtAutomocNoQt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(QtAutomocNoQt) diff --git a/Tests/README.rst b/Tests/README.rst index 2810fec4e..4bbbce195 100644 --- a/Tests/README.rst +++ b/Tests/README.rst @@ -2,8 +2,9 @@ CMake Tests Directory ********************* This directory contains the CMake test suite. -See also the `CMake Source Code Guide`_. +See also the `CMake Testing Guide`_ and the `CMake Source Code Guide`_. +.. _`CMake Testing Guide`: ../Help/dev/testing.rst .. _`CMake Source Code Guide`: ../Help/dev/source.rst Many tests exist as immediate subdirectories, but some tests diff --git a/Tests/ReturnTest/CMakeLists.txt b/Tests/ReturnTest/CMakeLists.txt index 03a0f7aa6..ff1081142 100644 --- a/Tests/ReturnTest/CMakeLists.txt +++ b/Tests/ReturnTest/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project (ReturnTest) function (FAILED testname) diff --git a/Tests/RunCMake/Android/CMakeLists.txt b/Tests/RunCMake/Android/CMakeLists.txt index dc9248697..bf2ef1506 100644 --- a/Tests/RunCMake/Android/CMakeLists.txt +++ b/Tests/RunCMake/Android/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Android/RunCMakeTest.cmake b/Tests/RunCMake/Android/RunCMakeTest.cmake index aa0cf4d92..317adf64b 100644 --- a/Tests/RunCMake/Android/RunCMakeTest.cmake +++ b/Tests/RunCMake/Android/RunCMakeTest.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) include(RunCMake) foreach(v TEST_ANDROID_NDK TEST_ANDROID_STANDALONE_TOOLCHAIN) diff --git a/Tests/RunCMake/AndroidMK/CMakeLists.txt b/Tests/RunCMake/AndroidMK/CMakeLists.txt index 576787a2a..43a4aee2a 100644 --- a/Tests/RunCMake/AndroidMK/CMakeLists.txt +++ b/Tests/RunCMake/AndroidMK/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) # or languages needed include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AndroidTestUtilities/CMakeLists.txt b/Tests/RunCMake/AndroidTestUtilities/CMakeLists.txt index dc9248697..bf2ef1506 100644 --- a/Tests/RunCMake/AndroidTestUtilities/CMakeLists.txt +++ b/Tests/RunCMake/AndroidTestUtilities/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AppleTextStubs/CMakeLists.txt b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/AppleTextStubs/CMakeLists.txt +++ b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/AutoExportDll/CMakeLists.txt b/Tests/RunCMake/AutoExportDll/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/AutoExportDll/CMakeLists.txt +++ b/Tests/RunCMake/AutoExportDll/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake deleted file mode 100644 index 003655109..000000000 --- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake +++ /dev/null @@ -1,487 +0,0 @@ -include(RunCMake) - -run_cmake(NoQt) -if (DEFINED with_qt_version) - set(RunCMake_TEST_OPTIONS - -Dwith_qt_version=${with_qt_version} - "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" - "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" - ) - - run_cmake(QtInFunction) - run_cmake(QtInFunctionNested) - run_cmake(QtInFunctionProperty) - - run_cmake(CMP0111-imported-target-full) - run_cmake(CMP0111-imported-target-libname) - run_cmake(CMP0111-imported-target-implib-only) - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocPredefs-build) - run_cmake(MocPredefs) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(MocPredefs-build ${CMAKE_COMMAND} --build . --config Debug) - endblock() - - # Detect information from the toolchain: - # - CMAKE_INCLUDE_FLAG_CXX - # - CMAKE_INCLUDE_SYSTEM_FLAG_CXX - run_cmake(Inspect) - include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") - - if(CMAKE_INCLUDE_SYSTEM_FLAG_CXX) - if(RunCMake_GENERATOR MATCHES "Visual Studio") - string(REGEX REPLACE "^-" "/" test_expect_stdout "${CMAKE_INCLUDE_SYSTEM_FLAG_CXX}") - else() - set(test_expect_stdout "-*${CMAKE_INCLUDE_SYSTEM_FLAG_CXX}") - endif() - string(APPEND test_expect_stdout " *(\"[^\"]*|([^ ]|\\ )*)[\\/]dummy_autogen[\\/]include") - if(RunCMake_GENERATOR_IS_MULTI_CONFIG) - string(APPEND test_expect_stdout "_Debug") - endif() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0151-new-build) - run_cmake_with_options(CMP0151-new ${RunCMake_TEST_OPTIONS} -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") - message(STATUS "RunCMake_TEST_EXPECT_stdout: ${RunCMake_TEST_EXPECT_stdout}") - run_cmake_command(CMP0151-new-build ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenUseSystemIncludeOn-build) - run_cmake_with_options(AutogenUseSystemIncludeOn ${RunCMake_TEST_OPTIONS} -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") - message(STATUS "RunCMake_TEST_EXPECT_stdout: ${RunCMake_TEST_EXPECT_stdout}") - run_cmake_command(AutogenUseSystemIncludeOn ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - endif() - - if(CMAKE_INCLUDE_FLAG_CXX) - if(RunCMake_GENERATOR MATCHES "Visual Studio") - string(REGEX REPLACE "^-" "/" test_expect_stdout "${CMAKE_INCLUDE_FLAG_CXX}") - else() - set(test_expect_stdout "-*${CMAKE_INCLUDE_FLAG_CXX}") - endif() - string(APPEND test_expect_stdout " *(\"[^\"]*|([^ ]|\\ )*)[\\/]dummy_autogen[\\/]include") - if(RunCMake_GENERATOR_IS_MULTI_CONFIG) - string(APPEND test_expect_stdout "_Debug") - endif() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0151-old-build) - run_cmake_with_options(CMP0151-old ${RunCMake_TEST_OPTIONS} -DCMAKE_POLICY_DEFAULT_CMP0151=OLD) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") - message(STATUS "RunCMake_TEST_EXPECT_stdout: ${RunCMake_TEST_EXPECT_stdout}") - run_cmake_command(CMP0151-old-build ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenUseSystemIncludeOff-build) - run_cmake_with_options(AutogenUseSystemIncludeOff ${RunCMake_TEST_OPTIONS} -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") - message(STATUS "RunCMake_TEST_EXPECT_stdout: ${RunCMake_TEST_EXPECT_stdout}") - run_cmake_command(AutogenUseSystemIncludeOff ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - - if(RunCMake_GENERATOR MATCHES "Make|Ninja") - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenSkipLinting-build) - list(APPEND RunCMake_TEST_OPTIONS - "-DPSEUDO_CPPCHECK=${PSEUDO_CPPCHECK}" - "-DPSEUDO_CPPLINT=${PSEUDO_CPPLINT}" - "-DPSEUDO_IWYU=${PSEUDO_IWYU}" - "-DPSEUDO_TIDY=${PSEUDO_TIDY}") - - run_cmake(AutogenSkipLinting) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(AutogenSkipLinting-build ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - endif() - endif() - - if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND NOT RunCMake_GENERATOR MATCHES "Xcode") - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocGeneratedFile-build) - run_cmake(MocGeneratedFile) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(MocGeneratedFile-build ${CMAKE_COMMAND} --build . --config Debug --verbose) - endblock() - if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config") - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocGeneratedFile-cross-config-build) - list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_CROSS_CONFIGS=all) - run_cmake(MocGeneratedFile) - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(MocGeneratedFile-cross-config-build ${CMAKE_COMMAND} --build . --config Release --target libgen:Debug) - run_cmake_command(MocGeneratedFile-cross-config-build ${CMAKE_COMMAND} --build . --config Debug --target libgen:Release) - endblock() - endif() - endif() - - if(RunCMake_GENERATOR MATCHES "Make|Ninja") - block() - if(QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) - if (RunCMake_GENERATOR MATCHES "Ninja Multi-Config") - set(config_list Debug Release RelWithDebInfo) - set(use_better_graph_list ON OFF) - else() - set(config_list single-config) - set(use_better_graph_list OFF) - endif() - - foreach(use_better_graph IN ITEMS ${use_better_graph_list}) - foreach(config IN ITEMS ${config_list}) - block() - if (config STREQUAL "single-config") - set(config_suffix "") - else() - set(config_path "_${config}") - if (use_better_graph) - set(config_suffix "_${config}") - endif() - endif() - - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_path}-build) - run_cmake_with_options(QtAutoMocDeps ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=${use_better_graph}) - set(RunCMake_TEST_NO_CLEAN 1) - # Build the project. - if (config STREQUAL "single-config") - set(config_param "") - else() - set(config_param "--config ${config}") - endif() - run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param}) - # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC - # for app_with_qt target. - file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp") - set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target app_with_qt|\ -Automatic MOC for target sub_exe_1|\ -Automatic MOC for target sub_exe_2") - set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for 'app_with_qt', 'sub_exe_1' and 'sub_exe_2'") - # Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2. - run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param}) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - unset(RunCMake_TEST_NOT_EXPECT_stdout) - - macro(check_file_exists file) - if (EXISTS "${file}") - set(check_result "PASSED") - set(message_type "STATUS") - else() - set(check_result "FAILED") - set(message_type "FATAL_ERROR") - endif() - - message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}") - endmacro() - - check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps${config_suffix}") - check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps${config_suffix}") - check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps${config_suffix}") - - check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp${config_suffix}") - check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp${config_suffix}") - check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp${config_suffix}") - - # Touch a header file to make sure an automoc dependency cycle is not introduced. - file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h") - set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle") - run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose) - # Need to run a second time to hit the dependency cycle. - set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle") - run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose) - endblock() - endforeach() - endforeach() - endif() - endblock() - endif() - - function(run_make_program dir) - execute_process( - COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} - WORKING_DIRECTORY "${dir}" - OUTPUT_VARIABLE make_program_stdout - ERROR_VARIABLE make_program_stderr - RESULT_VARIABLE make_program_result - ) - if (NOT DEFINED RunMakeProgram_expected_result) - set(RunMakeProgram_expected_result 0) - endif() - if(NOT "${make_program_result}" MATCHES "${RunMakeProgram_expected_result}") - message(STATUS " -============ beginning of ${RunCMake_MAKE_PROGRAM}'s stdout ============ -${make_program_stdout} -=============== end of ${RunCMake_MAKE_PROGRAM}'s stdout =============== -") - message(STATUS " -============ beginning of ${RunCMake_MAKE_PROGRAM}'s stderr ============ -${make_program_stderr} -=============== end of ${RunCMake_MAKE_PROGRAM}'s stderr =============== -") - message(FATAL_ERROR - "top ${RunCMake_MAKE_PROGRAM} build failed exited with status ${make_program_result}") - endif() - set(make_program_stdout "${make_program_stdout}" PARENT_SCOPE) - endfunction(run_make_program) - - function(count_substring STRING SUBSTRING COUNT_VAR) - string(LENGTH "${STRING}" STRING_LENGTH) - string(LENGTH "${SUBSTRING}" SUBSTRING_LENGTH) - if (SUBSTRING_LENGTH EQUAL 0) - message(FATAL_ERROR "SUBSTRING_LENGTH is 0") - endif() - - if (STRING_LENGTH EQUAL 0) - message(FATAL_ERROR "STRING_LENGTH is 0") - endif() - - if (STRING_LENGTH LESS SUBSTRING_LENGTH) - message(FATAL_ERROR "STRING_LENGTH is less than SUBSTRING_LENGTH") - endif() - - set(COUNT 0) - string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START) - while(SUBSTRING_START GREATER_EQUAL 0) - math(EXPR COUNT "${COUNT} + 1") - math(EXPR SUBSTRING_START "${SUBSTRING_START} + ${SUBSTRING_LENGTH}") - string(SUBSTRING "${STRING}" ${SUBSTRING_START} -1 STRING) - string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START) - endwhile() - - set(${COUNT_VAR} ${COUNT} PARENT_SCOPE) - endfunction() - - function(expect_only_once make_program_stdout expected_output test_name) - count_substring("${make_program_stdout}" "${expected_output}" count) - if(NOT count EQUAL 1) - message(STATUS "${test_name}-expect_only_once - FAILED") - message(FATAL_ERROR "Expected to find ${expected_output} exactly once in ${make_program_stdout} but found ${count} occurrences of ${expected_output}") - else() - message(STATUS "${test_name}-expect_only_once - PASSED") - endif() - endfunction() - - function(expect_n_times string_to_check expected_output expected_count test_name) - count_substring("${string_to_check}" "${expected_output}" count) - if(NOT count EQUAL ${expected_count}) - message(STATUS "${test_name}-expect_${expected_count}_times - FAILED") - message(FATAL_ERROR "Expected to find ${expected_output} exactly ${expected_count} times in ${string_to_check} but found ${count} occurrences of ${expected_output}") - else() - message(STATUS "${test_name}-expect_${expected_count}_times - PASSED") - endif() - endfunction() - - function(not_expect make_program_stdout unexpected_output test_name) - count_substring("${make_program_stdout}" "${unexpected_output}" count) - if(NOT count EQUAL 0) - message(STATUS "${test_name}-not_expect - FAILED") - message(FATAL_ERROR "Expected to find ${unexpected_output} exactly 0 times in ${make_program_stdout} but found ${count} occurrences of ${unexpected_output}") - else() - message(STATUS "${test_name}-not_expect - PASSED") - endif() - endfunction() - - if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) - foreach(exe IN ITEMS Moc Uic Rcc) - if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config") - block() - set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-multi-config-build) - run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - set(RunCMake_TEST_NO_CLEAN 1) - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*") - set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_running_exe_${config}") - run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config}) - endblock() - endforeach() - set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do") - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_no_work_to_do") - run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config}) - endblock() - endforeach() - endblock() - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build) - run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${config}.ninja) - - set(expected_output "running_exe_${config}") - expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}") - - foreach(sub_config IN ITEMS Debug Release RelWithDebInfo) - if(NOT sub_config STREQUAL config) - set(unexpected_output "running_exe_${sub_config}") - not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig-${config}-${unexpected_output}") - endif() - endforeach() - - if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") - set(expected_output "cmake_autogen") - else() - set(expected_output "cmake_autorcc") - endif() - expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}") - endblock() - endforeach() - endblock() - block() - foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) - foreach(target_config IN ITEMS Debug Release RelWithDebInfo) - block() - set(TEST_SUFFIX "-CrossConfig-${ninja_config}-${target_config}") - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build) - set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX}) - run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=${ninja_config} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - - run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja dummy:${target_config}) - - set(expected_output "running_exe_${ninja_config}") - expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}") - - foreach(sub_config IN ITEMS Debug Release RelWithDebInfo) - if(NOT sub_config STREQUAL ninja_config) - set(unexpected_output "running_exe_${sub_config}") - not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${unexpected_output}") - endif() - endforeach() - - if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") - set(expected_output "cmake_autogen") - else() - set(expected_output "cmake_autorcc") - endif() - expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}") - endblock() - endforeach() - endforeach() - endblock() - block() - foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) - set(TEST_SUFFIX "-CrossConfig-${ninja_config}-all-all") - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build) - set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX}) - run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja all:all) - endforeach() - endblock() - elseif (RunCMake_GENERATOR MATCHES "Ninja|Make") - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build) - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}") - run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_BUILD_TYPE=${config} -DCMAKE_AUTOGEN_VERBOSE=ON) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*") - run_cmake_command(Auto${exe}ExecutableConfig-${config}-build ${CMAKE_COMMAND} --build .) - endblock() - endforeach() - endblock() - endif() - endforeach() - endif() - - # Visual Studio specific dependency tests - if (RunCMake_GENERATOR MATCHES "Visual Studio") - foreach(exe IN ITEMS Moc Uic Rcc) - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build) - set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") - run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON) - unset(RunCMake_TEST_VARIANT_DESCRIPTION) - set(RunCMake_TEST_NO_CLEAN 1) - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-first-build") - run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config}) - endblock() - endforeach() - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") - set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}") - set(not_expect_descripton "Auto${exe}") - else () - set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}") - set(not_expect_descripton "Auto${exe}") - endif() - set(RunCMake_TEST_VARIANT_DESCRIPTION "-second-build-${config}_expect_no_${not_expect_descripton}") - run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config}) - endblock() - endforeach() - endblock() - endforeach() - endif() - - if (RunCMake_GENERATOR MATCHES "Xcode") - foreach(exe IN ITEMS Moc Uic Rcc) - block() - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build) - set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") - set(RunCMake_TEST_EXPECT_stderr ".*") - run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_MAKE_PROGRAM ${CMAKE_COMMAND}) - run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config Debug) - if (exe STREQUAL "Moc") - set(expected_count 16) - elseif (exe STREQUAL "Uic") - set(expected_count 4) - else() - set(expected_count 12) - endif() - expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}") - expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}") - - if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") - expect_n_times("${make_program_stdout}" "AutoGen:" 20 "${exe}Example-build-AutoGen:") - endif() - - foreach(config IN ITEMS Debug Release RelWithDebInfo) - block() - run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config ${config}) - not_expect("${make_program_stdout}" "Auto${exe}" "${exe}Example-${config}_Auto${exe}") - not_expect("${make_program_stdout}" "AutoGen:" "${exe}Example-${config}_AutoGen") - endblock() - endforeach() - endblock() - endforeach() - endif() - - if (QtCore_VERSION VERSION_GREATER_EQUAL 6) - if (RunCMake_GENERATOR MATCHES "Make|Ninja") - foreach(value IN ITEMS ON OFF) - block() - set(RunCMake_TEST_BINARY_DIR - ${RunCMake_BINARY_DIR}/RccNoZTSD-${value}-build) - run_cmake_with_options(RccExample ${RunCMake_TEST_OPTIONS} - -DCMAKE_AUTOGEN_VERBOSE=ON -DZSTD_VALUE=${value}) - if (value STREQUAL "OFF") - set(RunCMake_TEST_EXPECT_stdout "--no-zstd") - else() - set(RunCMake_TEST_NOT_EXPECT_stdout "--no-zstd") - endif() - set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(RccNoZTSD-${value}-build ${CMAKE_COMMAND} - --build . --config Debug) - endblock() - endforeach() - endif() - endif() -endif () diff --git a/Tests/RunCMake/Autogen/AutogenSkipLinting-build-stderr.txt b/Tests/RunCMake/Autogen_1/AutogenSkipLinting-build-stderr.txt similarity index 100% rename from Tests/RunCMake/Autogen/AutogenSkipLinting-build-stderr.txt rename to Tests/RunCMake/Autogen_1/AutogenSkipLinting-build-stderr.txt diff --git a/Tests/RunCMake/Autogen/AutogenSkipLinting.cmake b/Tests/RunCMake/Autogen_1/AutogenSkipLinting.cmake similarity index 100% rename from Tests/RunCMake/Autogen/AutogenSkipLinting.cmake rename to Tests/RunCMake/Autogen_1/AutogenSkipLinting.cmake diff --git a/Tests/RunCMake/Autogen/AutogenUseSystemIncludeCommon.cmake b/Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeCommon.cmake similarity index 92% rename from Tests/RunCMake/Autogen/AutogenUseSystemIncludeCommon.cmake rename to Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeCommon.cmake index bbefd5f55..e0f3ce81a 100644 --- a/Tests/RunCMake/Autogen/AutogenUseSystemIncludeCommon.cmake +++ b/Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeCommon.cmake @@ -2,8 +2,6 @@ enable_language(CXX) find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) -set(CMAKE_AUTOMOC ON) - add_library(dummy SHARED empty.cpp) target_link_libraries(dummy Qt${with_qt_version}::Core Qt${with_qt_version}::Widgets diff --git a/Tests/RunCMake/Autogen/AutogenUseSystemIncludeOff.cmake b/Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeOff.cmake similarity index 100% rename from Tests/RunCMake/Autogen/AutogenUseSystemIncludeOff.cmake rename to Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeOff.cmake diff --git a/Tests/RunCMake/Autogen/AutogenUseSystemIncludeOn.cmake b/Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeOn.cmake similarity index 100% rename from Tests/RunCMake/Autogen/AutogenUseSystemIncludeOn.cmake rename to Tests/RunCMake/Autogen_1/AutogenUseSystemIncludeOn.cmake diff --git a/Tests/RunCMake/Autogen/CMP0111-imported-target-full.cmake b/Tests/RunCMake/Autogen_1/CMP0111-imported-target-full.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0111-imported-target-full.cmake rename to Tests/RunCMake/Autogen_1/CMP0111-imported-target-full.cmake diff --git a/Tests/RunCMake/Autogen/CMP0111-imported-target-implib-only.cmake b/Tests/RunCMake/Autogen_1/CMP0111-imported-target-implib-only.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0111-imported-target-implib-only.cmake rename to Tests/RunCMake/Autogen_1/CMP0111-imported-target-implib-only.cmake diff --git a/Tests/RunCMake/Autogen/CMP0111-imported-target-libname.cmake b/Tests/RunCMake/Autogen_1/CMP0111-imported-target-libname.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0111-imported-target-libname.cmake rename to Tests/RunCMake/Autogen_1/CMP0111-imported-target-libname.cmake diff --git a/Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake b/Tests/RunCMake/Autogen_1/CMP0111-imported-target-prelude.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake rename to Tests/RunCMake/Autogen_1/CMP0111-imported-target-prelude.cmake diff --git a/Tests/RunCMake/Autogen/CMP0151-common.cmake b/Tests/RunCMake/Autogen_1/CMP0151-common.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0151-common.cmake rename to Tests/RunCMake/Autogen_1/CMP0151-common.cmake diff --git a/Tests/RunCMake/Autogen/CMP0151-new.cmake b/Tests/RunCMake/Autogen_1/CMP0151-new.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0151-new.cmake rename to Tests/RunCMake/Autogen_1/CMP0151-new.cmake diff --git a/Tests/RunCMake/Autogen/CMP0151-old.cmake b/Tests/RunCMake/Autogen_1/CMP0151-old.cmake similarity index 100% rename from Tests/RunCMake/Autogen/CMP0151-old.cmake rename to Tests/RunCMake/Autogen_1/CMP0151-old.cmake diff --git a/Tests/RunCMake/Autogen/CMakeLists.txt b/Tests/RunCMake/Autogen_1/CMakeLists.txt similarity index 100% rename from Tests/RunCMake/Autogen/CMakeLists.txt rename to Tests/RunCMake/Autogen_1/CMakeLists.txt diff --git a/Tests/RunCMake/Autogen/Inspect.cmake b/Tests/RunCMake/Autogen_1/Inspect.cmake similarity index 100% rename from Tests/RunCMake/Autogen/Inspect.cmake rename to Tests/RunCMake/Autogen_1/Inspect.cmake diff --git a/Tests/RunCMake/Autogen/MocGeneratedFile.cmake b/Tests/RunCMake/Autogen_1/MocGeneratedFile.cmake similarity index 100% rename from Tests/RunCMake/Autogen/MocGeneratedFile.cmake rename to Tests/RunCMake/Autogen_1/MocGeneratedFile.cmake diff --git a/Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt b/Tests/RunCMake/Autogen_1/MocPredefs-build-stderr.txt similarity index 100% rename from Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt rename to Tests/RunCMake/Autogen_1/MocPredefs-build-stderr.txt diff --git a/Tests/RunCMake/Autogen/MocPredefs-check.cxx b/Tests/RunCMake/Autogen_1/MocPredefs-check.cxx similarity index 100% rename from Tests/RunCMake/Autogen/MocPredefs-check.cxx rename to Tests/RunCMake/Autogen_1/MocPredefs-check.cxx diff --git a/Tests/RunCMake/Autogen/MocPredefs-prefix.cmake b/Tests/RunCMake/Autogen_1/MocPredefs-prefix.cmake similarity index 100% rename from Tests/RunCMake/Autogen/MocPredefs-prefix.cmake rename to Tests/RunCMake/Autogen_1/MocPredefs-prefix.cmake diff --git a/Tests/RunCMake/Autogen/MocPredefs.cmake b/Tests/RunCMake/Autogen_1/MocPredefs.cmake similarity index 100% rename from Tests/RunCMake/Autogen/MocPredefs.cmake rename to Tests/RunCMake/Autogen_1/MocPredefs.cmake diff --git a/Tests/RunCMake/Autogen/MocPredefs.cxx b/Tests/RunCMake/Autogen_1/MocPredefs.cxx similarity index 100% rename from Tests/RunCMake/Autogen/MocPredefs.cxx rename to Tests/RunCMake/Autogen_1/MocPredefs.cxx diff --git a/Tests/RunCMake/Autogen/NoQt-stderr.txt b/Tests/RunCMake/Autogen_1/NoQt-stderr.txt similarity index 100% rename from Tests/RunCMake/Autogen/NoQt-stderr.txt rename to Tests/RunCMake/Autogen_1/NoQt-stderr.txt diff --git a/Tests/RunCMake/Autogen/NoQt.cmake b/Tests/RunCMake/Autogen_1/NoQt.cmake similarity index 100% rename from Tests/RunCMake/Autogen/NoQt.cmake rename to Tests/RunCMake/Autogen_1/NoQt.cmake diff --git a/Tests/RunCMake/Autogen/QtInFunction.cmake b/Tests/RunCMake/Autogen_1/QtInFunction.cmake similarity index 100% rename from Tests/RunCMake/Autogen/QtInFunction.cmake rename to Tests/RunCMake/Autogen_1/QtInFunction.cmake diff --git a/Tests/RunCMake/Autogen/QtInFunctionNested-stderr.txt b/Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt similarity index 100% rename from Tests/RunCMake/Autogen/QtInFunctionNested-stderr.txt rename to Tests/RunCMake/Autogen_1/QtInFunctionNested-stderr.txt diff --git a/Tests/RunCMake/Autogen/QtInFunctionNested.cmake b/Tests/RunCMake/Autogen_1/QtInFunctionNested.cmake similarity index 100% rename from Tests/RunCMake/Autogen/QtInFunctionNested.cmake rename to Tests/RunCMake/Autogen_1/QtInFunctionNested.cmake diff --git a/Tests/RunCMake/Autogen/QtInFunctionProperty.cmake b/Tests/RunCMake/Autogen_1/QtInFunctionProperty.cmake similarity index 100% rename from Tests/RunCMake/Autogen/QtInFunctionProperty.cmake rename to Tests/RunCMake/Autogen_1/QtInFunctionProperty.cmake diff --git a/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake new file mode 100644 index 000000000..2cf60e420 --- /dev/null +++ b/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake @@ -0,0 +1,134 @@ +include(RunCMake) + +run_cmake(NoQt) +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + + run_cmake(QtInFunction) + run_cmake(QtInFunctionNested) + run_cmake(QtInFunctionProperty) + + run_cmake(CMP0111-imported-target-full) + run_cmake(CMP0111-imported-target-libname) + run_cmake(CMP0111-imported-target-implib-only) + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocPredefs-build) + run_cmake(MocPredefs) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(MocPredefs-build ${CMAKE_COMMAND} --build . --config Debug) + endblock() + + # Detect information from the toolchain: + # - CMAKE_INCLUDE_FLAG_CXX + # - CMAKE_INCLUDE_SYSTEM_FLAG_CXX + run_cmake(Inspect) + include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + foreach(autogen_type IN ITEMS MOC UIC) + block() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-AUTO${autogen_type}=ON") + if(CMAKE_INCLUDE_SYSTEM_FLAG_CXX) + if(RunCMake_GENERATOR MATCHES "Visual Studio") + string(REGEX REPLACE "^-" "/" test_expect_stdout_common "${CMAKE_INCLUDE_SYSTEM_FLAG_CXX}") + else() + set(test_expect_stdout_common "-*${CMAKE_INCLUDE_SYSTEM_FLAG_CXX}") + endif() + set(test_expect_stdout_1 "${test_expect_stdout_common}") + set(test_expect_stdout_2 "${test_expect_stdout_common}") + string(APPEND test_expect_stdout_1 " *(\"[^\"]*|([^ ]|\\ )*)[\\/]dummy_autogen[\\/]include") + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + string(APPEND test_expect_stdout_1 "_Debug") + endif() + string(APPEND test_expect_stdout_2 " *(\"[^\"]*|([^ ]|\\ )*)[\\/]QtCore") + set(test_expect_stdout "${test_expect_stdout_1}.*${test_expect_stdout_2}") + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0151-new-build) + run_cmake_with_options(CMP0151-new ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTO${autogen_type}=ON -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") + run_cmake_command(CMP0151-new-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenUseSystemIncludeOn-build) + run_cmake_with_options(AutogenUseSystemIncludeOn ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTO${autogen_type}=ON -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") + run_cmake_command(AutogenUseSystemIncludeOn-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + endif() + + if(CMAKE_INCLUDE_FLAG_CXX) + if(RunCMake_GENERATOR MATCHES "Visual Studio") + string(REGEX REPLACE "^-" "/" test_expect_stdout "${CMAKE_INCLUDE_FLAG_CXX}") + else() + set(test_expect_stdout "-*${CMAKE_INCLUDE_FLAG_CXX}") + endif() + string(APPEND test_expect_stdout " *(\"[^\"]*|([^ ]|\\ )*)[\\/]dummy_autogen[\\/]include") + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + string(APPEND test_expect_stdout "_Debug") + endif() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0151-old-build) + run_cmake_with_options(CMP0151-old ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTO${autogen_type}=ON -DCMAKE_POLICY_DEFAULT_CMP0151=OLD) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") + run_cmake_command(CMP0151-old-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenUseSystemIncludeOff-build) + run_cmake_with_options(AutogenUseSystemIncludeOff ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTO${autogen_type}=ON -DCMAKE_POLICY_DEFAULT_CMP0151=NEW) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_EXPECT_stdout "${test_expect_stdout}") + run_cmake_command(AutogenUseSystemIncludeOff-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + endif() + endblock() + endforeach() + if(CMAKE_INCLUDE_FLAG_CXX) + if(RunCMake_GENERATOR MATCHES "Make|Ninja") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AutogenSkipLinting-build) + list(APPEND RunCMake_TEST_OPTIONS + "-DPSEUDO_CPPCHECK=${PSEUDO_CPPCHECK}" + "-DPSEUDO_CPPLINT=${PSEUDO_CPPLINT}" + "-DPSEUDO_IWYU=${PSEUDO_IWYU}" + "-DPSEUDO_TIDY=${PSEUDO_TIDY}") + + run_cmake(AutogenSkipLinting) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(AutogenSkipLinting-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + endif() + endif() + + if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND NOT RunCMake_GENERATOR MATCHES "Xcode") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocGeneratedFile-build) + run_cmake(MocGeneratedFile) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(MocGeneratedFile-build ${CMAKE_COMMAND} --build . --config Debug --verbose) + endblock() + if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocGeneratedFile-cross-config-build) + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_CROSS_CONFIGS=all) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMAKE_CROSS_CONFIGS-all") + run_cmake(MocGeneratedFile) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Release") + run_cmake_command(MocGeneratedFile-cross-config-build ${CMAKE_COMMAND} --build . --config Release --target libgen:Debug) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Debug") + run_cmake_command(MocGeneratedFile-cross-config-build ${CMAKE_COMMAND} --build . --config Debug --target libgen:Release) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + endblock() + endif() + endif() +endif () diff --git a/Tests/RunCMake/Autogen/SkipLinting.cxx b/Tests/RunCMake/Autogen_1/SkipLinting.cxx similarity index 100% rename from Tests/RunCMake/Autogen/SkipLinting.cxx rename to Tests/RunCMake/Autogen_1/SkipLinting.cxx diff --git a/Tests/RunCMake/Autogen/SkipLinting.h b/Tests/RunCMake/Autogen_1/SkipLinting.h similarity index 100% rename from Tests/RunCMake/Autogen/SkipLinting.h rename to Tests/RunCMake/Autogen_1/SkipLinting.h diff --git a/Tests/RunCMake/Autogen/empty.cpp b/Tests/RunCMake/Autogen_1/empty.cpp similarity index 100% rename from Tests/RunCMake/Autogen/empty.cpp rename to Tests/RunCMake/Autogen_1/empty.cpp diff --git a/Tests/RunCMake/VS10ProjectWinCE/CMakeLists.txt b/Tests/RunCMake/Autogen_2/CMakeLists.txt similarity index 62% rename from Tests/RunCMake/VS10ProjectWinCE/CMakeLists.txt rename to Tests/RunCMake/Autogen_2/CMakeLists.txt index 91baae7eb..2632ffa91 100644 --- a/Tests/RunCMake/VS10ProjectWinCE/CMakeLists.txt +++ b/Tests/RunCMake/Autogen_2/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5.0) +cmake_minimum_required(VERSION 3.16) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen/MyWindow.cpp b/Tests/RunCMake/Autogen_2/MyWindow.cpp similarity index 100% rename from Tests/RunCMake/Autogen/MyWindow.cpp rename to Tests/RunCMake/Autogen_2/MyWindow.cpp diff --git a/Tests/RunCMake/Autogen/MyWindow.h b/Tests/RunCMake/Autogen_2/MyWindow.h similarity index 100% rename from Tests/RunCMake/Autogen/MyWindow.h rename to Tests/RunCMake/Autogen_2/MyWindow.h diff --git a/Tests/RunCMake/Autogen/MyWindow.ui b/Tests/RunCMake/Autogen_2/MyWindow.ui similarity index 100% rename from Tests/RunCMake/Autogen/MyWindow.ui rename to Tests/RunCMake/Autogen_2/MyWindow.ui diff --git a/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt b/Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt similarity index 100% rename from Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt rename to Tests/RunCMake/Autogen_2/QtAutoMocDeps-stderr.txt diff --git a/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake b/Tests/RunCMake/Autogen_2/QtAutoMocDeps.cmake similarity index 100% rename from Tests/RunCMake/Autogen/QtAutoMocDeps.cmake rename to Tests/RunCMake/Autogen_2/QtAutoMocDeps.cmake diff --git a/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt b/Tests/RunCMake/Autogen_2/QtSubDir1/CMakeLists.txt similarity index 100% rename from Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt rename to Tests/RunCMake/Autogen_2/QtSubDir1/CMakeLists.txt diff --git a/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt b/Tests/RunCMake/Autogen_2/QtSubDir2/CMakeLists.txt similarity index 100% rename from Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt rename to Tests/RunCMake/Autogen_2/QtSubDir2/CMakeLists.txt diff --git a/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Autogen_2/QtSubDir3/CMakeLists.txt similarity index 100% rename from Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt rename to Tests/RunCMake/Autogen_2/QtSubDir3/CMakeLists.txt diff --git a/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake new file mode 100644 index 000000000..e97e89617 --- /dev/null +++ b/Tests/RunCMake/Autogen_2/RunCMakeTest.cmake @@ -0,0 +1,87 @@ +include(RunCMake) + +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + if(RunCMake_GENERATOR MATCHES "Make|Ninja") + block() + if(QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) + if (RunCMake_GENERATOR MATCHES "Ninja Multi-Config") + set(config_list Debug Release RelWithDebInfo) + set(use_better_graph_list ON OFF) + else() + set(config_list single-config) + set(use_better_graph_list OFF) + endif() + + foreach(use_better_graph IN ITEMS ${use_better_graph_list}) + foreach(config IN ITEMS ${config_list}) + block() + if (config STREQUAL "single-config") + set(config_suffix "") + else() + set(config_path "_${config}") + if (use_better_graph) + set(config_suffix "_${config}") + endif() + endif() + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_path}-build) + run_cmake_with_options(QtAutoMocDeps ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=${use_better_graph}) + set(RunCMake_TEST_NO_CLEAN 1) + # Build the project. + if (config STREQUAL "single-config") + set(config_param "") + else() + set(config_param "--config ${config}") + endif() + run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param}) + # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC + # for app_with_qt target. + file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp") + set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target app_with_qt|\ +Automatic MOC for target sub_exe_1|\ +Automatic MOC for target sub_exe_2") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for 'app_with_qt', 'sub_exe_1' and 'sub_exe_2'") + # Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2. + run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param}) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + unset(RunCMake_TEST_NOT_EXPECT_stdout) + + macro(check_file_exists file) + if (EXISTS "${file}") + set(check_result "PASSED") + set(message_type "STATUS") + else() + set(check_result "FAILED") + set(message_type "FATAL_ERROR") + endif() + + message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}") + endmacro() + + check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps${config_suffix}") + check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps${config_suffix}") + check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps${config_suffix}") + + check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp${config_suffix}") + check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp${config_suffix}") + check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp${config_suffix}") + + # Touch a header file to make sure an automoc dependency cycle is not introduced. + file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle") + run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose) + # Need to run a second time to hit the dependency cycle. + set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle") + run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose) + endblock() + endforeach() + endforeach() + endif() + endblock() + endif() +endif () diff --git a/Tests/RunCMake/Autogen/app.cpp b/Tests/RunCMake/Autogen_2/app.cpp similarity index 100% rename from Tests/RunCMake/Autogen/app.cpp rename to Tests/RunCMake/Autogen_2/app.cpp diff --git a/Tests/RunCMake/Autogen/app_qt.cpp b/Tests/RunCMake/Autogen_2/app_qt.cpp similarity index 100% rename from Tests/RunCMake/Autogen/app_qt.cpp rename to Tests/RunCMake/Autogen_2/app_qt.cpp diff --git a/Tests/RunCMake/Autogen/simple_lib.cpp b/Tests/RunCMake/Autogen_2/simple_lib.cpp similarity index 100% rename from Tests/RunCMake/Autogen/simple_lib.cpp rename to Tests/RunCMake/Autogen_2/simple_lib.cpp diff --git a/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake b/Tests/RunCMake/Autogen_3/AutoMocExecutableConfig.cmake similarity index 61% rename from Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake rename to Tests/RunCMake/Autogen_3/AutoMocExecutableConfig.cmake index 3ee9be975..fc6ed7f7e 100644 --- a/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake +++ b/Tests/RunCMake/Autogen_3/AutoMocExecutableConfig.cmake @@ -7,9 +7,9 @@ endif() get_target_property(moc_location Qt${with_qt_version}::moc IMPORTED_LOCATION) set_target_properties(dummy PROPERTIES AUTOMOC_MOC_OPTIONS "EXE_PATH=${moc_location}") -add_executable(mymoc $<$:exe_debug.cpp> - $<$:exe_release.cpp> - $<$:exe_relwithdebinfo.cpp> +add_executable(mymoc $<$:../Autogen_common/exe_debug.cpp> + $<$:../Autogen_common/exe_release.cpp> + $<$:../Autogen_common/exe_relwithdebinfo.cpp> ) set_target_properties(dummy PROPERTIES AUTOMOC_EXECUTABLE $) diff --git a/Tests/RunCMake/Autogen_3/CMakeLists.txt b/Tests/RunCMake/Autogen_3/CMakeLists.txt new file mode 100644 index 000000000..2632ffa91 --- /dev/null +++ b/Tests/RunCMake/Autogen_3/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen/MocExample.cmake b/Tests/RunCMake/Autogen_3/MocExample.cmake similarity index 85% rename from Tests/RunCMake/Autogen/MocExample.cmake rename to Tests/RunCMake/Autogen_3/MocExample.cmake index f06f8f67b..b4f4729fa 100644 --- a/Tests/RunCMake/Autogen/MocExample.cmake +++ b/Tests/RunCMake/Autogen_3/MocExample.cmake @@ -3,7 +3,7 @@ enable_language(CXX) set(CMAKE_CXX_STANDARD 11) find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) -add_library(dummy STATIC example.cpp) +add_library(dummy STATIC ../Autogen_common/example.cpp) target_link_libraries(dummy Qt${with_qt_version}::Core Qt${with_qt_version}::Widgets Qt${with_qt_version}::Gui) diff --git a/Tests/RunCMake/Autogen_3/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_3/RunCMakeTest.cmake new file mode 100644 index 000000000..a2fb3df71 --- /dev/null +++ b/Tests/RunCMake/Autogen_3/RunCMakeTest.cmake @@ -0,0 +1,11 @@ +include(RunCMake) +include(Autogen_common/utils) + +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + autogen_executable_test(Moc) +endif () diff --git a/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake b/Tests/RunCMake/Autogen_4/AutoUicExecutableConfig.cmake similarity index 61% rename from Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake rename to Tests/RunCMake/Autogen_4/AutoUicExecutableConfig.cmake index 55b88b88e..72a0d9f2a 100644 --- a/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake +++ b/Tests/RunCMake/Autogen_4/AutoUicExecutableConfig.cmake @@ -7,9 +7,9 @@ endif() get_target_property(uic_location Qt${with_qt_version}::uic IMPORTED_LOCATION) set_target_properties(dummy PROPERTIES AUTOUIC_OPTIONS "EXE_PATH=${uic_location}") -add_executable(myuic $<$:exe_debug.cpp> - $<$:exe_release.cpp> - $<$:exe_relwithdebinfo.cpp> +add_executable(myuic $<$:../Autogen_common/exe_debug.cpp> + $<$:../Autogen_common/exe_release.cpp> + $<$:../Autogen_common/exe_relwithdebinfo.cpp> ) set_target_properties(dummy PROPERTIES AUTOUIC_EXECUTABLE $) diff --git a/Tests/RunCMake/Autogen_4/CMakeLists.txt b/Tests/RunCMake/Autogen_4/CMakeLists.txt new file mode 100644 index 000000000..2632ffa91 --- /dev/null +++ b/Tests/RunCMake/Autogen_4/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen_4/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_4/RunCMakeTest.cmake new file mode 100644 index 000000000..ac8c5d440 --- /dev/null +++ b/Tests/RunCMake/Autogen_4/RunCMakeTest.cmake @@ -0,0 +1,11 @@ +include(RunCMake) +include(Autogen_common/utils) + +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + autogen_executable_test(Uic) +endif () diff --git a/Tests/RunCMake/Autogen/UicExample.cmake b/Tests/RunCMake/Autogen_4/UicExample.cmake similarity index 100% rename from Tests/RunCMake/Autogen/UicExample.cmake rename to Tests/RunCMake/Autogen_4/UicExample.cmake diff --git a/Tests/RunCMake/Autogen/example_ui.cpp b/Tests/RunCMake/Autogen_4/example_ui.cpp similarity index 100% rename from Tests/RunCMake/Autogen/example_ui.cpp rename to Tests/RunCMake/Autogen_4/example_ui.cpp diff --git a/Tests/RunCMake/Autogen/example_ui.h b/Tests/RunCMake/Autogen_4/example_ui.h similarity index 100% rename from Tests/RunCMake/Autogen/example_ui.h rename to Tests/RunCMake/Autogen_4/example_ui.h diff --git a/Tests/RunCMake/Autogen/uiA.ui b/Tests/RunCMake/Autogen_4/uiA.ui similarity index 100% rename from Tests/RunCMake/Autogen/uiA.ui rename to Tests/RunCMake/Autogen_4/uiA.ui diff --git a/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake b/Tests/RunCMake/Autogen_5/AutoRccExecutableConfig.cmake similarity index 61% rename from Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake rename to Tests/RunCMake/Autogen_5/AutoRccExecutableConfig.cmake index 0e464205d..b15dd72e4 100644 --- a/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake +++ b/Tests/RunCMake/Autogen_5/AutoRccExecutableConfig.cmake @@ -7,9 +7,9 @@ endif() get_target_property(rcc_location Qt${with_qt_version}::rcc IMPORTED_LOCATION) set_target_properties(dummy PROPERTIES AUTORCC_OPTIONS "EXE_PATH=${rcc_location}") -add_executable(myrcc $<$:exe_debug.cpp> - $<$:exe_release.cpp> - $<$:exe_relwithdebinfo.cpp> +add_executable(myrcc $<$:../Autogen_common/exe_debug.cpp> + $<$:../Autogen_common/exe_release.cpp> + $<$:../Autogen_common/exe_relwithdebinfo.cpp> ) set_target_properties(dummy PROPERTIES AUTORCC_EXECUTABLE $) diff --git a/Tests/RunCMake/Autogen_5/CMakeLists.txt b/Tests/RunCMake/Autogen_5/CMakeLists.txt new file mode 100644 index 000000000..2632ffa91 --- /dev/null +++ b/Tests/RunCMake/Autogen_5/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen/RccExample.cmake b/Tests/RunCMake/Autogen_5/RccExample.cmake similarity index 86% rename from Tests/RunCMake/Autogen/RccExample.cmake rename to Tests/RunCMake/Autogen_5/RccExample.cmake index ade0fef8b..9f8dc596a 100644 --- a/Tests/RunCMake/Autogen/RccExample.cmake +++ b/Tests/RunCMake/Autogen_5/RccExample.cmake @@ -3,7 +3,7 @@ enable_language(CXX) set(CMAKE_CXX_STANDARD 11) find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) -add_library(dummy STATIC example.cpp data.qrc) +add_library(dummy STATIC ../Autogen_common/example.cpp data.qrc) target_link_libraries(dummy Qt${with_qt_version}::Core Qt${with_qt_version}::Widgets Qt${with_qt_version}::Gui) diff --git a/Tests/RunCMake/Autogen_5/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_5/RunCMakeTest.cmake new file mode 100644 index 000000000..8060ec4e5 --- /dev/null +++ b/Tests/RunCMake/Autogen_5/RunCMakeTest.cmake @@ -0,0 +1,31 @@ +include(RunCMake) +include(Autogen_common/utils) + +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + autogen_executable_test(Rcc) + if (QtCore_VERSION VERSION_GREATER_EQUAL 6) + if (RunCMake_GENERATOR MATCHES "Make|Ninja") + foreach(value IN ITEMS ON OFF) + block() + set(RunCMake_TEST_BINARY_DIR + ${RunCMake_BINARY_DIR}/RccNoZTSD-${value}-build) + run_cmake_with_options(RccExample ${RunCMake_TEST_OPTIONS} + -DCMAKE_AUTOGEN_VERBOSE=ON -DZSTD_VALUE=${value}) + if (value STREQUAL "OFF") + set(RunCMake_TEST_EXPECT_stdout "--no-zstd") + else() + set(RunCMake_TEST_NOT_EXPECT_stdout "--no-zstd") + endif() + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(RccNoZTSD-${value}-build ${CMAKE_COMMAND} + --build . --config Debug) + endblock() + endforeach() + endif() + endif() +endif () diff --git a/Tests/RunCMake/Autogen/data.qrc b/Tests/RunCMake/Autogen_5/data.qrc similarity index 100% rename from Tests/RunCMake/Autogen/data.qrc rename to Tests/RunCMake/Autogen_5/data.qrc diff --git a/Tests/RunCMake/Autogen_6/CMakeLists.txt b/Tests/RunCMake/Autogen_6/CMakeLists.txt new file mode 100644 index 000000000..2632ffa91 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake new file mode 100644 index 000000000..b629a5cfe --- /dev/null +++ b/Tests/RunCMake/Autogen_6/RunCMakeTest.cmake @@ -0,0 +1,70 @@ +include(RunCMake) +include(Autogen_common/utils) + +if (DEFINED with_qt_version) + set(RunCMake_TEST_OPTIONS + -Dwith_qt_version=${with_qt_version} + "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" + ) + if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) + macro(set_test_variables_for_unwanted_builds) + if (RunCMake_GENERATOR MATCHES "Ninja") + set(RunCMake_TEST_NOT_EXPECT_stdout "widget2.cpp.o.d|mainwindow.cpp.o.d") + elseif (RunCMake_GENERATOR MATCHES "Make") + set(RunCMake_TEST_NOT_EXPECT_stdout "Building CXX object multi_ui_files/CMakeFiles/example.dir/src/widget2.cpp.o|\ + Building CXX object multi_ui_files/CMakeFiles/example.dir/src/mainwindow.cpp.o") + elseif (RunCMake_GENERATOR MATCHES "Visual Studio") + set(RunCMake_TEST_NOT_EXPECT_stdout "widget2.cpp|mainwindow.cpp") + elseif (RunCMake_GENERATOR MATCHES "Xcode") + set(RunCMake_TEST_NOT_EXPECT_stdout "widget2.cpp|mainwindow.cpp") + endif() + endmacro() + + function(uic_build_test test_name binary_dir source_dir file_to_touch test_config) + set(RunCMake_TEST_BINARY_DIR ${binary_dir}) + set(RunCMake_TEST_SOURCE_DIR ${source_dir}) + + if (NOT RunCMake_GENERATOR MATCHES "Visual Studio") + set(test_verbose_arg "--verbose") + endif() + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(config_desc "-${test_config}") + set(RunCMake_TEST_VARIANT_DESCRIPTION "${config_desc}") + set(multiconfig_config_arg "--config ${test_config}") + else() + set(RunCMake_TEST_VARIANT_DESCRIPTION "") + set(config_arg "-DCMAKE_BUILD_TYPE=Debug") + endif() + run_cmake_with_options(${test_name} ${RunCMake_TEST_OPTIONS} ${config_arg}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command("${test_name}-build" ${CMAKE_COMMAND} --build . ${test_verbose_arg} ${multiconfig_config_arg}) + + file(TOUCH ${file_to_touch}) + set(RunCMake_TEST_VARIANT_DESCRIPTION "${config_desc}-first_build_after_touching") + set_test_variables_for_unwanted_builds() + run_cmake_command("${test_name}-build" ${CMAKE_COMMAND} --build . ${test_verbose_arg} ${multiconfig_config_arg}) + message(STATUS "${test_name}-build${config_desc}-Only build files that were touched were built - PASSED") + endfunction() + + if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode") + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(configs "Debug" "Release") + else() + set(configs "single_config") + endif() + + foreach(config IN ITEMS ${configs}) + if (NOT ${config} STREQUAL "single_config") + set(config_desc "-${config}") + endif() + + uic_build_test(multi_ui_files_touch_ui ${RunCMake_BINARY_DIR}/multi_ui_files_touch_ui${config_desc}-build + ${RunCMake_SOURCE_DIR}/multi_ui_files ${RunCMake_SOURCE_DIR}/multi_ui_files/src/widget1.ui ${config}) + + uic_build_test(multi_ui_files_touch_cpp ${RunCMake_BINARY_DIR}/multi_ui_files_touch_cpp${config_desc}-build + ${RunCMake_SOURCE_DIR}/multi_ui_files ${RunCMake_SOURCE_DIR}/multi_ui_files/src/widget1.cpp ${config}) + endforeach() + endif() + endif() +endif () diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/CMakeLists.txt b/Tests/RunCMake/Autogen_6/multi_ui_files/CMakeLists.txt new file mode 100644 index 000000000..a4f720639 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.10) + +project(UicIncrementalBuild LANGUAGES CXX) + +find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +add_executable(example + src/mainwindow.ui + src/widget1.ui + src/widget2.ui + src/mainwindow.h + src/widget1.h + src/widget2.h + src/main.cpp + src/mainwindow.cpp + src/widget1.cpp + src/widget2.cpp +) + +target_link_libraries(example PRIVATE Qt${with_qt_version}::Widgets + Qt${with_qt_version}::Core + Qt${with_qt_version}::Gui) diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/main.cpp b/Tests/RunCMake/Autogen_6/multi_ui_files/src/main.cpp new file mode 100644 index 000000000..8d76ad99f --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/main.cpp @@ -0,0 +1,11 @@ +#include + +#include "mainwindow.h" + +int main(int argc, char* argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.cpp b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.cpp new file mode 100644 index 000000000..eb3d3669d --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.cpp @@ -0,0 +1,25 @@ +#include "mainwindow.h" + +#include + +#include "src/ui_mainwindow.h" +#include "widget1.h" + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + auto layout = new QVBoxLayout; + layout->addWidget(new Widget1); + + QWidget* w = new QWidget(this); + w->setLayout(layout); + + setCentralWidget(w); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.h b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.h new file mode 100644 index 000000000..3e6445964 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow* ui; +}; +#endif // MAINWINDOW_H diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.ui b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.ui new file mode 100644 index 000000000..828d7c178 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/mainwindow.ui @@ -0,0 +1,33 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + + diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.cpp b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.cpp new file mode 100644 index 000000000..05657ce62 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.cpp @@ -0,0 +1,22 @@ +#include "widget1.h" + +#include "src/ui_widget1.h" + +Widget1::Widget1(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget1) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget1::~Widget1() +{ + delete ui; +} + +void Widget1::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.h b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.h new file mode 100644 index 000000000..8c28d6deb --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.h @@ -0,0 +1,23 @@ +#ifndef WIDGET1_H +#define WIDGET1_H + +#include + +namespace Ui { +class Widget1; +} + +class Widget1 : public QWidget +{ + Q_OBJECT +public: + explicit Widget1(QWidget* parent = nullptr); + ~Widget1(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget1* ui; +}; + +#endif // WIDGET1_H diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.ui b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.ui new file mode 100644 index 000000000..db0c58d5d --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget1.ui @@ -0,0 +1,52 @@ + + + Widget1 + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Input: + + + + + + + + + + OnTextChanged: + + + + + + + + + + + + + + TextLabel + + + + + + + + diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.cpp b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.cpp new file mode 100644 index 000000000..7f4bbafce --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.cpp @@ -0,0 +1,22 @@ +#include "widget2.h" + +#include "src/ui_widget2.h" + +Widget2::Widget2(QWidget* parent) + : QWidget(parent) + , ui(new Ui::Widget2) +{ + ui->setupUi(this); + connect(ui->lineEdit, SIGNAL(textChanged(const QString&)), this, + SLOT(onTextChanged(const QString&))); +} + +Widget2::~Widget2() +{ + delete ui; +} + +void Widget2::onTextChanged(const QString& text) +{ + ui->OnTextChanged->setText(text); +} diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.h b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.h new file mode 100644 index 000000000..41d7e1fd0 --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.h @@ -0,0 +1,24 @@ +#ifndef WIDGET2_H +#define WIDGET2_H + +#include + +namespace Ui { +class Widget2; +} + +class Widget2 : public QWidget +{ + Q_OBJECT + +public: + explicit Widget2(QWidget* parent = nullptr); + ~Widget2(); +public slots: + void onTextChanged(const QString& text); + +private: + Ui::Widget2* ui; +}; + +#endif // WIDGET2_H diff --git a/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.ui b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.ui new file mode 100644 index 000000000..facf4678f --- /dev/null +++ b/Tests/RunCMake/Autogen_6/multi_ui_files/src/widget2.ui @@ -0,0 +1,45 @@ + + + Widget2 + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Input: + + + + + + + + + + OnTextChanged: + + + + + + + + + + + + + + + diff --git a/Tests/RunCMake/Autogen/example.cpp b/Tests/RunCMake/Autogen_common/example.cpp similarity index 100% rename from Tests/RunCMake/Autogen/example.cpp rename to Tests/RunCMake/Autogen_common/example.cpp diff --git a/Tests/RunCMake/Autogen/example.h b/Tests/RunCMake/Autogen_common/example.h similarity index 100% rename from Tests/RunCMake/Autogen/example.h rename to Tests/RunCMake/Autogen_common/example.h diff --git a/Tests/RunCMake/Autogen/exe_common.h b/Tests/RunCMake/Autogen_common/exe_common.h similarity index 100% rename from Tests/RunCMake/Autogen/exe_common.h rename to Tests/RunCMake/Autogen_common/exe_common.h diff --git a/Tests/RunCMake/Autogen/exe_debug.cpp b/Tests/RunCMake/Autogen_common/exe_debug.cpp similarity index 100% rename from Tests/RunCMake/Autogen/exe_debug.cpp rename to Tests/RunCMake/Autogen_common/exe_debug.cpp diff --git a/Tests/RunCMake/Autogen/exe_release.cpp b/Tests/RunCMake/Autogen_common/exe_release.cpp similarity index 100% rename from Tests/RunCMake/Autogen/exe_release.cpp rename to Tests/RunCMake/Autogen_common/exe_release.cpp diff --git a/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp b/Tests/RunCMake/Autogen_common/exe_relwithdebinfo.cpp similarity index 100% rename from Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp rename to Tests/RunCMake/Autogen_common/exe_relwithdebinfo.cpp diff --git a/Tests/RunCMake/Autogen_common/utils.cmake b/Tests/RunCMake/Autogen_common/utils.cmake new file mode 100644 index 000000000..0692ca519 --- /dev/null +++ b/Tests/RunCMake/Autogen_common/utils.cmake @@ -0,0 +1,266 @@ +function(run_make_program dir) + execute_process( + COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN} + WORKING_DIRECTORY "${dir}" + OUTPUT_VARIABLE make_program_stdout + ERROR_VARIABLE make_program_stderr + RESULT_VARIABLE make_program_result + ) + if (NOT DEFINED RunMakeProgram_expected_result) + set(RunMakeProgram_expected_result 0) + endif() + if(NOT "${make_program_result}" MATCHES "${RunMakeProgram_expected_result}") + message(STATUS " +============ beginning of ${RunCMake_MAKE_PROGRAM}'s stdout ============ +${make_program_stdout} +=============== end of ${RunCMake_MAKE_PROGRAM}'s stdout =============== +") + message(STATUS " +============ beginning of ${RunCMake_MAKE_PROGRAM}'s stderr ============ +${make_program_stderr} +=============== end of ${RunCMake_MAKE_PROGRAM}'s stderr =============== +") + message(FATAL_ERROR + "top ${RunCMake_MAKE_PROGRAM} build failed exited with status ${make_program_result}") + endif() + set(make_program_stdout "${make_program_stdout}" PARENT_SCOPE) +endfunction(run_make_program) + +function(count_substring STRING SUBSTRING COUNT_VAR) + string(LENGTH "${STRING}" STRING_LENGTH) + string(LENGTH "${SUBSTRING}" SUBSTRING_LENGTH) + if (SUBSTRING_LENGTH EQUAL 0) + message(FATAL_ERROR "SUBSTRING_LENGTH is 0") + endif() + + if (STRING_LENGTH EQUAL 0) + message(FATAL_ERROR "STRING_LENGTH is 0") + endif() + + if (STRING_LENGTH LESS SUBSTRING_LENGTH) + message(FATAL_ERROR "STRING_LENGTH is less than SUBSTRING_LENGTH") + endif() + + set(COUNT 0) + string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START) + while(SUBSTRING_START GREATER_EQUAL 0) + math(EXPR COUNT "${COUNT} + 1") + math(EXPR SUBSTRING_START "${SUBSTRING_START} + ${SUBSTRING_LENGTH}") + string(SUBSTRING "${STRING}" ${SUBSTRING_START} -1 STRING) + string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START) + endwhile() + + set(${COUNT_VAR} ${COUNT} PARENT_SCOPE) +endfunction() + +function(not_expect make_program_stdout unexpected_output test_name) + count_substring("${make_program_stdout}" "${unexpected_output}" count) + if(NOT count EQUAL 0) + message(STATUS "${test_name}-not_expect - FAILED") + message(FATAL_ERROR "Expected to find ${unexpected_output} exactly 0 times in ${make_program_stdout} but found ${count} occurrences of ${unexpected_output}") + else() + message(STATUS "${test_name}-not_expect - PASSED") + endif() +endfunction() + +function(expect_only_once make_program_stdout expected_output test_name) + count_substring("${make_program_stdout}" "${expected_output}" count) + if(NOT count EQUAL 1) + message(STATUS "${test_name}-expect_only_once - FAILED") + message(FATAL_ERROR "Expected to find ${expected_output} exactly once in ${make_program_stdout} but found ${count} occurrences of ${expected_output}") + else() + message(STATUS "${test_name}-expect_only_once - PASSED") + endif() +endfunction() + +function(expect_n_times string_to_check expected_output expected_count test_name) + count_substring("${string_to_check}" "${expected_output}" count) + if(NOT count EQUAL ${expected_count}) + message(STATUS "${test_name}-expect_${expected_count}_times - FAILED") + message(FATAL_ERROR "Expected to find ${expected_output} exactly ${expected_count} times in ${string_to_check} but found ${count} occurrences of ${expected_output}") + else() + message(STATUS "${test_name}-expect_${expected_count}_times - PASSED") + endif() +endfunction() + +function(autogen_executable_test exe) + if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0) + if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config") + block() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-multi-config-build) + run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + set(RunCMake_TEST_NO_CLEAN 1) + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_running_exe_${config}") + run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config}) + endblock() + endforeach() + set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do") + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_no_work_to_do") + run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config}) + endblock() + endforeach() + endblock() + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build) + run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${config}.ninja) + + set(expected_output "running_exe_${config}") + expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}") + + foreach(sub_config IN ITEMS Debug Release RelWithDebInfo) + if(NOT sub_config STREQUAL config) + set(unexpected_output "running_exe_${sub_config}") + not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig-${config}-${unexpected_output}") + endif() + endforeach() + + if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") + set(expected_output "cmake_autogen") + else() + set(expected_output "cmake_autorcc") + endif() + expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}") + endblock() + endforeach() + endblock() + block() + foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) + foreach(target_config IN ITEMS Debug Release RelWithDebInfo) + block() + set(TEST_SUFFIX "-CrossConfig-${ninja_config}-${target_config}") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX}) + run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=${ninja_config} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + + run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja dummy:${target_config}) + + set(expected_output "running_exe_${ninja_config}") + expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}") + + foreach(sub_config IN ITEMS Debug Release RelWithDebInfo) + if(NOT sub_config STREQUAL ninja_config) + set(unexpected_output "running_exe_${sub_config}") + not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${unexpected_output}") + endif() + endforeach() + + if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") + set(expected_output "cmake_autogen") + else() + set(expected_output "cmake_autorcc") + endif() + expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}") + endblock() + endforeach() + endforeach() + endblock() + block() + foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) + set(TEST_SUFFIX "-CrossConfig-${ninja_config}-all-all") + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX}) + run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja all:all) + endforeach() + endblock() + elseif (RunCMake_GENERATOR MATCHES "Ninja|Make") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build) + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}") + run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_BUILD_TYPE=${config} -DCMAKE_AUTOGEN_VERBOSE=ON) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*") + run_cmake_command(Auto${exe}ExecutableConfig-${config}-build ${CMAKE_COMMAND} --build .) + endblock() + endforeach() + endblock() + endif() + endif() + + # Visual Studio specific dependency tests + if (RunCMake_GENERATOR MATCHES "Visual Studio") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") + run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + set(RunCMake_TEST_NO_CLEAN 1) + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-first-build") + run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config}) + endblock() + endforeach() + foreach(config IN ITEMS Debug Release RelWithDebInfo) + block() + if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") + set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}") + set(not_expect_descripton "Auto${exe}") + else () + set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}") + set(not_expect_descripton "Auto${exe}") + endif() + set(RunCMake_TEST_VARIANT_DESCRIPTION "-second-build-${config}_expect_no_${not_expect_descripton}") + run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config}) + endblock() + endforeach() + endblock() + endif() + + if (RunCMake_GENERATOR MATCHES "Xcode") + block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure") + set(RunCMake_TEST_EXPECT_stderr ".*") + run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_MAKE_PROGRAM ${CMAKE_COMMAND}) + run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config Debug) + if (exe STREQUAL "Moc") + set(expected_count 4) + elseif (exe STREQUAL "Uic") + set(expected_count 1) + else() + set(expected_count 12) + endif() + expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}") + + if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") + expect_n_times("${make_program_stdout}" "AutoGen:" 5 "${exe}Example-build-AutoGen:") + endif() + + + foreach(config IN ITEMS Release RelWithDebInfo) + block() + # Note: We expect to see Auto${exe} or AutoGen in the output for + # moc and uic because they should be triggered per configuration. + # For rcc, we don't expect to see Auto${exe} or AutoGen in the output + # because all configurations trigger with the first configuration. + run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config ${config}) + if (exe STREQUAL "Moc" OR exe STREQUAL "Uic") + expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-${config}-Auto${exe}") + expect_n_times("${make_program_stdout}" "AutoGen" 5 "${exe}Example-build-${config}-AutoGen:") + else() + not_expect("${make_program_stdout}" "Auto${exe}" "${exe}Example-build-${config}_Auto${exe}") + not_expect("${make_program_stdout}" "AutoGen" "${exe}Example-build-${config}_AutoGen") + endif() + endblock() + endforeach() + endblock() + endif() +endfunction() diff --git a/Tests/RunCMake/BuildDepends/CMakeLists.txt b/Tests/RunCMake/BuildDepends/CMakeLists.txt index 8eb57486a..30c0273a6 100644 --- a/Tests/RunCMake/BuildDepends/CMakeLists.txt +++ b/Tests/RunCMake/BuildDepends/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/BuildDepends/CompileDepends.c b/Tests/RunCMake/BuildDepends/CompileDepends.c new file mode 100644 index 000000000..b627738e5 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompileDepends.c @@ -0,0 +1,5 @@ +#include +int main(void) +{ + return COUNT; +} diff --git a/Tests/RunCMake/BuildDepends/CompileDepends.cmake b/Tests/RunCMake/BuildDepends/CompileDepends.cmake new file mode 100644 index 000000000..69d7ffabd --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompileDepends.cmake @@ -0,0 +1,78 @@ +enable_language(C) + +add_executable(main CompileDepends.c) +target_include_directories(main PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +set(CODE_WITH_SPACE [[ +add_executable(main2 ../CompileDepends.c) +target_include_directories(main2 PRIVATE ${CMAKE_BINARY_DIR}) +]]) +if(MAKE_SUPPORTS_SPACES) + add_subdirectory("With Space") + set(check_pairs_with_space " + \"$|${CMAKE_CURRENT_BINARY_DIR}/CompileDepends.h\" + ") + set(check_exes_with_space " + \"$\" + ") +endif() + +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$>.cmake CONTENT " +cmake_minimum_required(VERSION ${CMAKE_VERSION}) +set(check_pairs + \"$|${CMAKE_CURRENT_BINARY_DIR}/CompileDepends.h\" + ${check_pairs_with_space} + ) +set(check_exes + \"$\" + ${check_exes_with_space} + ) + +if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2) + include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\") + if (NOT CMAKE_DEPEND_INFO_FILES) + set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\") + else() + list(FILTER CMAKE_DEPEND_INFO_FILES EXCLUDE REGEX main2) + foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES) + include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\") + if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES) + set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPENDS_DEPENDENCY_FILES not found.\") + else() + list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 1 OBJECT_FILE) + list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 3 DEP_FILE) + if (NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") + set(RunCMake_TEST_FAILED \"File \${DEP_FILE} not found.\") + else() + set (TARGET_DEP_FILE \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") + cmake_path(REPLACE_FILENAME TARGET_DEP_FILE \"compiler_depend.make\") + file(READ \"\${TARGET_DEP_FILE}\" DEPENDS_CONTENT) + if (WIN32) + string (REPLACE \"\\\\\" \"/\" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") + string (TOLOWER \"\${DEPENDS_CONTENT}\" DEPENDS_CONTENT) + string (TOLOWER \"\${OBJECT_FILE}\" OBJECT_FILE) + else() + string(REPLACE \"\\\\ \" \" \" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") + endif() + if(NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+[Cc]ompile[Dd]epends.c\" + OR NOT DEPENDS_CONTENT MATCHES \"[Cc]ompile[Dd]epends.h\") + set(RunCMake_TEST_FAILED \"Dependency file badly generated:\n \${TARGET_DEP_FILE}\") + endif() + endif() + endif() + endforeach() + endif() +endif() + +if (RunCMake_GENERATOR STREQUAL \"Ninja\" AND check_step EQUAL 2) + execute_process( + COMMAND \${RunCMake_MAKE_PROGRAM} -t deps + WORKING_DIRECTORY \${RunCMake_TEST_BINARY_DIR} + OUTPUT_VARIABLE deps + ) + if(NOT deps MATCHES \"CompileDepends.c\" OR NOT deps MATCHES \"CompileDepends.h\") + string(REPLACE deps \"\\n\" \"\\n \" \" \${deps}\") + set(RunCMake_TEST_FAILED \"Dependencies not detected correctly:\\n\${deps}\") + endif() +endif() +") diff --git a/Tests/RunCMake/BuildDepends/CompileDepends.step1.cmake b/Tests/RunCMake/BuildDepends/CompileDepends.step1.cmake new file mode 100644 index 000000000..63ac0d631 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompileDepends.step1.cmake @@ -0,0 +1,3 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/CompileDepends.h" [[ +#define COUNT 1 +]]) diff --git a/Tests/RunCMake/BuildDepends/CompileDepends.step2.cmake b/Tests/RunCMake/BuildDepends/CompileDepends.step2.cmake new file mode 100644 index 000000000..5b57df00d --- /dev/null +++ b/Tests/RunCMake/BuildDepends/CompileDepends.step2.cmake @@ -0,0 +1,3 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/CompileDepends.h" [[ +#define COUNT 2 +]]) diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake deleted file mode 100644 index 8a9e6008b..000000000 --- a/Tests/RunCMake/BuildDepends/CompilerDependencies.cmake +++ /dev/null @@ -1,46 +0,0 @@ -enable_language(C) - -add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c) - -file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$>.cmake CONTENT " -set(check_pairs - \"$|${CMAKE_CURRENT_BINARY_DIR}/main.c\" - \"$|${CMAKE_CURRENT_BINARY_DIR}/main.h\" - ) -set(check_exes - \"$\" - ) - -if (check_step EQUAL 2) - include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\") - if (NOT CMAKE_DEPEND_INFO_FILES) - set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\") - else() - include(\"${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_DEPEND_INFO_FILES}\") - if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES) - set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPENDS_DEPENDENCY_FILES not found.\") - else() - list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 1 OBJECT_FILE) - list(GET CMAKE_DEPENDS_DEPENDENCY_FILES 3 DEP_FILE) - if (NOT EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") - set(RunCMake_TEST_FAILED \"File \${DEP_FILE} not found.\") - else() - set (TARGET_DEP_FILE \"${CMAKE_CURRENT_BINARY_DIR}/\${DEP_FILE}\") - cmake_path(REPLACE_FILENAME TARGET_DEP_FILE \"compiler_depend.make\") - file(READ \"\${TARGET_DEP_FILE}\" DEPENDS_CONTENT) - if (WIN32) - string (REPLACE \"\\\\\" \"/\" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") - string (TOLOWER \"\${DEPENDS_CONTENT}\" DEPENDS_CONTENT) - string (TOLOWER \"\${OBJECT_FILE}\" OBJECT_FILE) - else() - string(REPLACE \"\\\\ \" \" \" DEPENDS_CONTENT \"\${DEPENDS_CONTENT}\") - endif() - if(NOT DEPENDS_CONTENT MATCHES \"\${OBJECT_FILE} *:.+main.c\" - OR NOT DEPENDS_CONTENT MATCHES \"main.h\") - set(RunCMake_TEST_FAILED \"Dependency file '\${TARGET_DEP_FILE}' badly generated.\") - endif() - endif() - endif() - endif() -endif() -") diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake deleted file mode 100644 index 1da2593cd..000000000 --- a/Tests/RunCMake/BuildDepends/CompilerDependencies.step1.cmake +++ /dev/null @@ -1,9 +0,0 @@ -file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.h" [[ -#define COUNT 1 -]]) - -file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.c" [[ -#include "main.h" - -int main(void) { return COUNT; } -]]) diff --git a/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake b/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake deleted file mode 100644 index e983665f8..000000000 --- a/Tests/RunCMake/BuildDepends/CompilerDependencies.step2.cmake +++ /dev/null @@ -1,3 +0,0 @@ -file(WRITE "${RunCMake_TEST_BINARY_DIR}/main.h" [[ -#define COUNT 2 -]]) diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-result.txt b/Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs-result.txt similarity index 100% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-result.txt rename to Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs-result.txt diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-stderr.txt b/Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs-stderr.txt similarity index 63% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-stderr.txt rename to Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs-stderr.txt index cddea3c18..03a5a75c7 100644 --- a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs-stderr.txt +++ b/Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs-stderr.txt @@ -1,4 +1,4 @@ -CMake Error at CustomCommandDependencies-BadArgs.cmake:[0-9]+ \(add_custom_command\): +CMake Error at CustomCommandDepends-BadArgs.cmake:[0-9]+ \(add_custom_command\): add_custom_command IMPLICIT_DEPENDS and DEPFILE can not both be specified. Call Stack \(most recent call first\): CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs.cmake similarity index 100% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies-BadArgs.cmake rename to Tests/RunCMake/BuildDepends/CustomCommandDepends-BadArgs.cmake diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDepends-compiler-deps-legacy.cmake similarity index 100% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake rename to Tests/RunCMake/BuildDepends/CustomCommandDepends-compiler-deps-legacy.cmake diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDepends.cmake similarity index 72% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies.cmake rename to Tests/RunCMake/BuildDepends/CustomCommandDepends.cmake index 28bbf114f..3a07c5535 100644 --- a/Tests/RunCMake/BuildDepends/CustomCommandDependencies.cmake +++ b/Tests/RunCMake/BuildDepends/CustomCommandDepends.cmake @@ -1,3 +1,4 @@ +cmake_policy(SET CMP0116 NEW) enable_language(C) add_custom_command(OUTPUT main.c @@ -10,21 +11,46 @@ add_custom_target(mainc ALL DEPENDS main.c) add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c) +set(CODE_WITH_SPACE [[ +add_custom_command(OUTPUT main2.c + DEPFILE main2.c.d + COMMAND "${CMAKE_COMMAND}" -DINFILE=../main.c.in -DOUTFILE=main2.c -DDEPFILE=main2.c.d + -P "${CMAKE_SOURCE_DIR}/GenerateDepFile.cmake" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +add_custom_target(main2c ALL DEPENDS main2.c) + +add_executable(main2 ${CMAKE_CURRENT_BINARY_DIR}/main2.c) +]]) +if(MAKE_SUPPORTS_SPACES) + add_subdirectory("With Space") + set(check_pairs_with_space " + \"$|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\" + \"$|${CMAKE_CURRENT_BINARY_DIR}/With Space/main2.c\" + ") + set(check_exes_with_space " + \"$\" + ") +endif() + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/check-$>.cmake CONTENT " cmake_minimum_required(VERSION 3.19) set(check_pairs \"$|${CMAKE_CURRENT_BINARY_DIR}/main.c.in\" \"$|${CMAKE_CURRENT_BINARY_DIR}/main.c\" + ${check_pairs_with_space} ) set(check_exes \"$\" + ${check_exes_with_space} ) -if (check_step EQUAL 2) +if (RunCMake_GENERATOR MATCHES \"Make\" AND check_step EQUAL 2) include(\"${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Makefile.cmake\") if (NOT CMAKE_DEPEND_INFO_FILES) set(RunCMake_TEST_FAILED \"Variable CMAKE_DEPEND_INFO_FILES not found.\") else() + list(FILTER CMAKE_DEPEND_INFO_FILES EXCLUDE REGEX main2) foreach(DEPEND_INFO_FILE IN LISTS CMAKE_DEPEND_INFO_FILES) include(\"${CMAKE_CURRENT_BINARY_DIR}/\${DEPEND_INFO_FILE}\") if (NOT CMAKE_DEPENDS_DEPENDENCY_FILES) @@ -70,4 +96,16 @@ if (check_step EQUAL 2) endforeach() endif() endif() + +if (RunCMake_GENERATOR STREQUAL \"Ninja\" AND check_step EQUAL 2) + execute_process( + COMMAND \${RunCMake_MAKE_PROGRAM} -t deps + WORKING_DIRECTORY \${RunCMake_TEST_BINARY_DIR} + OUTPUT_VARIABLE deps + ) + if(NOT deps MATCHES \"main.c:.*main.c.in\") + string(REPLACE deps \"\\n\" \"\\n \" \" \${deps}\") + set(RunCMake_TEST_FAILED \"Dependencies not detected correctly:\\n\${deps}\") + endif() +endif() ") diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies.step1.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDepends.step1.cmake similarity index 100% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies.step1.cmake rename to Tests/RunCMake/BuildDepends/CustomCommandDepends.step1.cmake diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies.step2.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDepends.step2.cmake similarity index 100% rename from Tests/RunCMake/BuildDepends/CustomCommandDependencies.step2.cmake rename to Tests/RunCMake/BuildDepends/CustomCommandDepends.step2.cmake diff --git a/Tests/RunCMake/BuildDepends/LinkDepends.cmake b/Tests/RunCMake/BuildDepends/LinkDepends.cmake index a414e0359..287f60838 100644 --- a/Tests/RunCMake/BuildDepends/LinkDepends.cmake +++ b/Tests/RunCMake/BuildDepends/LinkDepends.cmake @@ -1,22 +1,38 @@ - enable_language(C) include("${CMAKE_BINARY_DIR}/../LinkDependsExternalLibrary-build/ExternalLibrary-debug.cmake") cmake_path(GET EXTERNAL_LIBRARY PARENT_PATH EXTERNAL_DIR) -add_library(LinkDependsLib SHARED "${CMAKE_CURRENT_BINARY_DIR}/lib_depends.c") +add_library(LinkDependsLib SHARED LinkDependsLib.c) target_link_directories(LinkDependsLib PRIVATE "${EXTERNAL_DIR}") target_link_libraries(LinkDependsLib PRIVATE External) -add_executable(LinkDependsExe "${CMAKE_CURRENT_BINARY_DIR}/exe_depends.c") +add_executable(LinkDependsExe LinkDependsExe.c) target_link_directories(LinkDependsExe PRIVATE "${EXTERNAL_DIR}") target_link_libraries(LinkDependsExe PRIVATE External) +set(CODE_WITH_SPACE [[ +add_library(LinkDependsLib2 SHARED ../LinkDependsLib.c) +target_link_directories(LinkDependsLib2 PRIVATE "${EXTERNAL_DIR}") +target_link_libraries(LinkDependsLib2 PRIVATE External) + +add_executable(LinkDependsExe2 ../LinkDependsExe.c) +target_link_directories(LinkDependsExe2 PRIVATE "${EXTERNAL_DIR}") +target_link_libraries(LinkDependsExe2 PRIVATE External) +]]) +if(MAKE_SUPPORTS_SPACES) + add_subdirectory("With Space") + set(check_pairs_with_space " + \"$|${EXTERNAL_LIBRARY}\" + \"$|${EXTERNAL_LIBRARY}\" + ") +endif() -file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check-$>.cmake" +file(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/check-$>.cmake" CONTENT " set(check_pairs \"$|${EXTERNAL_LIBRARY}\" \"$|${EXTERNAL_LIBRARY}\" + ${check_pairs_with_space} ) ") diff --git a/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake b/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake deleted file mode 100644 index 5ce55b017..000000000 --- a/Tests/RunCMake/BuildDepends/LinkDepends.step1.cmake +++ /dev/null @@ -1,23 +0,0 @@ - -file(WRITE "${RunCMake_TEST_BINARY_DIR}/lib_depends.c" [[ - -extern void external(void); - -void lib_depends(void) -{ - external(); -} -]]) - - -file(WRITE "${RunCMake_TEST_BINARY_DIR}/exe_depends.c" [[ - -extern void external(void); - -int main(void) -{ - external(); - - return 0; -} -]]) diff --git a/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake b/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake index 596e1eb7d..f6d4a6c30 100644 --- a/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake +++ b/Tests/RunCMake/BuildDepends/LinkDependsCheck.cmake @@ -1,9 +1,12 @@ enable_language(C) -file(WRITE "${CMAKE_BINARY_DIR}/LinkDependsUseLinker.cmake" - "set(CMAKE_LINK_DEPENDS_USE_LINKER ${CMAKE_LINK_DEPENDS_USE_LINKER}) -set(CMAKE_C_LINK_DEPENDS_USE_LINKER ${CMAKE_C_LINK_DEPENDS_USE_LINKER})\n") +file(WRITE "${CMAKE_BINARY_DIR}/LinkDependsUseLinker.cmake" " +set(CMAKE_LINK_DEPENDS_USE_LINKER ${CMAKE_LINK_DEPENDS_USE_LINKER}) +set(CMAKE_C_LINK_DEPENDS_USE_LINKER ${CMAKE_C_LINK_DEPENDS_USE_LINKER}) +set(CMAKE_C_COMPILER_LINKER_ID ${CMAKE_C_COMPILER_LINKER_ID}) +set(CMAKE_C_COMPILER_LINKER_VERSION ${CMAKE_C_COMPILER_LINKER_VERSION}) +") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/check-$>.cmake" diff --git a/Tests/RunCMake/BuildDepends/LinkDependsExe.c b/Tests/RunCMake/BuildDepends/LinkDependsExe.c new file mode 100644 index 000000000..842938681 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDependsExe.c @@ -0,0 +1,5 @@ +extern int external(void); +int main(void) +{ + return external(); +} diff --git a/Tests/RunCMake/BuildDepends/LinkDependsLib.c b/Tests/RunCMake/BuildDepends/LinkDependsLib.c new file mode 100644 index 000000000..afde58ee1 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/LinkDependsLib.c @@ -0,0 +1,5 @@ +extern int external(void); +int use_external(void) +{ + return external(); +} diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index a5f962255..5176f5bb3 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -33,6 +33,7 @@ function(run_BuildDepends CASE) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif() + list(APPEND RunCMake_TEST_OPTIONS ${ARGN}) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") include(${RunCMake_SOURCE_DIR}/${CASE}.step1.cmake OPTIONAL) @@ -68,7 +69,10 @@ if(NOT RunCMake_GENERATOR STREQUAL "Xcode") unset(run_BuildDepends_skip_step_2) endif() -if(CMake_TEST_Fortran) +if(CMake_TEST_Fortran + # FIXME(lfortran): The compiler fails on the test's includes. + AND NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran" + ) run_BuildDepends(FortranInclude) endif() @@ -126,7 +130,7 @@ function(run_ReGeneration) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}") set(ProjectHeader [=[ - cmake_minimum_required(VERSION 3.5) + cmake_minimum_required(VERSION 3.10) project(Regenerate-Project NONE) ]=]) @@ -167,16 +171,18 @@ if ((RunCMake_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")) OR (RunCMake_GENERATOR STREQUAL "NMake Makefiles" AND MSVC_VERSION GREATER 1300 - AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")) - run_BuildDepends(CompilerDependencies) - run_BuildDepends(CustomCommandDependencies) + AND CMAKE_C_COMPILER_ID STREQUAL "MSVC") + OR RunCMake_GENERATOR MATCHES "Ninja" + ) + run_BuildDepends(CompileDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES}) + run_BuildDepends(CustomCommandDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES}) endif() if (RunCMake_GENERATOR MATCHES "Makefiles") - run_cmake(CustomCommandDependencies-BadArgs) - run_cmake_with_options(CustomCommandDependencies-compiler-deps-legacy -DCMAKE_DEPENDS_USE_COMPILER=FALSE) + run_cmake(CustomCommandDepends-BadArgs) + run_cmake_with_options(CustomCommandDepends-compiler-deps-legacy -DCMAKE_DEPENDS_USE_COMPILER=FALSE) set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(CustomCommandDependencies-compiler-deps-legacy ${CMAKE_COMMAND} --build . --config Debug) + run_cmake_command(CustomCommandDepends-compiler-deps-legacy ${CMAKE_COMMAND} --build . --config Debug) unset(RunCMake_TEST_NO_CLEAN) endif() @@ -202,9 +208,12 @@ if (RunCMake_GENERATOR MATCHES "Make|Ninja") run_BuildDepends(LinkDependsCheck) include("${RunCMake_BINARY_DIR}/LinkDependsCheck-build/LinkDependsUseLinker.cmake") if ((NOT DEFINED CMAKE_LINK_DEPENDS_USE_LINKER OR CMAKE_LINK_DEPENDS_USE_LINKER) - AND CMAKE_C_LINK_DEPENDS_USE_LINKER) + AND CMAKE_C_LINK_DEPENDS_USE_LINKER + # FIXME(#26401): GNU binutils 2.43 broke dependency-file generation. + AND NOT (CMAKE_C_COMPILER_LINKER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_LINKER_VERSION VERSION_GREATER_EQUAL "2.43") + ) run_BuildDepends(LinkDependsExternalLibrary) unset(run_BuildDepends_skip_step_2) - run_BuildDepends(LinkDepends) + run_BuildDepends(LinkDepends -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES}) endif() endif() diff --git a/Tests/RunCMake/BuildDepends/With Space/CMakeLists.txt b/Tests/RunCMake/BuildDepends/With Space/CMakeLists.txt new file mode 100644 index 000000000..981c798c7 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/With Space/CMakeLists.txt @@ -0,0 +1 @@ +cmake_language(EVAL CODE "${CODE_WITH_SPACE}") diff --git a/Tests/RunCMake/CMP0004/CMakeLists.txt b/Tests/RunCMake/CMP0004/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CMP0004/CMakeLists.txt +++ b/Tests/RunCMake/CMP0004/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0068/CMakeLists.txt b/Tests/RunCMake/CMP0068/CMakeLists.txt index 375cbdb5f..1b06bf560 100644 --- a/Tests/RunCMake/CMP0068/CMakeLists.txt +++ b/Tests/RunCMake/CMP0068/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.8) # old enough to not set CMP0068 project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0068/RunCMakeTest.cmake b/Tests/RunCMake/CMP0068/RunCMakeTest.cmake index 88a622537..49734ca93 100644 --- a/Tests/RunCMake/CMP0068/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0068/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0068-OLD) run_cmake(CMP0068-NEW) diff --git a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake index 61283252c..12d20cacb 100644 --- a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake @@ -1,4 +1,5 @@ include(RunCMake) +set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON) run_cmake(CMP0069-OLD) run_cmake(CMP0069-NEW-cmake) diff --git a/Tests/RunCMake/CMP0129/C-NEW.cmake b/Tests/RunCMake/CMP0129/C-NEW.cmake new file mode 100644 index 000000000..1c8f4c054 --- /dev/null +++ b/Tests/RunCMake/CMP0129/C-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 NEW) +include(C-common.cmake) diff --git a/Tests/RunCMake/CMP0129/C-OLD-stderr.txt b/Tests/RunCMake/CMP0129/C-OLD-stderr.txt new file mode 100644 index 000000000..5c622d2de --- /dev/null +++ b/Tests/RunCMake/CMP0129/C-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at C-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0129 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMP0129/C-OLD.cmake b/Tests/RunCMake/CMP0129/C-OLD.cmake new file mode 100644 index 000000000..789bf6469 --- /dev/null +++ b/Tests/RunCMake/CMP0129/C-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 OLD) +include(C-common.cmake) diff --git a/Tests/RunCMake/CMP0129/C-WARN.cmake b/Tests/RunCMake/CMP0129/C-WARN.cmake new file mode 100644 index 000000000..09b5167b0 --- /dev/null +++ b/Tests/RunCMake/CMP0129/C-WARN.cmake @@ -0,0 +1 @@ +include(C-common.cmake) diff --git a/Tests/RunCMake/CMP0129/C.cmake b/Tests/RunCMake/CMP0129/C-common.cmake similarity index 63% rename from Tests/RunCMake/CMP0129/C.cmake rename to Tests/RunCMake/CMP0129/C-common.cmake index e9ebe90e1..1e91327c5 100644 --- a/Tests/RunCMake/CMP0129/C.cmake +++ b/Tests/RunCMake/CMP0129/C-common.cmake @@ -1,7 +1,3 @@ -if(SET_CMP0129) - cmake_policy(SET CMP0129 ${SET_CMP0129}) -endif() - enable_language(C) set(CMAKE_VERBOSE_MAKEFILE TRUE) include(CompareCompilerVersion.cmake) diff --git a/Tests/RunCMake/CMP0129/CMakeLists.txt b/Tests/RunCMake/CMP0129/CMakeLists.txt index d8200fca0..f5cf5b716 100644 --- a/Tests/RunCMake/CMP0129/CMakeLists.txt +++ b/Tests/RunCMake/CMP0129/CMakeLists.txt @@ -1,3 +1,3 @@ cmake_minimum_required(VERSION 3.22) project(${RunCMake_TEST} NONE) -include(${RunCMake_TEST}.cmake) +include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/CMP0129/CXX-NEW.cmake b/Tests/RunCMake/CMP0129/CXX-NEW.cmake new file mode 100644 index 000000000..00cd7c5e0 --- /dev/null +++ b/Tests/RunCMake/CMP0129/CXX-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 NEW) +include(CXX-common.cmake) diff --git a/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt b/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt new file mode 100644 index 000000000..71a1ecfd6 --- /dev/null +++ b/Tests/RunCMake/CMP0129/CXX-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at CXX-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0129 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMP0129/CXX-OLD.cmake b/Tests/RunCMake/CMP0129/CXX-OLD.cmake new file mode 100644 index 000000000..b7d864103 --- /dev/null +++ b/Tests/RunCMake/CMP0129/CXX-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 OLD) +include(CXX-common.cmake) diff --git a/Tests/RunCMake/CMP0129/CXX-WARN.cmake b/Tests/RunCMake/CMP0129/CXX-WARN.cmake new file mode 100644 index 000000000..db36956a7 --- /dev/null +++ b/Tests/RunCMake/CMP0129/CXX-WARN.cmake @@ -0,0 +1 @@ +include(CXX-common.cmake) diff --git a/Tests/RunCMake/CMP0129/CXX.cmake b/Tests/RunCMake/CMP0129/CXX-common.cmake similarity index 64% rename from Tests/RunCMake/CMP0129/CXX.cmake rename to Tests/RunCMake/CMP0129/CXX-common.cmake index ffb81b84c..016e8ffbc 100644 --- a/Tests/RunCMake/CMP0129/CXX.cmake +++ b/Tests/RunCMake/CMP0129/CXX-common.cmake @@ -1,7 +1,3 @@ -if(SET_CMP0129) - cmake_policy(SET CMP0129 ${SET_CMP0129}) -endif() - enable_language(CXX) set(CMAKE_VERBOSE_MAKEFILE TRUE) include(CompareCompilerVersion.cmake) diff --git a/Tests/RunCMake/CMP0129/Fortran-NEW.cmake b/Tests/RunCMake/CMP0129/Fortran-NEW.cmake new file mode 100644 index 000000000..6ac9cb92a --- /dev/null +++ b/Tests/RunCMake/CMP0129/Fortran-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 NEW) +include(Fortran-common.cmake) diff --git a/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt b/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt new file mode 100644 index 000000000..01260c019 --- /dev/null +++ b/Tests/RunCMake/CMP0129/Fortran-OLD-stderr.txt @@ -0,0 +1,10 @@ +^CMake Deprecation Warning at Fortran-OLD\.cmake:[0-9]+ \(cmake_policy\): + The OLD behavior for policy CMP0129 will be removed from a future version + of CMake\. + + The cmake-policies\(7\) manual explains that the OLD behaviors of all + policies are deprecated and that a policy should be set to OLD only under + specific short-term circumstances\. Projects should be ported to the NEW + behavior and not rely on setting a policy to OLD\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/CMP0129/Fortran-OLD.cmake b/Tests/RunCMake/CMP0129/Fortran-OLD.cmake new file mode 100644 index 000000000..df6ca83e3 --- /dev/null +++ b/Tests/RunCMake/CMP0129/Fortran-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0129 OLD) +include(Fortran-common.cmake) diff --git a/Tests/RunCMake/CMP0129/Fortran-WARN.cmake b/Tests/RunCMake/CMP0129/Fortran-WARN.cmake new file mode 100644 index 000000000..3dc27c393 --- /dev/null +++ b/Tests/RunCMake/CMP0129/Fortran-WARN.cmake @@ -0,0 +1 @@ +include(Fortran-common.cmake) diff --git a/Tests/RunCMake/CMP0129/Fortran-common.cmake b/Tests/RunCMake/CMP0129/Fortran-common.cmake new file mode 100644 index 000000000..7b6ecc70e --- /dev/null +++ b/Tests/RunCMake/CMP0129/Fortran-common.cmake @@ -0,0 +1,4 @@ +enable_language(Fortran) +set(CMAKE_VERBOSE_MAKEFILE TRUE) +include(CompareCompilerVersion.cmake) +compare_compiler_version(Fortran) diff --git a/Tests/RunCMake/CMP0129/Fortran.cmake b/Tests/RunCMake/CMP0129/Fortran.cmake deleted file mode 100644 index abaca7e19..000000000 --- a/Tests/RunCMake/CMP0129/Fortran.cmake +++ /dev/null @@ -1,15 +0,0 @@ -include(CheckLanguage) -check_language(Fortran) -if(NOT CMAKE_Fortran_COMPILER) - # No Fortran compiler, skipping Fortran test - return() -endif() - -if(SET_CMP0129) - cmake_policy(SET CMP0129 ${SET_CMP0129}) -endif() - -enable_language(Fortran) -set(CMAKE_VERBOSE_MAKEFILE TRUE) -include(CompareCompilerVersion.cmake) -compare_compiler_version(Fortran) diff --git a/Tests/RunCMake/CMP0129/RunCMakeTest.cmake b/Tests/RunCMake/CMP0129/RunCMakeTest.cmake index 1b0e11bca..c1473525c 100644 --- a/Tests/RunCMake/CMP0129/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0129/RunCMakeTest.cmake @@ -1,8 +1,14 @@ set(RunCMake_TEST_NO_CMP0129 ON) include(RunCMake) -foreach(lang C CXX Fortran) - run_cmake(${lang}) - run_cmake_with_options(${lang} "-DSET_CMP0129=NEW") - run_cmake_with_options(${lang} "-DSET_CMP0129=OLD") -endforeach() +run_cmake(C-WARN) +run_cmake(C-OLD) +run_cmake(C-NEW) +run_cmake(CXX-WARN) +run_cmake(CXX-OLD) +run_cmake(CXX-NEW) +if(CMake_TEST_Fortran) + run_cmake(Fortran-WARN) + run_cmake(Fortran-OLD) + run_cmake(Fortran-NEW) +endif() diff --git a/Tests/RunCMake/CMP0156/CMP0156-Common-Imported.cmake b/Tests/RunCMake/CMP0156/CMP0156-Common-Imported.cmake new file mode 100644 index 000000000..0c0e34f63 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-Common-Imported.cmake @@ -0,0 +1,21 @@ + +enable_language(C) + +add_library(lib1 STATIC lib1.c) +set_property(TARGET lib1 PROPERTY ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") + +# This function simulates a find_package call for the third-party lib +# by making an imported target with non-global scope. +function(find_package_lib1) + add_library(lib1::lib1 STATIC IMPORTED) + + set_target_properties(lib1::lib1 PROPERTIES + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/Release/${CMAKE_STATIC_LIBRARY_PREFIX}lib1${CMAKE_STATIC_LIBRARY_SUFFIX}" + ) + + add_dependencies(lib1::lib1 lib1) +endfunction() + +# ------------------------------------------------------------------------------ +add_subdirectory(subdir1) +add_subdirectory(subdir2) diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-result.txt b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-result.txt new file mode 100644 index 000000000..d197c913c --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-result.txt @@ -0,0 +1 @@ +[^0] diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-stdout.txt b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-stdout.txt new file mode 100644 index 000000000..c60296760 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported-build-stdout.txt @@ -0,0 +1,3 @@ +ld: warning: ignoring duplicate libraries: '[^']*liblib1\.a' +ld: fatal warning\(s\) induced error \(-fatal_warnings\) +(cc|clang): error: linker command failed with exit code 1 \(use -v to see invocation\) diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported.cmake new file mode 100644 index 000000000..bdfc7b80d --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang-Imported.cmake @@ -0,0 +1,5 @@ + +cmake_policy(SET CMP0156 NEW) +set(APPLE_TEST TRUE) + +include (CMP0156-Common-Imported.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-Imported.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW-Imported.cmake new file mode 100644 index 000000000..92101d0f4 --- /dev/null +++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-Imported.cmake @@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0156 NEW) + +include (CMP0156-Common-Imported.cmake) diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt index b18168c37..d197c913c 100644 --- a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt @@ -1 +1 @@ -.+ +[^0] diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt index ea3b5c113..28dfd44a5 100644 --- a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt +++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt @@ -1,3 +1,3 @@ -ld: warning: ignoring duplicate libraries: '.*liblib1.a', '.*liblib2.a' +ld: warning: ignoring duplicate libraries: '[^']*liblib1\.a', '[^']*liblib2\.a' ld: fatal warning\(s\) induced error \(-fatal_warnings\) (cc|clang): error: linker command failed with exit code 1 \(use -v to see invocation\) diff --git a/Tests/RunCMake/CMP0156/RunCMakeTest.cmake b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake index bd5183015..a58fc8831 100644 --- a/Tests/RunCMake/CMP0156/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake @@ -3,10 +3,14 @@ include(RunCMake) # CMP0156 control how libraries are specified for the link step # a sensible configuration is how circular dependency is handled +if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(OPTIONS -DCMAKE_BUILD_TYPE=Release) +endif() + macro(run_cmake_and_build test) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_OUTPUT_MERGE TRUE) - run_cmake(${test}) + run_cmake_with_options(${test} ${OPTIONS}) set(RunCMake_TEST_NO_CLEAN TRUE) run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release) unset(RunCMake_TEST_NO_CLEAN) @@ -24,3 +28,12 @@ if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" run_cmake_and_build(CMP0156-OLD-AppleClang) run_cmake_and_build(CMP0156-NEW-AppleClang) endif() + + +run_cmake_and_build(CMP0156-NEW-Imported) + +if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" + AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "15.0") + # special case for Apple: FIXME(#26284): linker will warning on duplicate libraries + run_cmake_and_build(CMP0156-NEW-AppleClang-Imported) +endif() diff --git a/Tests/RunCMake/CMP0156/subdir1/CMakeLists.txt b/Tests/RunCMake/CMP0156/subdir1/CMakeLists.txt new file mode 100644 index 000000000..0e88cd50f --- /dev/null +++ b/Tests/RunCMake/CMP0156/subdir1/CMakeLists.txt @@ -0,0 +1,5 @@ + +find_package_lib1() + +add_library(lib2 STATIC ../lib2.c) +target_link_libraries(lib2 PUBLIC lib1::lib1) diff --git a/Tests/RunCMake/CMP0156/subdir2/CMakeLists.txt b/Tests/RunCMake/CMP0156/subdir2/CMakeLists.txt new file mode 100644 index 000000000..724483cce --- /dev/null +++ b/Tests/RunCMake/CMP0156/subdir2/CMakeLists.txt @@ -0,0 +1,14 @@ + +find_package_lib1() + +# ----------------------------------- +# This target depends on the third-party lib1 both +# directly and transitively. +add_executable(main ../main.c) +target_link_libraries(main PRIVATE lib1::lib1 lib2) + +if (APPLE_TEST) + target_link_options(main PRIVATE "LINKER:-fatal_warnings") +else() + target_link_options(main PRIVATE "$<$>,$,$,15.0>>:LINKER:-no_warn_duplicate_libraries>") +endif() diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-result.txt b/Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt similarity index 100% rename from Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-result.txt rename to Tests/RunCMake/CMP0171/CMP0171-NEW-result.txt diff --git a/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt new file mode 100644 index 000000000..155ddd28a --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-NEW-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at CMP0171-NEW\.cmake:[0-9]+ \(add_custom_target\): + The target name "codegen" is reserved\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake b/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake new file mode 100644 index 000000000..547f6c0af --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-NEW.cmake @@ -0,0 +1,6 @@ +# codegen is now a reserved name and this will cause an error since the policy is new. +add_custom_target(codegen + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt b/Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt similarity index 100% rename from Tests/RunCMake/file/MAKE_DIRECTORY-fail-result.txt rename to Tests/RunCMake/CMP0171/CMP0171-OLD-result.txt diff --git a/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt new file mode 100644 index 000000000..1ae331898 --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-OLD-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at CMP0171-OLD\.cmake:[0-9]+ \(add_custom_command\): + add_custom_command CODEGEN option requires policy CMP0171 be set to NEW\! +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake b/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake new file mode 100644 index 000000000..c34b3fbee --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-OLD.cmake @@ -0,0 +1,9 @@ +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + # This will cause an error since the CODEGEN option + # requires that CMP0171 is set to NEW + CODEGEN +) diff --git a/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt new file mode 100644 index 000000000..ee79553cc --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-WARN-stderr.txt @@ -0,0 +1,9 @@ +CMake Warning \(dev\) at CMP0171-WARN\.cmake:[0-9]+ \(add_custom_target\): + Policy CMP0171 is not set: 'codegen' is a reserved target name\. Run "cmake + --help-policy CMP0171" for policy details. Use the cmake_policy command to + set the policy and suppress this warning\. + + The target name "codegen" is reserved\. +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/CMP0171/CMP0171-WARN.cmake b/Tests/RunCMake/CMP0171/CMP0171-WARN.cmake new file mode 100644 index 000000000..6d61723cd --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-WARN.cmake @@ -0,0 +1,6 @@ +# CMake should warn the user if they have a target named codegen. +add_custom_target(codegen + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) diff --git a/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt b/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt new file mode 100644 index 000000000..d197c913c --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMP0171-codegen-build-result.txt @@ -0,0 +1 @@ +[^0] diff --git a/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-OFF.cmake b/Tests/RunCMake/CMP0171/CMP0171-codegen.cmake similarity index 100% rename from Tests/RunCMake/CommandLine/DeprecateVS12-WARN-OFF.cmake rename to Tests/RunCMake/CMP0171/CMP0171-codegen.cmake diff --git a/Tests/RunCMake/CMP0171/CMakeLists.txt b/Tests/RunCMake/CMP0171/CMakeLists.txt new file mode 100644 index 000000000..1a5755c17 --- /dev/null +++ b/Tests/RunCMake/CMP0171/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.29) +project(${RunCMake_TEST} LANGUAGES C) + +include(${RunCMake_TEST}.cmake) + +enable_testing() diff --git a/Tests/RunCMake/CMP0171/RunCMakeTest.cmake b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake new file mode 100644 index 000000000..75941d769 --- /dev/null +++ b/Tests/RunCMake/CMP0171/RunCMakeTest.cmake @@ -0,0 +1,18 @@ +include(RunCMake) + +run_cmake("CMP0171-WARN") + +run_cmake_with_options(CMP0171-OLD "-DCMAKE_POLICY_DEFAULT_CMP0171=OLD") + +run_cmake_with_options(CMP0171-NEW "-DCMAKE_POLICY_DEFAULT_CMP0171=NEW") + +# The entire point of this test is to ensure the codegen target is not created +# unintentionally. It can only be created if CMP0171 is NEW. +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0171-codegen-build) + run_cmake(CMP0171-codegen) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + # This command will fail with either 1 or 2 depending. + run_cmake_command(CMP0171-codegen-build ${CMAKE_COMMAND} --build . --config Debug --target codegen) +endblock() diff --git a/Tests/RunCMake/CMP0173/CMP0173-NEW-result.txt b/Tests/RunCMake/CMP0173/CMP0173-NEW-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0173/CMP0173-NEW-stderr.txt b/Tests/RunCMake/CMP0173/CMP0173-NEW-stderr.txt new file mode 100644 index 000000000..384a027d4 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-NEW-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): + CMakeFindFrameworks\.cmake is not maintained and lacks support for more + recent framework handling\. It will be removed in a future version of + CMake\. Update the code to use find_library\(\) instead\. Use of this module + is now an error according to policy CMP0173\. +Call Stack \(most recent call first\): + CMP0173-common\.cmake:12 \(include\) + CMP0173-NEW\.cmake:4 \(include\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/CMP0173/CMP0173-NEW.cmake b/Tests/RunCMake/CMP0173/CMP0173-NEW.cmake new file mode 100644 index 000000000..f26093a70 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-NEW.cmake @@ -0,0 +1,4 @@ +cmake_policy(SET CMP0173 NEW) + +set(should_find NO) +include(CMP0173-common.cmake) diff --git a/Tests/RunCMake/CMP0173/CMP0173-OLD-stdout.txt b/Tests/RunCMake/CMP0173/CMP0173-OLD-stdout.txt new file mode 100644 index 000000000..f32056a2e --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-OLD-stdout.txt @@ -0,0 +1,2 @@ +-- Configuring done \([0-9]+\.[0-9]s\) +-- Generating done \([0-9]+\.[0-9]s\) diff --git a/Tests/RunCMake/CMP0173/CMP0173-OLD.cmake b/Tests/RunCMake/CMP0173/CMP0173-OLD.cmake new file mode 100644 index 000000000..731672826 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-OLD.cmake @@ -0,0 +1,4 @@ +cmake_policy(SET CMP0173 OLD) + +set(should_find YES) +include(CMP0173-common.cmake) diff --git a/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt b/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt new file mode 100644 index 000000000..3a4c13068 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-WARN-stderr.txt @@ -0,0 +1,29 @@ +CMake Warning \(dev\) at CMP0173-common\.cmake:1 \(include\): + Policy CMP0173 is not set: The CMakeFindFrameworks module is removed\. Run + "cmake --help-policy CMP0173" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. + +Call Stack \(most recent call first\): + CMP0173-WARN\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): + CMakeFindFrameworks\.cmake is not maintained and lacks support for more + recent framework handling\. It will be removed in a future version of + CMake\. Update the code to use find_library\(\) instead\. +Call Stack \(most recent call first\): + CMP0173-common\.cmake:1 \(include\) + CMP0173-WARN\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at .*/Modules/CMakeFindFrameworks\.cmake:[0-9]+ \(message\): + CMakeFindFrameworks\.cmake is not maintained and lacks support for more + recent framework handling\. It will be removed in a future version of + CMake\. Update the code to use find_library\(\) instead\. +Call Stack \(most recent call first\): + CMP0173-common\.cmake:12 \(include\) + CMP0173-WARN\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/CMP0173/CMP0173-WARN.cmake b/Tests/RunCMake/CMP0173/CMP0173-WARN.cmake new file mode 100644 index 000000000..dcc59b7c0 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-WARN.cmake @@ -0,0 +1,2 @@ +set(should_find YES) +include(CMP0173-common.cmake) diff --git a/Tests/RunCMake/CMP0173/CMP0173-common.cmake b/Tests/RunCMake/CMP0173/CMP0173-common.cmake new file mode 100644 index 000000000..4a46bbe62 --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMP0173-common.cmake @@ -0,0 +1,12 @@ +include(CMakeFindFrameworks OPTIONAL RESULT_VARIABLE found) +if(NOT should_find AND found) + message(FATAL_ERROR + "The CMakeFindFrameworks module should not have been found, but it was." + ) +endif() +if(should_find AND NOT found) + message(FATAL_ERROR + "The CMakeFindFrameworks module should have been found, but it was not." + ) +endif() +include(${CMAKE_ROOT}/Modules/CMakeFindFrameworks.cmake) diff --git a/Tests/RunCMake/CMP0173/CMakeLists.txt b/Tests/RunCMake/CMP0173/CMakeLists.txt new file mode 100644 index 000000000..dda37d8bc --- /dev/null +++ b/Tests/RunCMake/CMP0173/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0173/RunCMakeTest.cmake b/Tests/RunCMake/CMP0173/RunCMakeTest.cmake new file mode 100644 index 000000000..42d5e3022 --- /dev/null +++ b/Tests/RunCMake/CMP0173/RunCMakeTest.cmake @@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0173-OLD) +run_cmake(CMP0173-NEW) +run_cmake(CMP0173-WARN) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index e205d9ff1..0a96aef22 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -35,10 +35,6 @@ macro(add_RunCMake_test test) ${TEST_ARGS} -P "${CMAKE_CURRENT_SOURCE_DIR}/${Test_Dir}/RunCMakeTest.cmake" ) - set_tests_properties("RunCMake.${test}" PROPERTIES LABELS "CMake;run") - if(${test} MATCHES ^CMP) - set_property(TEST "RunCMake.${test}" APPEND PROPERTY LABELS "policy") - endif() endmacro() function(add_RunCMake_test_group test types) @@ -153,6 +149,9 @@ add_RunCMake_test(CMP0126) if("${CMAKE_C_COMPILER_ID}" STREQUAL "LCC" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "LCC" OR "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "LCC") + if(CMAKE_Fortran_COMPILER) + list(APPEND CMP0129_ARGS -DCMake_TEST_Fortran=1) + endif() add_RunCMake_test("CMP0129") set_property(TEST RunCMake.CMP0129 APPEND PROPERTY LABELS "Fortran") endif() @@ -177,6 +176,8 @@ add_RunCMake_test(CMP0163) add_RunCMake_test(CMP0165) add_RunCMake_test(CMP0169) add_RunCMake_test(CMP0170) +add_RunCMake_test(CMP0171) +add_RunCMake_test(CMP0173) # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode @@ -213,7 +214,10 @@ if(CMAKE_GENERATOR MATCHES "Ninja") -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX} -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}) if(CMAKE_Fortran_COMPILER) - list(APPEND Ninja_ARGS -DTEST_Fortran=1) + list(APPEND Ninja_ARGS + -DCMake_TEST_Fortran=1 + -DCMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID} + ) endif() if(ninja_test_with_qt_version) list(APPEND Ninja_ARGS @@ -254,8 +258,8 @@ if(CMAKE_GENERATOR MATCHES "Ninja") add_RunCMake_test(NinjaPrivateDeps -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig}) - add_RunCMake_test(InstallParallel) endif() +add_RunCMake_test(InstallParallel) add_RunCMake_test(CTest) if(NOT CMake_TEST_EXTERNAL_CMAKE) @@ -280,33 +284,40 @@ if(CMake_TEST_APPLE_SILICON) add_RunCMake_test(AppleSilicon) endif() set(want_NoQt_test TRUE) +set(autogen_test_number 1 2 3 4 5 6) if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND) # Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH cmake_path(GET Qt6_DIR PARENT_PATH base_dir) # /lib/cmake cmake_path(GET base_dir PARENT_PATH base_dir) # /lib cmake_path(GET base_dir PARENT_PATH base_dir) # - add_RunCMake_test(AutogenQt6 TEST_DIR Autogen - -Dwith_qt_version=6 - -DQtCore_VERSION=${Qt6Core_VERSION} - "-DQt6_DIR:PATH=${Qt6_DIR}" - "-DCMAKE_PREFIX_PATH:STRING=${base_dir}" - -DPSEUDO_TIDY=$ - -DPSEUDO_IWYU=$ - -DPSEUDO_CPPLINT=$ - -DPSEUDO_CPPCHECK=$ - ) + # Note: Since RunCMake.Autogen tests cause time out on some CI, + # we split the tests. + foreach(val IN ITEMS ${autogen_test_number}) + add_RunCMake_test("Autogen_Qt6_${val}" TEST_DIR "Autogen_${val}" + -Dwith_qt_version=6 + -DQtCore_VERSION=${Qt6Core_VERSION} + "-DQt6_DIR:PATH=${Qt6_DIR}" + "-DCMAKE_PREFIX_PATH:STRING=${base_dir}" + -DPSEUDO_TIDY=$ + -DPSEUDO_IWYU=$ + -DPSEUDO_CPPLINT=$ + -DPSEUDO_CPPCHECK=$ + ) + endforeach() set(want_NoQt_test FALSE) endif () if(CMake_TEST_Qt5 AND Qt5Widgets_FOUND) - add_RunCMake_test(AutogenQt5 TEST_DIR Autogen - -Dwith_qt_version=5 - -DQtCore_VERSION=${Qt5Core_VERSION} - "-DQt5_DIR:PATH=${Qt5_DIR}" - ) + foreach(val IN ITEMS ${autogen_test_number}) + add_RunCMake_test("Autogen_Qt5_${val}" TEST_DIR "Autogen_${val}" + -Dwith_qt_version=5 + -DQtCore_VERSION=${Qt5Core_VERSION} + "-DQt5_DIR:PATH=${Qt5_DIR}" + ) + endforeach() set(want_NoQt_test FALSE) endif () if(want_NoQt_test) - add_RunCMake_test(AutogenNoQt TEST_DIR Autogen) + add_RunCMake_test(AutogenNoQt TEST_DIR Autogen_1) endif() if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS @@ -322,10 +333,14 @@ if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS endif() if(CMAKE_Fortran_COMPILER) - list(APPEND BuildDepends_ARGS -DCMake_TEST_Fortran=1) + list(APPEND BuildDepends_ARGS + -DCMake_TEST_Fortran=1 + -DCMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID} + ) endif() add_RunCMake_test(BuildDepends + -DMAKE_SUPPORTS_SPACES=${MAKE_SUPPORTS_SPACES} -DMSVC_VERSION=${MSVC_VERSION} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_LINK_DEPENDS_USE_COMPILER=${CMAKE_C_LINK_DEPENDS_USE_COMPILER} @@ -354,7 +369,7 @@ if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja") endif() add_RunCMake_test(CompilerNotFound) if(DEFINED CMake_TEST_OBJC) - list(APPEND CompilerTest_ARGS -DCMake_TEST_OBJC=${CMake_TEST_OBJC}) + list(APPEND CMake_TEST_LANG_VARS -DCMake_TEST_OBJC=${CMake_TEST_OBJC}) endif() if(CMAKE_Fortran_COMPILER) # lfortran < 1.24 cannot handle long file names. Fortran is not @@ -362,19 +377,21 @@ if(CMAKE_Fortran_COMPILER) if(CMAKE_C_COMPILER_ID STREQUAL "LCC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "1.24") string(LENGTH "${CMAKE_CURRENT_BINARY_DIR}" _CCBD_LEN) if(_CCBD_LEN LESS 35) - list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1) + list(APPEND CMake_TEST_LANG_VARS -DCMake_TEST_Fortran=1) endif() unset(_CCBD_LEN) else() - list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1) + list(APPEND CMake_TEST_LANG_VARS -DCMake_TEST_Fortran=1) endif() endif() foreach(lang IN ITEMS CUDA HIP ISPC) if(CMake_TEST_${lang}) - list(APPEND CompilerTest_ARGS -DCMake_TEST_${lang}=1) + list(APPEND CMake_TEST_LANG_VARS -DCMake_TEST_${lang}=1) endif() endforeach() -add_RunCMake_test(CompilerTest) +add_RunCMake_test(CompilerId ${CMake_TEST_LANG_VARS}) +set_property(TEST RunCMake.CompilerId APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran") +add_RunCMake_test(CompilerTest ${CMake_TEST_LANG_VARS}) set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran") add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE}) add_RunCMake_test(DisallowedCommands) @@ -395,6 +412,7 @@ if(CMAKE_USE_SYSTEM_JSONCPP) endif() add_RunCMake_test(FileAPI -DPython_EXECUTABLE=${Python_EXECUTABLE} -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}) +add_RunCMake_test(ConfigDir) add_RunCMake_test(FindBoost) add_RunCMake_test(FindLua) add_RunCMake_test(FindOpenGL) @@ -477,6 +495,7 @@ if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG) ) endif() add_RunCMake_test(ScriptMode) +add_RunCMake_test(StandardLinkDirectories) add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMake_TEST_Swift=${CMake_TEST_Swift} -DXCODE_VERSION=${XCODE_VERSION}) @@ -547,6 +566,7 @@ add_RunCMake_test(cmake_language) add_RunCMake_test(cmake_minimum_required) add_RunCMake_test(cmake_parse_arguments) add_RunCMake_test(cmake_path -DMSYS=${MSYS}) +add_RunCMake_test(cmake_pkg_config) add_RunCMake_test(continue) add_executable(color_warning color_warning.c) add_executable(fake_build_command fake_build_command.c) @@ -579,12 +599,14 @@ foreach(var CMake_TEST_TLS_VERIFY_URL CMake_TEST_TLS_VERIFY_URL_BAD CMake_TEST_TLS_VERSION + CMake_TEST_TLS_VERSION_URL_BAD ) if(DEFINED ${var}) list(APPEND file-DOWNLOAD_ARGS -D${var}=${${var}}) endif() endforeach() add_RunCMake_test(file-DOWNLOAD) +add_RunCMake_test(file-MAKE_DIRECTORY) add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMake_TEST_ELF_LARGE=${CMake_TEST_ELF_LARGE} @@ -672,7 +694,13 @@ if(CMake_TEST_CUDA) set_property(TEST RunCMake.CUDA_architectures APPEND PROPERTY LABELS "CUDA") endif() -add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER}) +if(CMAKE_Fortran_COMPILER) + list(APPEND DependencyGraph_ARGS + -DCMake_TEST_Fortran=1 + -DCMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID} + ) + endif() +add_RunCMake_test(DependencyGraph) set_property(TEST RunCMake.DependencyGraph APPEND PROPERTY LABELS "Fortran") # Add C++ Module tests. @@ -755,9 +783,6 @@ if("${CMAKE_GENERATOR}" MATCHES "Visual Studio") -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION} ) add_RunCMake_test(VS10ProjectUseDebugLibraries) - if( vs12 AND wince ) - add_RunCMake_test( VS10ProjectWinCE "-DRunCMake_GENERATOR_PLATFORM=${wince_sdk}") - endif() endif() if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])" @@ -814,6 +839,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)" endif() add_RunCMake_test(LinkLibrariesProcessing) +add_RunCMake_test(LinkLibrariesStrategy) add_RunCMake_test(File_Archive) add_RunCMake_test(File_Configure) add_RunCMake_test(File_Generate) @@ -1056,6 +1082,7 @@ set(cpack_tests DEB.DEB_DESCRIPTION DEB.PROJECT_META DEB.COMPONENT_WITH_SPECIAL_CHARS + DEB.MULTIARCH RPM.AUTO_SUFFIXES RPM.CUSTOM_BINARY_SPEC_FILE @@ -1153,6 +1180,7 @@ add_RunCMake_test(AutoExportDll ) add_RunCMake_test(AndroidMK) +add_RunCMake_test(PackageInfo) if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN) if(NOT "${CMAKE_GENERATOR}" MATCHES "Make|Ninja|Visual Studio 1[456]") @@ -1188,7 +1216,10 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_SIMULATE_ID=${CMAKE_C_SIMULATE_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) -add_RunCMake_test(UnityBuild -DCMake_TEST_OBJC=${CMake_TEST_OBJC}) +add_RunCMake_test(UnityBuild -DCMake_TEST_OBJC=${CMake_TEST_OBJC} -DCMake_TEST_CUDA=${CMake_TEST_CUDA}) +set_property(TEST RunCMake.UnityBuild APPEND + PROPERTY LABELS "CUDA") + add_RunCMake_test(CMakePresets -DPython_EXECUTABLE=${Python_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA} @@ -1216,7 +1247,8 @@ add_RunCMake_test(CMakePresetsWorkflow add_RunCMake_test(VerifyHeaderSets) add_RunCMake_test(set_tests_properties) -if(${CMAKE_GENERATOR} MATCHES "Make|Ninja") +if(CMAKE_GENERATOR MATCHES "Make|Ninja") + add_RunCMake_test(Codegen) add_RunCMake_test(TransformDepfile) endif() diff --git a/Tests/RunCMake/CMakePresets/CommentValid-result.txt b/Tests/RunCMake/CMakePresets/CommentValid-result.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValid-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON.cmake b/Tests/RunCMake/CMakePresets/CommentValid.cmake similarity index 100% rename from Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON.cmake rename to Tests/RunCMake/CMakePresets/CommentValid.cmake diff --git a/Tests/RunCMake/CMakePresets/CommentValid.json.in b/Tests/RunCMake/CMakePresets/CommentValid.json.in new file mode 100644 index 000000000..1206ded51 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValid.json.in @@ -0,0 +1,14 @@ +{ + "$comment": [ + "example comment", + "with an array of strings" + ], + "version": 10, + "configurePresets": [ + { + "name": "CommentValid", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/CommentValidFull-result.txt b/Tests/RunCMake/CMakePresets/CommentValidFull-result.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValidFull-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-result.txt b/Tests/RunCMake/CMakePresets/CommentValidFull.cmake similarity index 100% rename from Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-result.txt rename to Tests/RunCMake/CMakePresets/CommentValidFull.cmake diff --git a/Tests/RunCMake/CMakePresets/CommentValidFull.json.in b/Tests/RunCMake/CMakePresets/CommentValidFull.json.in new file mode 100644 index 000000000..40bab97c9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValidFull.json.in @@ -0,0 +1,179 @@ +{ + "$comment": [ + "example comment", + "with an array of strings" + ], + "version": 10, + "cmakeMinimumRequired": { + "$comment": "We can add comments in version" + }, + "configurePresets": [ + { + "name": "CommentValidFull", + "$comment": "example comment", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "architecture": { + "$comment": "We can add comments in architecture", + "value": "v143", + "strategy": "external" + }, + "toolset": { + "$comment": "We can add comments in toolset", + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "SOME_VARIABLE": { + "$comment": "We can add comments in every cacheVariable, that we created", + "value": "SOME_VALUE" + } + }, + "warnings": { + "$comment": "We can add comments in warnings", + "dev": false + }, + "errors": { + "$comment": "We can add comments in errors", + "dev": false + }, + "debug": { + "$comment": "We can add comments in debug", + "find": false + }, + "trace": { + "$comment": "We can add comments in trace", + "mode": "off" + } + } + ], + "buildPresets": [ + { + "$comment": "example comment", + "name": "default", + "condition": { + "$comment": "We can add comments in condition", + "type": "const", + "value": false + }, + "configurePreset": "CommentValidFull" + } + ], + "testPresets": [ + { + "name": "default", + "configurePreset": "CommentValidFull", + "filter": { + "$comment": "We can add comments in filter", + "include": { + "$comment": ["example comment", "with an array of strings", "inside include"], + "index": { + "$comment": ["example comment", "with an array of strings"], + "start": 0 + } + }, + "exclude": { + "$comment": ["example comment", "with an array of strings", "inside exclude"], + "fixtures": { + "$comment": ["example comment", "with an array of strings"], + "any": ".*full.*" + } + } + }, + "execution": { + "$comment": ["example comment", "with an array of strings"], + "repeat": { + "$comment": "Some comments here", + "mode": "until-fail", + "count": 1 + } + }, + "condition": { + "$comment": "We can add comments in equal & non-equal conditions", + "type": "equals", + "lhs": "test1", + "rhs": "test2" + }, + "$comment": ["example comment", "with an array of strings"] + } + ], + "packagePresets": [ + { + "name": "CommentValidFull", + "$comment": ["example comment", "with an array of strings"], + "configurePreset": "CommentValidFull", + "condition": { + "$comment": "We can add comments in list check conditions", + "type": "notInList", + "list": [ + "test1", + "test2" + ], + "string": "test1" + }, + "output": { + "$comment": "Hello, World!", + "debug": false + } + }, + { + "name": "CommentValidFull1", + "configurePreset": "CommentValidFull", + "condition": { + "$comment": "We can add comments in aggregation conditions", + "type": "allOf", + "conditions": [ + { + "$comment": "We can add comments in regex match conditions", + "type": "matches", + "regex": ".*tests.*", + "string": "test1" + }, + { + "type": "matches", + "regex": ".*tests.*", + "string": "test2" + }, + { + "type": "not", + "$comment": "We can add comments in not conditions", + "condition": { + "type": "matches", + "regex": ".*tests.*", + "string": "test3" + } + } + ] + }, + "output": { + "$comment": "Hello, World 123!", + "verbose": false + } + } + ], + "workflowPresets": [ + { + "name": "CommentValidFull", + "$comment": ["example comment", "with an array of strings"], + "steps": [ + { + "type": "configure", + "name": "CommentValidFull" + }, + { + "type": "build", + "name": "default" + }, + { + "$comment": "We must test it before we can package it", + "type": "test", + "name": "default" + }, + { + "type": "package", + "name": "CommentValidFull" + } + ] + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/CommentValidOldSchema-result.txt b/Tests/RunCMake/CMakePresets/CommentValidOldSchema-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValidOldSchema-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresets/CommentValidOldSchema-stderr.txt b/Tests/RunCMake/CMakePresets/CommentValidOldSchema-stderr.txt new file mode 100644 index 000000000..b102f5bc2 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValidOldSchema-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error: Could not read presets from .* +Error: @2,15: Invalid extra field \"\$comment\" in root object diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-result.txt b/Tests/RunCMake/CMakePresets/CommentValidOldSchema.cmake similarity index 100% rename from Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-result.txt rename to Tests/RunCMake/CMakePresets/CommentValidOldSchema.cmake diff --git a/Tests/RunCMake/CMakePresets/CommentValidOldSchema.json.in b/Tests/RunCMake/CMakePresets/CommentValidOldSchema.json.in new file mode 100644 index 000000000..d92ee1df4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/CommentValidOldSchema.json.in @@ -0,0 +1,14 @@ +{ + "$comment": [ + "invalid comment", + "version less than 10" + ], + "version": 9, + "configurePresets": [ + { + "name": "CommentValidOldSchema", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt b/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValid-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt b/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt new file mode 100644 index 000000000..43d0958dd --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValid-stdout.txt @@ -0,0 +1,2 @@ + +Generate graphviz: .+[\\/]my_graphviz\.dot diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid.cmake b/Tests/RunCMake/CMakePresets/GraphvizValid.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CMakePresets/GraphvizValid.json.in b/Tests/RunCMake/CMakePresets/GraphvizValid.json.in new file mode 100644 index 000000000..0a4ab6aff --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValid.json.in @@ -0,0 +1,11 @@ +{ + "version": 10, + "configurePresets": [ + { + "name": "GraphvizValid", + "generator": "@RunCMake_GENERATOR@", + "graphviz": "${sourceDir}/my_graphviz.dot", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt new file mode 100644 index 000000000..0b7b4d60f --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error: Could not read presets from .* +File version must be 10 or higher for graphviz preset support diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in new file mode 100644 index 000000000..85d0b1b46 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/GraphvizValidOldSchema.json.in @@ -0,0 +1,11 @@ +{ + "version": 9, + "configurePresets": [ + { + "name": "GraphvizValid", + "generator": "@RunCMake_GENERATOR@", + "graphviz": "${sourceDir}/my_graphviz.dot", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt index 598478fc6..f8454b9e2 100644 --- a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt +++ b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/HighVersion: -Error: @2,14: Unrecognized "version" field +Error: @2,14: Unrecognized "version" 1000: must be >=1 and <=10 "version": 1000, \^$ diff --git a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt index e9331006d..f4f65f9e1 100644 --- a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt +++ b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresets/LowVersion: -Error: @2,14: Unrecognized "version" field +Error: @2,14: Unrecognized "version" 0: must be >=1 and <=10 "version": 0, \^ diff --git a/Tests/RunCMake/CMakePresets/NoCommentValid-result.txt b/Tests/RunCMake/CMakePresets/NoCommentValid-result.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoCommentValid-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMakePresets/NoCommentValid.cmake b/Tests/RunCMake/CMakePresets/NoCommentValid.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CMakePresets/NoCommentValid.json.in b/Tests/RunCMake/CMakePresets/NoCommentValid.json.in new file mode 100644 index 000000000..cf10ee2c1 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoCommentValid.json.in @@ -0,0 +1,10 @@ +{ + "version": 10, + "configurePresets": [ + { + "name": "NoCommentValid", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt b/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoGraphvizValid-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake b/Tests/RunCMake/CMakePresets/NoGraphvizValid.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in b/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in new file mode 100644 index 000000000..b689833f4 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoGraphvizValid.json.in @@ -0,0 +1,10 @@ +{ + "version": 10, + "configurePresets": [ + { + "name": "NoGraphvizValid", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 2ec0a43ab..a92a4c4e5 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake @@ -92,6 +92,17 @@ endfunction() set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(NoCMakePresets) run_cmake_presets(Comment) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +run_cmake_presets(NoCommentValid) +run_cmake_presets(CommentValid) +run_cmake_presets(CommentValidFull) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_presets(CommentValidOldSchema) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +run_cmake_presets(NoGraphvizValid) +run_cmake_presets(GraphvizValid) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_presets(GraphvizValidOldSchema) run_cmake_presets(JSONParseError) run_cmake_presets(InvalidRoot) run_cmake_presets(NoVersion) diff --git a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt index 049ff54c8..8e23c9b6e 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt @@ -1,5 +1,5 @@ ^Unknown argument -DINVALID_OPTION -Usage: cmake --workflow \[options\] +Usage: cmake --workflow Options: --preset = Workflow preset to execute\. --list-presets = List available workflow presets\. diff --git a/Tests/RunCMake/CMakePresetsWorkflow/MultiplePresets.json.in b/Tests/RunCMake/CMakePresetsWorkflow/MultiplePresets.json.in new file mode 100644 index 000000000..9e7dcb8c6 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/MultiplePresets.json.in @@ -0,0 +1,41 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "SinglePresetArg", + "generator": "@RunCMake_GENERATOR@", + "cacheVariables": + { + "WORKFLOW_PRESET": "SinglePresetArg" + } + }, + { + "name": "RepeatedPresetArg", + "generator": "@RunCMake_GENERATOR@", + "cacheVariables": + { + "WORKFLOW_PRESET": "RepeatedPresetArg" + } + } + ], + "workflowPresets": [ + { + "name": "SinglePresetArg", + "steps": [ + { + "type": "configure", + "name": "SinglePresetArg" + } + ] + }, + { + "name": "RepeatedPresetArg", + "steps": [ + { + "type": "configure", + "name": "RepeatedPresetArg" + } + ] + } + ] +} diff --git a/Tests/RunCMake/CMakePresetsWorkflow/RepeatedPresetArg.cmake b/Tests/RunCMake/CMakePresetsWorkflow/RepeatedPresetArg.cmake new file mode 100644 index 000000000..b508d1c73 --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/RepeatedPresetArg.cmake @@ -0,0 +1,3 @@ +if(WORKFLOW_PRESET STREQUAL "RepeatedPresetArg") + message(FATAL_ERROR "First preset argument used instead of repeated preset argument") +endif() diff --git a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake index 550600a18..8c1002d00 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake @@ -45,7 +45,9 @@ function(run_cmake_workflow_presets name) set(RunCMake-check-file "check.cmake") endif() - if(eq) + if(CMakePresets_DIRECT_ARG) + set(preset_arg "${name}") + elseif(eq) set(eq 0 PARENT_SCOPE) set(preset_arg "--preset=${name}") else() @@ -87,3 +89,10 @@ 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) + +set(CMakePresets_DIRECT_ARG TRUE) +set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/MultiplePresets.json.in") +run_cmake_workflow_presets(SinglePresetArg) +run_cmake_workflow_presets(RepeatedPresetArg --preset SinglePresetArg) +unset(CMakePresets_FILE) +unset(CMakePresets_DIRECT_ARG) diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SinglePresetArg.cmake b/Tests/RunCMake/CMakePresetsWorkflow/SinglePresetArg.cmake new file mode 100644 index 000000000..13b754a2b --- /dev/null +++ b/Tests/RunCMake/CMakePresetsWorkflow/SinglePresetArg.cmake @@ -0,0 +1,3 @@ +if(NOT WORKFLOW_PRESET STREQUAL "SinglePresetArg") + message(FATAL_ERROR "Expected 'SinglePresetArg' workflow preset, but actual value is ${WORKFLOW_PRESET}") +endif() diff --git a/Tests/RunCMake/CPack/CMakeLists.txt b/Tests/RunCMake/CPack/CMakeLists.txt index 18a673e7d..315f5f3b4 100644 --- a/Tests/RunCMake/CPack/CMakeLists.txt +++ b/Tests/RunCMake/CPack/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "") diff --git a/Tests/RunCMake/CPack/DEB/Helpers.cmake b/Tests/RunCMake/CPack/DEB/Helpers.cmake index 8904c696f..39a316f42 100644 --- a/Tests/RunCMake/CPack/DEB/Helpers.cmake +++ b/Tests/RunCMake/CPack/DEB/Helpers.cmake @@ -101,7 +101,7 @@ function(getMissingShlibsErrorExtra FILE RESULT_VAR) string(APPEND error_extra "; readelf \"\n") # Only dynamically linked ELF files are included - # Extract only file name infront of ":" + # Extract only file name in front of ":" foreach(_FILE IN LISTS deb_install_files) if(_FILE MATCHES "ELF.*shared object") string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}") diff --git a/Tests/RunCMake/CPack/README.txt b/Tests/RunCMake/CPack/README.txt index 216512505..c63026e0d 100644 --- a/Tests/RunCMake/CPack/README.txt +++ b/Tests/RunCMake/CPack/README.txt @@ -10,8 +10,8 @@ CPack test root directory: 'Tests/RunCMake/CPack/tests'. All phases are executed separately for each generator that is bound to a test. Tests for each generator are subtests of test 'RunCMake.CPack_'. -Each test must also be added to 'RunCMakeTest.cmake' script located in CPack -test root directory. +Each test must be added to list "cpack_tests" in 'Tests/RunCMake/CMakeLists.txt' +and also to 'RunCMakeTest.cmake' script located in 'Tests/RunCMake/CPack/'. Line that adds a test is: run_cpack_test( "" @@ -53,7 +53,7 @@ Test consists of - verification of generated files The phases are executed once per specified generator, packaging type and subtest -combinatiion. +combination. test prerequirements phase (optional): -------------------------------------- @@ -136,7 +136,7 @@ this step and must contain - EXPECTED_FILES_COUNT variable that contains the number of expected files that will be generated (0 or more) -- EXPECTED_FILE_ that contains globing expression +- EXPECTED_FILE_ that contains globbing expression that uniquely defines expected file name (will be used to find expected file) and should be present once for each expected file. NOTE: This variable should be used only as last resort as it sets generator @@ -173,7 +173,7 @@ This phase is executed if '/VerifyResult.cmake' script exists. VerifyResult.cmake script also automatically prints out standard output and standard error from CPack execution phase that is compared with '/-stdout.txt' regular expression and -'/-stderr.txt' regular expresson respectively. +'/-stderr.txt' regular expression respectively. NOTE: For subtests generator name can also be suffixed with subtest name and/or packaging type (MONOLITHIC, COMPONENT, GROUP) and in such cases the preferences of which file will be used are as follows: diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 5d3240434..c026bf6fd 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10) include(RunCMake) include("${RunCMake_SOURCE_DIR}/CPackTestHelpers.cmake") @@ -73,3 +73,4 @@ run_cpack_test_package_target(PRE_POST_SCRIPTS "ZIP" false "MONOLITHIC;COMPONENT run_cpack_test_subtests(DUPLICATE_FILE "success;conflict_file;conflict_symlink" "TGZ" false "COMPONENT;GROUP") run_cpack_test(COMPONENT_WITH_SPECIAL_CHARS "RPM.COMPONENT_WITH_SPECIAL_CHARS;DEB.COMPONENT_WITH_SPECIAL_CHARS;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ" false "MONOLITHIC;COMPONENT;GROUP") run_cpack_test_package_target(COMPONENT_WITH_SPECIAL_CHARS "RPM.COMPONENT_WITH_SPECIAL_CHARS;DEB.COMPONENT_WITH_SPECIAL_CHARS;7Z;TBZ2;TGZ;TXZ;TZ;ZIP;STGZ" false "MONOLITHIC;COMPONENT;GROUP") +run_cpack_test_subtests(MULTIARCH "same;foreign;allowed;fail" "DEB.MULTIARCH" false "MONOLITHIC;COMPONENT") diff --git a/Tests/RunCMake/CPack/tests/MULTIARCH/DEB-fail-stderr.txt b/Tests/RunCMake/CPack/tests/MULTIARCH/DEB-fail-stderr.txt new file mode 100644 index 000000000..8164e8340 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/MULTIARCH/DEB-fail-stderr.txt @@ -0,0 +1 @@ +Error: invalid value for Multi-Arch: fail\. Valid values are: same, foreign, allowed diff --git a/Tests/RunCMake/CPack/tests/MULTIARCH/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/MULTIARCH/ExpectedFiles.cmake new file mode 100644 index 000000000..14b898f86 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/MULTIARCH/ExpectedFiles.cmake @@ -0,0 +1,5 @@ +set(EXPECTED_FILES_COUNT "0") +if(NOT ${RunCMake_SUBTEST_SUFFIX} STREQUAL "fail") + set(EXPECTED_FILES_COUNT "1") + set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt") +endif () diff --git a/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake new file mode 100644 index 000000000..f4e80502d --- /dev/null +++ b/Tests/RunCMake/CPack/tests/MULTIARCH/VerifyResult.cmake @@ -0,0 +1,4 @@ +if(NOT ${RunCMake_SUBTEST_SUFFIX} STREQUAL "fail") + set(MULTIARCH_control "Multi-Arch: ${RunCMake_SUBTEST_SUFFIX}") + verifyDebControl("${FOUND_FILE_1}" "MULTIARCH" "control") +endif () diff --git a/Tests/RunCMake/CPack/tests/MULTIARCH/test.cmake b/Tests/RunCMake/CPack/tests/MULTIARCH/test.cmake new file mode 100644 index 000000000..9784342d4 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/MULTIARCH/test.cmake @@ -0,0 +1,7 @@ +install(FILES CMakeLists.txt DESTINATION foo COMPONENT test) + +set(CPACK_DEBIAN_PACKAGE_MULTIARCH ${RunCMake_SUBTEST_SUFFIX}) + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(CPACK_COMPONENTS_ALL test) +endif() diff --git a/Tests/RunCMake/CPackConfig/CMP0172-NEW-check.cmake b/Tests/RunCMake/CPackConfig/CMP0172-NEW-check.cmake new file mode 100644 index 000000000..3e26e53ae --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-NEW-check.cmake @@ -0,0 +1,2 @@ +include(${RunCMake_SOURCE_DIR}/check.cmake) +test_variable(CPACK_WIX_INSTALL_SCOPE "perMachine") diff --git a/Tests/RunCMake/CPackConfig/CMP0172-NEW.cmake b/Tests/RunCMake/CPackConfig/CMP0172-NEW.cmake new file mode 100644 index 000000000..ddab22769 --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-NEW.cmake @@ -0,0 +1 @@ +cmake_policy(SET CMP0172 NEW) diff --git a/Tests/RunCMake/CPackConfig/CMP0172-OLD-check.cmake b/Tests/RunCMake/CPackConfig/CMP0172-OLD-check.cmake new file mode 100644 index 000000000..63889b4c4 --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-OLD-check.cmake @@ -0,0 +1,2 @@ +include(${RunCMake_SOURCE_DIR}/check.cmake) +test_variable(CPACK_WIX_INSTALL_SCOPE "") diff --git a/Tests/RunCMake/CPackConfig/CMP0172-OLD.cmake b/Tests/RunCMake/CPackConfig/CMP0172-OLD.cmake new file mode 100644 index 000000000..856c9e7d9 --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-OLD.cmake @@ -0,0 +1,2 @@ +set(CMAKE_POLICY_WARNING_CMP0172 1) +cmake_policy(SET CMP0172 OLD) diff --git a/Tests/RunCMake/CPackConfig/CMP0172-WARN-check.cmake b/Tests/RunCMake/CPackConfig/CMP0172-WARN-check.cmake new file mode 100644 index 000000000..63889b4c4 --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-WARN-check.cmake @@ -0,0 +1,2 @@ +include(${RunCMake_SOURCE_DIR}/check.cmake) +test_variable(CPACK_WIX_INSTALL_SCOPE "") diff --git a/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt b/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt new file mode 100644 index 000000000..4596686ab --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-WARN-stderr.txt @@ -0,0 +1,12 @@ +^CMake Warning \(dev\) at [^ +]*/Modules/CPack\.cmake:[0-9]+ \(message\): + Policy CMP0172 is not set: The CPack module enables per-machine + installation by default in the CPack WIX Generator\. Run "cmake + --help-policy CMP0172" for policy details\. Use the cmake_policy command to + set the policy and suppress this warning. + + For compatibility, CMake will not enable per-machine installation by + default in the CPack WIX Generator\. +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/CPackConfig/CMP0172-WARN.cmake b/Tests/RunCMake/CPackConfig/CMP0172-WARN.cmake new file mode 100644 index 000000000..d9c4f0ace --- /dev/null +++ b/Tests/RunCMake/CPackConfig/CMP0172-WARN.cmake @@ -0,0 +1 @@ +set(CMAKE_POLICY_WARNING_CMP0172 1) diff --git a/Tests/RunCMake/CPackConfig/CMakeLists.txt b/Tests/RunCMake/CPackConfig/CMakeLists.txt index 2b3e1f991..955c78a0c 100644 --- a/Tests/RunCMake/CPackConfig/CMakeLists.txt +++ b/Tests/RunCMake/CPackConfig/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST}) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake b/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake index 1de640bd9..4d610731a 100644 --- a/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake @@ -5,6 +5,9 @@ run_cmake(CMP0133-WARN) run_cmake(CMP0161-NEW) run_cmake(CMP0161-OLD) run_cmake(CMP0161-WARN) +run_cmake(CMP0172-NEW) +run_cmake(CMP0172-OLD) +run_cmake(CMP0172-WARN) run_cmake(Simple) run_cmake(Default) run_cmake(Special) diff --git a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt index 404e162d5..2811c3fdd 100644 --- a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt +++ b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt +++ b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt +++ b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTest/CMakeLists.txt b/Tests/RunCMake/CTest/CMakeLists.txt index 1319aecc3..9bfae1237 100644 --- a/Tests/RunCMake/CTest/CMakeLists.txt +++ b/Tests/RunCMake/CTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(NOT NoProject) project(${RunCMake_TEST} NONE) endif() diff --git a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt +++ b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in index e4a6f5f34..ea741b1ec 100644 --- a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in +++ b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestCommandLine@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/CTestCommandLine/FailureLabels-result.txt b/Tests/RunCMake/CTestCommandLine/FailureLabels-result.txt new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailureLabels-result.txt @@ -0,0 +1 @@ +8 diff --git a/Tests/RunCMake/CTestCommandLine/FailureLabels-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailureLabels-stderr.txt new file mode 100644 index 000000000..ba4235def --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailureLabels-stderr.txt @@ -0,0 +1 @@ +Errors while running CTest diff --git a/Tests/RunCMake/CTestCommandLine/FailureLabels-stdout.txt b/Tests/RunCMake/CTestCommandLine/FailureLabels-stdout.txt new file mode 100644 index 000000000..3fa9fb8c9 --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/FailureLabels-stdout.txt @@ -0,0 +1,4 @@ +The following tests FAILED: +[ ]+1 - ShortName \(Failed\) Label1 Label2 +[ ]+2 - LongerName \(Failed\) Label3 +[ ]+3 - Long_Test_Name_That_Is_Over_Fifty_Characters_In_Length \(Failed\) Label4 diff --git a/Tests/RunCMake/CTestCommandLine/PrintLabels-stdout.txt b/Tests/RunCMake/CTestCommandLine/PrintLabels-stdout.txt new file mode 100644 index 000000000..21791aa3b --- /dev/null +++ b/Tests/RunCMake/CTestCommandLine/PrintLabels-stdout.txt @@ -0,0 +1,6 @@ +^Test project [^ +]*/Tests/RunCMake/CTestCommandLine/PrintLabels +All Labels: + Label1 + Label2 + Label3$ diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index 190c6c1bb..b2374ca5a 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake @@ -7,6 +7,36 @@ set(ENV{no_proxy} "$ENV{no_proxy},badhostname.invalid") set(RunCMake_TEST_TIMEOUT 60) +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/FailureLabels) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " +add_test(ShortName \"${CMAKE_COMMAND}\" -E false) +set_tests_properties(ShortName PROPERTIES LABELS \"Label1;Label2\") +add_test(LongerName \"${CMAKE_COMMAND}\" -E false) +set_tests_properties(LongerName PROPERTIES LABELS \"Label3\") +add_test(Long_Test_Name_That_Is_Over_Fifty_Characters_In_Length \"${CMAKE_COMMAND}\" -E false) +set_tests_properties(Long_Test_Name_That_Is_Over_Fifty_Characters_In_Length PROPERTIES LABELS \"Label4\") +") + run_cmake_command(FailureLabels ${CMAKE_CTEST_COMMAND}) +endblock() + +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PrintLabels) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" " +add_test(A \"${CMAKE_COMMAND}\" -E true) +set_tests_properties(A PROPERTIES LABELS \"Label1;Label2\") +add_test(B \"${CMAKE_COMMAND}\" -E true) +set_tests_properties(B PROPERTIES LABELS \"Label3\") +") + run_cmake_command(PrintLabels ${CMAKE_CTEST_COMMAND} --print-labels) +endblock() + run_cmake_command(repeat-opt-bad1 ${CMAKE_CTEST_COMMAND} --repeat until-pass ) diff --git a/Tests/RunCMake/CTestCommandLine/test.cmake.in b/Tests/RunCMake/CTestCommandLine/test.cmake.in index 11bede721..ed9a3b004 100644 --- a/Tests/RunCMake/CTestCommandLine/test.cmake.in +++ b/Tests/RunCMake/CTestCommandLine/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in index cfcf56d25..0303da88a 100644 --- a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in +++ b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TimeoutAfterMatch NONE) include(CTest) add_test(NAME SleepFor1Second COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_SOURCE_DIR}/SleepFor1Second.cmake) diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in index 172d2c6bc..3d417783e 100644 --- a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in +++ b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/CXXModules/Inspect.cmake b/Tests/RunCMake/CXXModules/Inspect.cmake index e648e8c08..2913c879a 100644 --- a/Tests/RunCMake/CXXModules/Inspect.cmake +++ b/Tests/RunCMake/CXXModules/Inspect.cmake @@ -42,6 +42,8 @@ set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\") set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\") set(CMAKE_CXX_STANDARD_DEFAULT \"${CMAKE_CXX_STANDARD_DEFAULT}\") set(CMAKE_CXX20_STANDARD_COMPILE_OPTION \"${CMAKE_CXX20_STANDARD_COMPILE_OPTION}\") +set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT \"${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}\") +set(CMAKE_CXX_MODULE_MAP_FORMAT \"${CMAKE_CXX_MODULE_MAP_FORMAT}\") ") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-check.cmake new file mode 100644 index 000000000..2fd33c678 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-check.cmake @@ -0,0 +1,39 @@ +include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(have_file 0) + foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-compiledb-public.dir/${CXXModules_config}/CXXDependInfo.json") + continue () + endif () + set(have_file 1) + + set(CMAKE_BUILD_TYPE "${CXXModules_config}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-compiledb-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoCompileDatabase-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-compiledb-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoCompileDatabase-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 () + set(CXXModules_config "${CXXModules_default_build_type}") + set(CMAKE_BUILD_TYPE "${CXXModules_default_build_type}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-compiledb-public.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoCompileDatabase-public.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") + + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-compiledb-private.dir/CXXDependInfo.json" actual_contents) + file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoCompileDatabase-private.json" expect_contents) + check_json("${actual_contents}" "${expect_contents}") +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt new file mode 100644 index 000000000..b9886ad08 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) in CMakeLists.txt: + CMake's support for exporting build databases 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. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake new file mode 100644 index 000000000..91a6884e1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoCompileDatabase.cmake @@ -0,0 +1,54 @@ +# Fake out that we have dyndep; we only need to generate, not actually build +# here. +set(CMAKE_CXX_SCANDEP_SOURCE "") + +set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "4bd552e2-b7fb-429a-ab23-c83ef53f3f13") + +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-compiledb-public) +target_sources(ninja-compiledb-public + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.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-compiledb-public + PRIVATE + cxx_std_20) +set_property(TARGET ninja-compiledb-public + PROPERTY EXPORT_BUILD_DATABASE 1) + +add_library(ninja-compiledb-private) +target_sources(ninja-compiledb-private + PRIVATE + sources/module-impl.cxx + sources/module-internal-part-impl.cxx + sources/module-part-impl.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-compiledb-private + PRIVATE + cxx_std_20) +set_property(TARGET ninja-compiledb-private + PROPERTY EXPORT_BUILD_DATABASE 1) diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index 8b7bc8628..4ed79ef43 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -119,6 +119,7 @@ if (RunCMake_GENERATOR MATCHES "Ninja") run_cmake(NinjaDependInfoExportFilesystemSafe) run_cmake(NinjaDependInfoBMIInstall) run_cmake(NinjaForceResponseFile) # issue#25367 + run_cmake(NinjaDependInfoCompileDatabase) elseif (RunCMake_GENERATOR MATCHES "Visual Studio") run_cmake(VisualStudioNoSyntheticTargets) else () @@ -162,6 +163,16 @@ function (run_cxx_module_test directory) run_cmake_command("examples/${test_name}-build" "${CMAKE_COMMAND}" --build . --config Debug --target "${RunCMake_CXXModules_TARGET}") else () run_cmake_command("examples/${test_name}-build" "${CMAKE_COMMAND}" --build . --config Debug) + foreach (RunCMake_CXXModules_TARGET IN LISTS RunCMake_CXXModules_TARGETS) + set(RunCMake_CXXModules_CONFIG "Debug") + set(RunCMake_CXXModules_NAME_SUFFIX "") + if (RunCMake_CXXModules_TARGET MATCHES "(.*)@(.*)") + set(RunCMake_CXXModules_TARGET "${CMAKE_MATCH_1}") + set(RunCMake_CXXModules_CONFIG "${CMAKE_MATCH_2}") + set(RunCMake_CXXModules_NAME_SUFFIX "-${RunCMake_CXXModules_CONFIG}") + endif () + run_cmake_command("examples/${test_name}-target-${RunCMake_CXXModules_TARGET}${RunCMake_CXXModules_NAME_SUFFIX}" "${CMAKE_COMMAND}" --build . --target "${RunCMake_CXXModules_TARGET}" --config "${RunCMake_CXXModules_CONFIG}") + endforeach () endif () if (RunCMake_CXXModules_INSTALL) run_cmake_command("examples/${test_name}-install" "${CMAKE_COMMAND}" --build . --target install --config Debug) @@ -276,6 +287,32 @@ if ("compile_commands" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-compile-commands) endif () +macro (setup_export_build_database_targets) + set(RunCMake_CXXModules_TARGETS + cmake_build_database-CXX + cmake_build_database) + + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(INSERT RunCMake_CXXModules_TARGETS 0 + cmake_build_database-CXX-Debug + cmake_build_database-Debug + # Other config targets. + cmake_build_database-CXX-Release@Release + cmake_build_database-Release@Release) + endif () +endmacro () + +# Tests which require build database support. +if ("build_database" IN_LIST CMake_TEST_MODULE_COMPILATION) + setup_export_build_database_targets() + set(RunCMake_CXXModules_NO_TEST 1) + + run_cxx_module_test(export-build-database) + + unset(RunCMake_CXXModules_NO_TEST) + unset(RunCMake_CXXModules_TARGETS) +endif () + # Tests which require collation work. if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(public-req-private) @@ -304,6 +341,17 @@ if ("internal_partitions" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(internal-partitions) endif () +function (run_cxx_module_import_test type name) + set(RunCMake_CXXModules_INSTALL 0) + + if ("EXPORT_BUILD_DATABASE" IN_LIST ARGN) + list(REMOVE_ITEM ARGN EXPORT_BUILD_DATABASE) + list(APPEND ARGN -DCMAKE_EXPORT_BUILD_DATABASE=1) + endif () + + run_cxx_module_test(import-modules "import-modules-${name}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${name}-${type}" ${ARGN}) +endfunction () + # Tests which install BMIs if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-interface-no-properties-build) @@ -320,29 +368,22 @@ if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) - set(test_suffix export-interface-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build") - - set(test_suffix export-interface-no-properties-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DNO_PROPERTIES=1) - - set(test_suffix export-include-directories-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DINCLUDE_PROPERTIES=1) - - set(test_suffix export-bmi-and-interface-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DWITH_BMIS=1) + run_cxx_module_import_test(build export-interface-build) + run_cxx_module_import_test(build export-interface-no-properties-build -DNO_PROPERTIES=1) + run_cxx_module_import_test(build export-include-directories-build -DINCLUDE_PROPERTIES=1) + run_cxx_module_import_test(build export-bmi-and-interface-build -DWITH_BMIS=1) + run_cxx_module_import_test(build export-command-sepdir-build -DEXPORT_COMMAND_SEPDIR=1) + run_cxx_module_import_test(build export-transitive-targets-build -DTRANSITIVE_TARGETS=1) + run_cxx_module_import_test(build export-transitive-modules-build -DTRANSITIVE_MODULES=1) + run_cxx_module_import_test(build export-with-headers-build -DWITH_HEADERS=1) - set(test_suffix export-command-sepdir-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DEXPORT_COMMAND_SEPDIR=1) + if ("build_database" IN_LIST CMake_TEST_MODULE_COMPILATION) + setup_export_build_database_targets() - set(test_suffix export-transitive-targets-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DTRANSITIVE_TARGETS=1) + run_cxx_module_import_test(build export-build-database -DBUILD_DATABASE=1 EXPORT_BUILD_DATABASE) - set(test_suffix export-transitive-modules-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DTRANSITIVE_MODULES=1) - - set(test_suffix export-with-headers-build) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DWITH_HEADERS=1) + unset(RunCMake_CXXModules_TARGETS) + endif () endif () endif () @@ -369,31 +410,14 @@ if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION) - set(RunCMake_CXXModules_INSTALL 0) - set(test_suffix export-interface-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install") - - set(test_suffix export-interface-no-properties-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DNO_PROPERTIES=1) - - set(test_suffix export-include-directories-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DINCLUDE_PROPERTIES=1) - - set(test_suffix export-bmi-and-interface-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DWITH_BMIS=1) - - set(test_suffix export-command-sepdir-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DEXPORT_COMMAND_SEPDIR=1) - - set(test_suffix export-transitive-targets-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DTRANSITIVE_TARGETS=1) - - set(test_suffix export-transitive-modules-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DTRANSITIVE_MODULES=1) - - set(test_suffix export-with-headers-install) - run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DWITH_HEADERS=1) - set(RunCMake_CXXModules_INSTALL 1) + run_cxx_module_import_test(install export-interface-install) + run_cxx_module_import_test(install export-interface-no-properties-install -DNO_PROPERTIES=1) + run_cxx_module_import_test(install export-include-directories-install -DINCLUDE_PROPERTIES=1) + run_cxx_module_import_test(install export-bmi-and-interface-install -DWITH_BMIS=1) + run_cxx_module_import_test(install export-command-sepdir-install -DEXPORT_COMMAND_SEPDIR=1) + run_cxx_module_import_test(install export-transitive-targets-install -DTRANSITIVE_TARGETS=1) + run_cxx_module_import_test(install export-transitive-modules-install -DTRANSITIVE_MODULES=1) + run_cxx_module_import_test(install export-with-headers-install -DWITH_HEADERS=1) endif () endif () endif () diff --git a/Tests/RunCMake/CXXModules/check-json.cmake b/Tests/RunCMake/CXXModules/check-json.cmake index bb04b36d9..8d9597324 100644 --- a/Tests/RunCMake/CXXModules/check-json.cmake +++ b/Tests/RunCMake/CXXModules/check-json.cmake @@ -3,19 +3,48 @@ cmake_policy(SET CMP0057 NEW) function (json_placeholders in out) string(REPLACE "" "${CXXModules_config}" in "${in}") + string(TOLOWER "${CXXModules_config}" config_lower) + string(REPLACE "" "${config_lower}" in "${in}") + string(REPLACE "" "${CXXModules_config_other}" in "${in}") + string(TOLOWER "${CXXModules_config_other}" config_lower) + string(REPLACE "" "${config_lower}" in "${in}") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) string(REPLACE "" "/${CXXModules_config}" in "${in}") + string(REPLACE "" "/${CXXModules_config_other}" in "${in}") else () string(REPLACE "" "" in "${in}") + string(REPLACE "" "" in "${in}") endif () if (CMAKE_BUILD_TYPE) string(REPLACE "" "${CXXModules_config}" in "${in}") + string(REPLACE "" "${CXXModules_config_other}" in "${in}") else () string(REPLACE "" "noconfig" in "${in}") endif () string(REPLACE "" "${RunCMake_SOURCE_DIR}" in "${in}") string(REPLACE "" "${RunCMake_TEST_BINARY_DIR}" in "${in}") string(REPLACE "" "${CMAKE_CXX_OUTPUT_EXTENSION}" in "${in}") + if (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "gcc") + set(bmiflag "-fmodule-only") + set(bmiext "gcm") + elseif (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "clang") + set(bmiflag "--precompile") + set(bmiext "pcm") + elseif (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "msvc") + set(bmiflag "-ifcOutput.*") + set(bmiext "ifc") + endif () + string(REPLACE "" "${bmiflag}" in "${in}") + string(REPLACE "" ".${bmiext}" in "${in}") + set(output_flag "-o") + if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(output_flag "-Fo") + endif () + string(REPLACE "" "${output_flag}" in "${in}") + string(REPLACE "" "${CMAKE_CXX20_STANDARD_COMPILE_OPTION}" in "${in}") + string(REPLACE "" "[0-9a-f]+" in "${in}") + string(REPLACE "REGEX:" "" in "${in}") + string(REPLACE "PATH:" "" in "${in}") set("${out}" "${in}" PARENT_SCOPE) endfunction () @@ -46,7 +75,26 @@ function (check_json_value path actual_type expect_type actual_value expect_valu endif () json_placeholders("${expect_value}" expect_value_expanded) - if (NOT actual_value STREQUAL expect_value_expanded) + if (expect_value MATCHES "^REGEX:PATH:") + string(REPLACE "\\" "/" actual_value_check "${actual_value}") + string(REGEX REPLACE "^\"(.*)\"$" "\\1" actual_value_check "${actual_value_check}") + if (NOT actual_value_check MATCHES "^${expect_value_expanded}$") + list(APPEND RunCMake_TEST_FAILED + "String mismatch (path regex) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$") + endif () + elseif (expect_value MATCHES "^REGEX:") + if (NOT actual_value MATCHES "^${expect_value_expanded}$") + list(APPEND RunCMake_TEST_FAILED + "String mismatch (regex) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$") + endif () + elseif (expect_value MATCHES "^PATH:") + string(REPLACE "\\" "/" actual_value_check "${actual_value}") + string(REGEX REPLACE "^\"(.*)\"$" "\\1" actual_value_check "${actual_value_check}") + if (NOT actual_value_check STREQUAL "${expect_value_expanded}") + list(APPEND RunCMake_TEST_FAILED + "String mismatch (path) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$") + endif () + elseif (NOT actual_value STREQUAL expect_value_expanded) list(APPEND RunCMake_TEST_FAILED "String mismatch at ${path}: ${actual_value} vs. ${expect_value_expanded}") endif () @@ -61,6 +109,22 @@ endfunction () # Check that two arrays are the same. function (check_json_array path actual expect) + if (item_filter) + string(JSON iter_len LENGTH "${actual}") + set(idx 0) + while (idx LESS iter_len) + string(JSON type TYPE "${actual}" "${idx}") + string(JSON item GET "${actual}" "${idx}") + if (type STREQUAL "STRING" AND + item MATCHES "${item_filter}") + string(JSON actual REMOVE "${actual}" "${idx}") + math(EXPR iter_len "${iter_len} - 1") + else () + math(EXPR idx "${idx} + 1") + endif () + endwhile () + endif () + string(JSON actual_len LENGTH "${actual}") string(JSON expect_len LENGTH "${expect}") diff --git a/Tests/RunCMake/CXXModules/examples/build-database-check.cmake b/Tests/RunCMake/CXXModules/examples/build-database-check.cmake new file mode 100644 index 000000000..734b5809f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/build-database-check.cmake @@ -0,0 +1,81 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) + +include("${CMAKE_CURRENT_LIST_DIR}/../check-json.cmake") + +function (check_build_database expect_basename fname component) + if (component STREQUAL "NO_EXIST") + if (EXISTS "${RunCMake_TEST_BINARY_DIR}/${fname}") + list(APPEND RunCMake_TEST_FAILED + "Build database detected before it is expected (${fname}).") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif () + return () + endif () + + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${fname}") + list(APPEND RunCMake_TEST_FAILED + "No build database detected (${fname}).") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + return () + endif () + + if (component STREQUAL "ALL") + set(CXXModules_config "Debug") + set(suffix "all") + elseif (component STREQUAL "ALL_MULTI") + set(CXXModules_config "Debug") + set(CXXModules_config_other "Release") + set(suffix "all-multi") + elseif (component STREQUAL "JUST_CXX") + set(CXXModules_config "Debug") + set(suffix "cxx") + elseif (component STREQUAL "JUST_CXX_MULTI") + set(CXXModules_config "Debug") + set(CXXModules_config_other "Release") + set(suffix "cxx-multi") + elseif (component STREQUAL "CXX_AND_DEBUG") + set(CXXModules_config "Debug") + set(suffix "cxx-config") + elseif (component STREQUAL "CXX_AND_RELEASE") + set(CXXModules_config "Release") + set(suffix "cxx-config") + elseif (component STREQUAL "JUST_DEBUG") + set(CXXModules_config "Debug") + set(suffix "config") + elseif (component STREQUAL "JUST_RELEASE") + set(CXXModules_config "Release") + set(suffix "config") + elseif (component STREQUAL "JUST_TARGET_DEBUG") + set(CXXModules_config "Debug") + set(suffix "target") + elseif (component STREQUAL "JUST_TARGET_RELEASE") + set(CXXModules_config "Release") + set(suffix "target") + elseif (component STREQUAL "JUST_TARGET") + set(CXXModules_config "Debug") + set(suffix "target") + else () + list(APPEND RunCMake_TEST_FAILED + "Unrecognized test component for ${fname}: ${component}") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + return () + endif () + + set(expected_file "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/expect/${expect_basename}-${suffix}.json") + if (NOT EXISTS "${expected_file}") + list(APPEND RunCMake_TEST_FAILED + "No expected output JSON file found: ${expected_file}") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + return () + endif () + + file(READ "${RunCMake_TEST_BINARY_DIR}/${fname}" actual) + file(READ "${expected_file}" expect) + + check_json("${actual}" "${expect}") + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction () + +cmake_policy(POP) diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all-multi.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all-multi.json new file mode 100644 index 000000000..e1603bee2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all-multi.json @@ -0,0 +1,300 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + }, + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all.json new file mode 100644 index 000000000..440bc4ef6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-all.json @@ -0,0 +1,153 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-config.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-config.json new file mode 100644 index 000000000..88771651e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-config.json @@ -0,0 +1,153 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-config.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-config.json new file mode 100644 index 000000000..88771651e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-config.json @@ -0,0 +1,153 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-multi.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-multi.json new file mode 100644 index 000000000..0a27fecd6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx-multi.json @@ -0,0 +1,300 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + }, + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx.json new file mode 100644 index 000000000..88771651e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-cxx.json @@ -0,0 +1,153 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "PATH:/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all-multi.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all-multi.json new file mode 100644 index 000000000..2fdaef5ad --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all-multi.json @@ -0,0 +1,278 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all.json new file mode 100644 index 000000000..cb3b3a230 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-all.json @@ -0,0 +1,142 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-config.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-config.json new file mode 100644 index 000000000..cb3b3a230 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-config.json @@ -0,0 +1,142 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-config.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-config.json new file mode 100644 index 000000000..cb3b3a230 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-config.json @@ -0,0 +1,142 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-multi.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-multi.json new file mode 100644 index 000000000..2fdaef5ad --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx-multi.json @@ -0,0 +1,278 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx.json new file mode 100644 index 000000000..cb3b3a230 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-cxx.json @@ -0,0 +1,142 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + }, + { + "family-name": "REGEX:CXXModules::export_build_database@", + "name": "REGEX:CXXModules__export_build_database@synth_@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option", + "REGEX:PATH:-Ddepflag=\"CMakeFiles/CXXModules__export_build_database@synth_.dir/.d\"", + "REGEX:", + "REGEX:PATH:CMakeFiles/CXXModules__export_build_database@synth_.dir/", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_interface_define", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "PATH:-I/examples/export-build-database/target_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dtarget_interface_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": { + "importable": "" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-target.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-target.json new file mode 100644 index 000000000..d2d975262 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-imported-target.json @@ -0,0 +1,59 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "use_import_interfaces", + "name": "use_import_interfaces@", + "translation-units": [ + { + "arguments": [ + "", + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option", + "PATH:-Ddepflag=\"CMakeFiles/use_import_interfaces.dir/use.cxx.d\"", + "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "-c", + "PATH:/examples/import-modules/use.cxx" + ], + "baseline-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "local-arguments": [ + "-Dtarget_interface_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/target_interface_include", + "PATH:-I/examples/export-build-database/target_public_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dtarget_interface_option", + "-Dtarget_public_option" + ], + "object": "PATH:CMakeFiles/use_import_interfaces.dir/use.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/import-modules/use.cxx", + "work-directory": "" + } + ], + "visible-sets": ["REGEX:CXXModules__export_build_database@synth_@"] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/expect/export-build-database-target.json b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-target.json new file mode 100644 index 000000000..440bc4ef6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/expect/export-build-database-target.json @@ -0,0 +1,153 @@ +{ + "version": 1, + "revision": 0, + "sets": [ + { + "family-name": "export_build_database", + "name": "export_build_database@", + "translation-units": [ + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/lib.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "-c", + "PATH:/examples/export-build-database/lib.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/lib.cxx", + "private": true, + "provides": {}, + "requires": ["importable"], + "source": "PATH:/examples/export-build-database/lib.cxx", + "work-directory": "" + }, + { + "arguments": [ + "", + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option", + "PATH:-Ddepflag=\"CMakeFiles/export_build_database.dir/importable.cxx.d\"", + "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "-c", + "PATH:/examples/export-build-database/importable.cxx" + ], + "baseline-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option" + ], + "local-arguments": [ + "-Ddep_interface_define", + "-Dfrom_compile_definitions", + "-Dtarget_private_define", + "-Dtarget_public_define", + "PATH:-I/examples/export-build-database/from_include_directories", + "PATH:-I/examples/export-build-database/target_private_include", + "PATH:-I/examples/export-build-database/target_public_include", + "PATH:-I/examples/export-build-database/dep_interface_include", + "-Dfrom_cmake_cxx_flags", + "-Dfrom_cmake_cxx__flags", + "", + "-Dfrom_compile_flags", + "-Dfrom_compile_options", + "-Dtarget_private_option", + "-Dtarget_public_option", + "-Ddep_interface_option", + "-Dfrom_source_flag", + "-Dfrom_source_option" + ], + "object": "PATH:CMakeFiles/export_build_database.dir/importable.cxx", + "private": true, + "provides": { + "importable": "/CMakeFiles/export_build_database.dir/importable" + }, + "requires": [], + "source": "PATH:/examples/export-build-database/importable.cxx", + "work-directory": "" + } + ], + "visible-sets": [] + } + ] +} diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-build-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-build-check.cmake new file mode 100644 index 000000000..131926baf --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-build-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" NO_EXIST) +check_build_database("export-build-database" "build_database_Debug.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + # check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) + # check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) + # check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-check.cmake new file mode 100644 index 000000000..efa43d285 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" NO_EXIST) +check_build_database("export-build-database" "build_database_Debug.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" NO_EXIST) + + check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" NO_EXIST) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-setup.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-setup.cmake new file mode 100644 index 000000000..20a9e902f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-setup.cmake @@ -0,0 +1,39 @@ +set(CMAKE_EXPERIMENTAL_EXPORT_BUILD_DATABASE "4bd552e2-b7fb-429a-ab23-c83ef53f3f13") + +get_property(is_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if (is_multiconfig) + set(CMAKE_CONFIGURATION_TYPES "Debug" "Release") +endif () + +set(CMAKE_EXPORT_BUILD_DATABASE 1) + +# Mock up depfile flags to keep things consistent; we don't need accurate +# dependency tracking for this test case anyways. +set(CMAKE_CXX_DEPFILE_FORMAT gcc) +set(CMAKE_DEPFILE_FLAGS_CXX "-Ddepflag=\\\"\\\"") +unset(CMAKE_CXX_DEPFILE_EXTENSION_REPLACE) + +# Disable MSVC flag injection from CMake abstractions. +set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "") +set(CMAKE_MSVC_RUNTIME_LIBRARY "") + +if (CMAKE_CXX_MODULE_BMI_ONLY_FLAG MATCHES "ifcOutput") + # Make a single flag for BMI-only to make the JSON expectations simpler. + set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG + "-ifcOnly;-ifcOutput") +endif () + +# Disable extensions to keep flag selection simpler. +set(CMAKE_CXX_EXTENSIONS 0) + +if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + set(output_flag "-Fo") +else () + set(output_flag "-o") +endif () +set(CMAKE_CXX_COMPILE_OBJECT + " ${output_flag} -c ") + +set(CMAKE_CXX_FLAGS "-Dfrom_cmake_cxx_flags") +set(CMAKE_CXX_FLAGS_DEBUG "-Dfrom_cmake_cxx_debug_flags") +set(CMAKE_CXX_FLAGS_RELEASE "-Dfrom_cmake_cxx_release_flags") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-build-database-stderr.txt new file mode 100644 index 000000000..b9886ad08 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) in CMakeLists.txt: + CMake's support for exporting build databases 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. diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database-check.cmake new file mode 100644 index 000000000..79db48267 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database-check.cmake @@ -0,0 +1,21 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database" "build_database.json" ALL_MULTI) + check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX_MULTI) + + check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database" "build_database_Release.json" JUST_RELEASE) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database" "build_database.json" ALL) + check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX) + + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX-check.cmake new file mode 100644 index 000000000..cf712bab1 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX-check.cmake @@ -0,0 +1,21 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX_MULTI) + + check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database" "build_database_Release.json" JUST_RELEASE) + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database" "build_database_CXX.json" JUST_CXX) + + check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Debug-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Debug-check.cmake new file mode 100644 index 000000000..6f60675dd --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Debug-check.cmake @@ -0,0 +1,14 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("export-build-database" "build_database_Debug.json" NO_EXIST) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + +check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) +check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake new file mode 100644 index 000000000..dcb36c93e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake @@ -0,0 +1,14 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + +check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) +check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Debug-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Debug-check.cmake new file mode 100644 index 000000000..686830260 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Debug-check.cmake @@ -0,0 +1,14 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + +check_build_database("export-build-database" "build_database_CXX_Release.json" NO_EXIST) +check_build_database("export-build-database" "build_database_Release.json" NO_EXIST) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" NO_EXIST) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Release-Release-check.cmake b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Release-Release-check.cmake new file mode 100644 index 000000000..31da4be87 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database-target-cmake_build_database/Release-Release-check.cmake @@ -0,0 +1,14 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") + +check_build_database("export-build-database" "build_database.json" NO_EXIST) +check_build_database("export-build-database" "build_database_CXX.json" NO_EXIST) + +check_build_database("export-build-database" "build_database_CXX_Debug.json" CXX_AND_DEBUG) +check_build_database("export-build-database" "build_database_Debug.json" JUST_DEBUG) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + +check_build_database("export-build-database" "build_database_CXX_Release.json" CXX_AND_RELEASE) +check_build_database("export-build-database" "build_database_Release.json" JUST_RELEASE) +check_build_database("export-build-database" "CMakeFiles/export_build_database.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-build-database/CMakeLists.txt new file mode 100644 index 000000000..318f91cb5 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database/CMakeLists.txt @@ -0,0 +1,85 @@ +cmake_minimum_required(VERSION 3.29) +project(cxx_modules_export_build_database CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +include("${CMAKE_SOURCE_DIR}/../export-build-database-setup.cmake") + +add_compile_options(-Dfrom_compile_options) +add_compile_definitions(-Dfrom_compile_definitions) +include_directories(from_include_directories) + +add_library(provide_flags INTERFACE) +target_compile_options(provide_flags + INTERFACE + -Ddep_interface_option) +target_compile_definitions(provide_flags + INTERFACE + dep_interface_define) +target_include_directories(provide_flags + INTERFACE + dep_interface_include) + +add_library(export_build_database) +target_sources(export_build_database + PRIVATE + lib.cxx + PUBLIC + FILE_SET modules + TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx) +target_compile_features(export_build_database PUBLIC cxx_std_20) +target_link_libraries(export_build_database + PRIVATE + provide_flags) + +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_FLAGS "-Dfrom_source_flag") +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_OPTIONS "-Dfrom_source_option") +set_property(SOURCE importable.cxx APPEND + PROPERTY COMPILE_DEFINITIONS "from_source_define") + +set_property(TARGET export_build_database APPEND + PROPERTY COMPILE_FLAGS "-Dfrom_compile_flags") +target_compile_options(export_build_database + PRIVATE + -Dtarget_private_option + INTERFACE + -Dtarget_interface_option + PUBLIC + -Dtarget_public_option) +target_compile_definitions(export_build_database + PRIVATE + target_private_define + INTERFACE + target_interface_define + PUBLIC + target_public_define) +target_include_directories(export_build_database + PRIVATE + target_private_include + INTERFACE + target_interface_include + PUBLIC + target_public_include) + +install(TARGETS export_build_database + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-targets.cmake") +install(TARGETS provide_flags + EXPORT CXXModulesDeps) +export(EXPORT CXXModulesDeps + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-dep-targets.cmake") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_build_database-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_build_database-dep-targets.cmake\") +include(\"\${CMAKE_CURRENT_LIST_DIR}/export_build_database-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/dep_interface_include/anchor b/Tests/RunCMake/CXXModules/examples/export-build-database/dep_interface_include/anchor new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-build-database/importable.cxx new file mode 100644 index 000000000..607680a07 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database/importable.cxx @@ -0,0 +1,6 @@ +export module importable; + +export int from_import() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/lib.cxx b/Tests/RunCMake/CXXModules/examples/export-build-database/lib.cxx new file mode 100644 index 000000000..b144b2770 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-build-database/lib.cxx @@ -0,0 +1,6 @@ +import importable; + +int f() +{ + return from_import(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/target_interface_include/anchor b/Tests/RunCMake/CXXModules/examples/export-build-database/target_interface_include/anchor new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CXXModules/examples/export-build-database/target_public_include/anchor b/Tests/RunCMake/CXXModules/examples/export-build-database/target_public_include/anchor new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-build-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-build-check.cmake new file mode 100644 index 000000000..21a29b6ac --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-build-check.cmake @@ -0,0 +1,18 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" NO_EXIST) +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Debug.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/Debug/CXX_build_database.json" NO_EXIST) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/CXX_build_database.json" NO_EXIST) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-check.cmake new file mode 100644 index 000000000..21a29b6ac --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-check.cmake @@ -0,0 +1,18 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" NO_EXIST) +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Debug.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/Debug/CXX_build_database.json" NO_EXIST) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database-imported" "CMakeFiles/export-module-commands.dir/CXX_build_database.json" NO_EXIST) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-stderr.txt new file mode 100644 index 000000000..b9886ad08 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) in CMakeLists.txt: + CMake's support for exporting build databases 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. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database-check.cmake new file mode 100644 index 000000000..cfa3a3c06 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database-check.cmake @@ -0,0 +1,22 @@ +include("${CMAKE_CURRENT_LIST_DIR}/build-database-check.cmake") +set(item_filter "-ifcOnly") + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database.json" ALL_MULTI) + check_build_database("export-build-database-imported" "build_database_CXX.json" JUST_CXX_MULTI) + + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database-imported" "build_database_Release.json" JUST_RELEASE) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database-imported" "build_database.json" ALL) + check_build_database("export-build-database-imported" "build_database_CXX.json" JUST_CXX) + + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX-check.cmake new file mode 100644 index 000000000..39203d90d --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX-check.cmake @@ -0,0 +1,22 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX.json" JUST_CXX_MULTI) + + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database-imported" "build_database_Release.json" JUST_RELEASE) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database-imported" "build_database_CXX.json" JUST_CXX) + + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Debug-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Debug-check.cmake new file mode 100644 index 000000000..cf01237e9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Debug-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake new file mode 100644 index 000000000..7e231eaf2 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/CXX/Release-Release-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../../build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database-imported" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Debug-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Debug-check.cmake new file mode 100644 index 000000000..876888c12 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Debug-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" NO_EXIST) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "build_database_Release.json" NO_EXIST) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" NO_EXIST) +else () + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Release-check.cmake b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Release-check.cmake new file mode 100644 index 000000000..94edc3b2c --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-build-database-target-cmake_build_database/Release-check.cmake @@ -0,0 +1,19 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../build-database-check.cmake") +set(item_filter "-ifcOnly") + +check_build_database("export-build-database-imported" "build_database.json" NO_EXIST) +check_build_database("export-build-database-imported" "build_database_CXX.json" JUST_CXX) + +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + check_build_database("export-build-database-imported" "build_database_CXX_Debug.json" CXX_AND_DEBUG) + check_build_database("export-build-database-imported" "build_database_Debug.json" JUST_DEBUG) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Debug/CXX_build_database.json" JUST_TARGET_DEBUG) + + check_build_database("export-build-database-imported" "build_database_CXX_Release.json" CXX_AND_RELEASE) + check_build_database("export-build-database-imported" "build_database_Release.json" JUST_RELEASE) + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/Release/CXX_build_database.json" JUST_TARGET_RELEASE) +else () + check_build_database("export-build-database-imported" "CMakeFiles/use_import_interfaces.dir/CXX_build_database.json" JUST_TARGET) +endif () + +string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt index 6796660ba..a12db0af5 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt @@ -19,12 +19,25 @@ elseif (TRANSITIVE_MODULES) set(package_name "export_transitive_modules") elseif (WITH_HEADERS) set(package_name "export_with_headers") +elseif (BUILD_DATABASE) + include("${CMAKE_SOURCE_DIR}/../export-build-database-setup.cmake") + set(package_name "export_build_database") else () set(package_name "export_interfaces") endif () set(target_name "CXXModules::${package_name}") find_package("${package_name}" REQUIRED) +if (BUILD_DATABASE) + # Remove `-isystem` flags for better flag checking consistency. + set_property(TARGET "CXXModules::${package_name}" "CXXModules::provide_flags" + PROPERTY SYSTEM 0) + # Disable debug and runtime flag injection. + set_property(TARGET "CXXModules::${package_name}" + PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "") + set_property(TARGET "CXXModules::${package_name}" + PROPERTY MSVC_RUNTIME_LIBRARY "") +endif () add_executable(use_import_interfaces) target_sources(use_import_interfaces @@ -32,5 +45,14 @@ target_sources(use_import_interfaces use.cxx) target_compile_features(use_import_interfaces PRIVATE cxx_std_20) target_link_libraries(use_import_interfaces PRIVATE "${target_name}") +if (BUILD_DATABASE AND + # Detect Clang targeting MSVC ABI. + CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + # Link to `libcmt.lib`. Build database tests neuter the runtime library + # selection to make flag matching easier. Manually perform the "link the + # runtime library" injected into the object files by the runtime library + # flag. + target_link_libraries(use_import_interfaces PRIVATE libcmt) +endif () add_test(NAME use_import_interfaces COMMAND use_import_interfaces) diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json index 78f792867..dfca73ea1 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json @@ -38,6 +38,7 @@ "visibility": "PRIVATE" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -47,5 +48,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-bmi-install-private.dir" + "module-dir": "/CMakeFiles/ninja-bmi-install-private.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json index 6c2335418..20f164a2f 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json @@ -38,6 +38,7 @@ "visibility": "PUBLIC" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -47,5 +48,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-bmi-install-public.dir" + "module-dir": "/CMakeFiles/ninja-bmi-install-public.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-private.json new file mode 100644 index 000000000..a41316060 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-private.json @@ -0,0 +1,51 @@ +{ + "bmi-installation": null, + "compiler-id": "", + "compiler-frontend-variant": "", + "compiler-simulate-id": "", + "config": "", + "cxx-modules": { + "CMakeFiles/ninja-compiledb-private.dir/sources/module-internal-part.cxx": { + "bmi-only": false, + "destination": null, + "name": "internal_partitions", + "relative-directory": "sources", + "source": "/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-compiledb-private.dir/sources/module-part.cxx": { + "bmi-only": false, + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + }, + "CMakeFiles/ninja-compiledb-private.dir/sources/module.cxx": { + "bmi-only": false, + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PRIVATE" + } + }, + "database-info": { + "template-path": "/CMakeFiles/ninja-compiledb-private.dir/CXX_build_database.json.in", + "output": "/CMakeFiles/ninja-compiledb-private.dir/CXX_build_database.json" + }, + "dir-cur-bld": "", + "dir-cur-src": "", + "dir-top-bld": "", + "dir-top-src": "", + "exports": [], + "forward-modules-from-target-dirs": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "/CMakeFiles/ninja-compiledb-private.dir", + "sources": {} +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-public.json new file mode 100644 index 000000000..632d56b41 --- /dev/null +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoCompileDatabase-public.json @@ -0,0 +1,51 @@ +{ + "bmi-installation": null, + "compiler-id": "", + "compiler-frontend-variant": "", + "compiler-simulate-id": "", + "config": "", + "cxx-modules": { + "CMakeFiles/ninja-compiledb-public.dir/sources/module-internal-part.cxx": { + "bmi-only": false, + "destination": null, + "name": "internal_partitions", + "relative-directory": "sources", + "source": "/sources/module-internal-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-compiledb-public.dir/sources/module-part.cxx": { + "bmi-only": false, + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "/sources/module-part.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + }, + "CMakeFiles/ninja-compiledb-public.dir/sources/module.cxx": { + "bmi-only": false, + "destination": null, + "name": "modules", + "relative-directory": "", + "source": "/sources/module.cxx", + "type": "CXX_MODULES", + "visibility": "PUBLIC" + } + }, + "database-info": { + "template-path": "/CMakeFiles/ninja-compiledb-public.dir/CXX_build_database.json.in", + "output": "/CMakeFiles/ninja-compiledb-public.dir/CXX_build_database.json" + }, + "dir-cur-bld": "", + "dir-cur-src": "", + "dir-top-bld": "", + "dir-top-src": "", + "exports": [], + "forward-modules-from-target-dirs": [], + "include-dirs": [], + "language": "CXX", + "linked-target-dirs": [], + "module-dir": "/CMakeFiles/ninja-compiledb-public.dir", + "sources": {} +} diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json index 71b2b66e8..235e17de7 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json @@ -33,6 +33,7 @@ "visibility": "PRIVATE" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -79,5 +80,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-exports-private.dir" + "module-dir": "/CMakeFiles/ninja-exports-private.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json index a9cde991a..ed19dd81b 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json @@ -33,6 +33,7 @@ "visibility": "PUBLIC" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -79,5 +80,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-exports-public.dir" + "module-dir": "/CMakeFiles/ninja-exports-public.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-private.json index 7905c535f..22005a2bb 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-private.json @@ -33,6 +33,7 @@ "visibility": "PRIVATE" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -79,5 +80,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-exports-private.dir" + "module-dir": "/CMakeFiles/ninja-exports-private.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-public.json index 1734590a9..e9d485222 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExportFilesystemSafe-public.json @@ -33,6 +33,7 @@ "visibility": "PUBLIC" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -79,5 +80,6 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-exports-public.dir" + "module-dir": "/CMakeFiles/ninja-exports-public.dir", + "sources": {} } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json index ed61e0e27..571998f68 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json @@ -33,6 +33,7 @@ "visibility": "PRIVATE" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -42,5 +43,23 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-file-sets-private.dir" + "module-dir": "/CMakeFiles/ninja-file-sets-private.dir", + "sources": { + "CMakeFiles/ninja-file-sets-private.dir/sources/module-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-private.dir/sources/module-internal-part-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-internal-part-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-private.dir/sources/module-part-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-part-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-private.dir/sources/module-use.cxx" : { + "language" : "CXX", + "source" : "/sources/module-use.cxx" + } + } } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json index 171935f26..781b988a6 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json @@ -33,6 +33,7 @@ "visibility": "PUBLIC" } }, + "database-info": null, "dir-cur-bld": "", "dir-cur-src": "", "dir-top-bld": "", @@ -42,5 +43,23 @@ "language": "CXX", "forward-modules-from-target-dirs": [], "linked-target-dirs": [], - "module-dir": "/CMakeFiles/ninja-file-sets-public.dir" + "module-dir": "/CMakeFiles/ninja-file-sets-public.dir", + "sources": { + "CMakeFiles/ninja-file-sets-public.dir/sources/module-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-public.dir/sources/module-internal-part-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-internal-part-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-public.dir/sources/module-part-impl.cxx" : { + "language" : "CXX", + "source" : "/sources/module-part-impl.cxx" + }, + "CMakeFiles/ninja-file-sets-public.dir/sources/module-use.cxx" : { + "language" : "CXX", + "source" : "/sources/module-use.cxx" + } + } } diff --git a/Tests/RunCMake/CacheNewline/CMakeLists.txt b/Tests/RunCMake/CacheNewline/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CacheNewline/CMakeLists.txt +++ b/Tests/RunCMake/CacheNewline/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt index 9f18d8d2a..820a5eb1f 100644 --- a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt +++ b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(CheckIPOSupported) diff --git a/Tests/RunCMake/CheckModules/CMakeLists.txt b/Tests/RunCMake/CheckModules/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CheckModules/CMakeLists.txt +++ b/Tests/RunCMake/CheckModules/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CheckModules/CheckLinkDirectories.cmake b/Tests/RunCMake/CheckModules/CheckLinkDirectories.cmake new file mode 100644 index 000000000..1bfb35304 --- /dev/null +++ b/Tests/RunCMake/CheckModules/CheckLinkDirectories.cmake @@ -0,0 +1,122 @@ +cmake_policy(SET CMP0075 NEW) + +enable_language(C) +enable_language(CXX) + +function(test_check_result isSucceed functionName) + if(isSucceed AND NOT IS_NEED_SUCCESS) + message(SEND_ERROR "${functionName}: check succeeded instead of failure") + elseif((NOT isSucceed) AND IS_NEED_SUCCESS) + message(SEND_ERROR "${functionName}: check failed instead of success") + endif() +endfunction() + +# Common variables +set(validCSourceCode "int main() {return 0;}") + +### +# Checking checkers +### + +# It uses common internal function `CMAKE_CHECK_SOURCE_COMPILES()` +# include(CheckCCompilerFlag) + +# Also checks common internal function `CMAKE_CHECK_SOURCE_COMPILES()` +include(CheckCSourceCompiles) +check_c_source_compiles("${validCSourceCode}" CHECK_C_SOURCE_COMPILES_SUCCEED) +test_check_result("${CHECK_C_SOURCE_COMPILES_SUCCEED}" check_c_source_compiles) + +# Also checks common internal function `CMAKE_CHECK_SOURCE_RUNS()` +include(CheckCSourceRuns) +check_c_source_runs("${validCSourceCode}" CHECK_C_SOURCE_RUNS_SUCCEED) +test_check_result("${CHECK_C_SOURCE_RUNS_SUCCEED}" check_c_source_runs) + +# Shares code with similar C checkers +# include(CheckCXXCompilerFlag) +# include(CheckCXXSourceCompiles) +# include(CheckCXXSourceRuns) +# include(CheckCXXSymbolExists) + +# Shares code with similar C checkers +# include(CheckCompilerFlag) +# include(CheckSourceCompiles) +# include(CheckSourceRuns) + +# Shares code with similar C checkers +# include(CheckFortranCompilerFlag) +# include(CheckFortranFunctionExists) # No way to test it +# include(CheckFortranSourceCompiles) +# include(CheckFortranSourceRuns) + +include(CheckFunctionExists) +check_function_exists (memcpy CHECK_FUNCTION_EXISTS_SUCCEED) +test_check_result("${CHECK_FUNCTION_EXISTS_SUCCEED}" check_function_exists) + +include(CheckIncludeFile) +check_include_file("stddef.h" CHECK_INCLUDE_FILE_SUCCEED) +test_check_result("${CHECK_INCLUDE_FILE_SUCCEED}" check_include_file) + +include(CheckIncludeFileCXX) +check_include_file_cxx("stddef.h" CHECK_INCLUDE_FILE_CXX_SUCCEED) +test_check_result("${CHECK_INCLUDE_FILE_CXX_SUCCEED}" check_include_file_cxx) + +include(CheckIncludeFiles) +check_include_files("stddef.h;stdlib.h" CHECK_INCLUDE_FILES_SUCCEED) +test_check_result("${CHECK_INCLUDE_FILES_SUCCEED}" check_include_files) + +include(CheckLibraryExists) +block(PROPAGATE HAVE_LIBM) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_DIRECTORIES) + check_library_exists(m ceil "" HAVE_LIBM) +endblock() + +if(HAVE_LIBM) + check_library_exists(m ceil "" CHECK_LIBRARY_EXISTS_SUCCEED) + test_check_result("${CHECK_LIBRARY_EXISTS_SUCCEED}" check_library_exists) +endif() + +# Shares code with similar C checkers +# include(CheckOBJCCompilerFlag) +# include(CheckOBJCSourceCompiles) +# include(CheckOBJCSourceRuns) + +# Shares code with similar C checkers +# include(CheckOBJCXXCompilerFlag) +# include(CheckOBJCXXSourceCompiles) +# include(CheckOBJCXXSourceRuns) + +include(CheckPrototypeDefinition) +block(PROPAGATE CHECK_PROTOTYPE_DEFINITION_WORKS) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_LINK_DIRECTORIES) + check_prototype_definition(memmove + "void *memmove(void *dest, const void *src, size_t n)" + "NULL" + "string.h" + CHECK_PROTOTYPE_DEFINITION_SUCCEED) +endblock() + +if (CHECK_PROTOTYPE_DEFINITION_WORKS) + check_prototype_definition(memmove + "void *memmove(void *dest, const void *src, size_t n)" + "NULL" + "string.h" + CHECK_PROTOTYPE_DEFINITION_SUCCEED) + test_check_result("${CHECK_PROTOTYPE_DEFINITION_SUCCEED}" check_prototype_definition) +endif() + +# It uses common internal function `CMAKE_CHECK_SOURCE_COMPILES()` +# include(CheckStructHasMember) + +include(CheckSymbolExists) +check_symbol_exists(errno "errno.h" CHECK_SYMBOL_EXISTS_SUCCEED) +test_check_result("${CHECK_SYMBOL_EXISTS_SUCCEED}" check_symbol_exists) + +include(CheckTypeSize) +check_type_size(int SIZEOF_INT) +test_check_result("${HAVE_SIZEOF_INT}" check_type_size) + +include(CheckVariableExists) +check_variable_exists(myTestVar CHECK_VARIABLE_EXISTS_SUCCEED) +test_check_result("${CHECK_VARIABLE_EXISTS_SUCCEED}" check_variable_exists) diff --git a/Tests/RunCMake/CheckModules/CheckLinkDirectoriesTestLib.cmake b/Tests/RunCMake/CheckModules/CheckLinkDirectoriesTestLib.cmake new file mode 100644 index 000000000..aa8a6c37d --- /dev/null +++ b/Tests/RunCMake/CheckModules/CheckLinkDirectoriesTestLib.cmake @@ -0,0 +1 @@ +add_subdirectory(TestLib) diff --git a/Tests/RunCMake/CheckModules/RunCMakeTest.cmake b/Tests/RunCMake/CheckModules/RunCMakeTest.cmake index 8a046e14c..762204e1e 100644 --- a/Tests/RunCMake/CheckModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CheckModules/RunCMakeTest.cmake @@ -22,3 +22,31 @@ run_cmake(CheckIncludeFilesOkNoC) run_cmake(CheckIncludeFilesMissingLanguage) run_cmake(CheckIncludeFilesUnknownArgument) run_cmake(CheckIncludeFilesUnknownLanguage) + +block() + # Set common variables + set(libDir ${RunCMake_BINARY_DIR}/CheckLinkDirectoriesTestLib-build/TestLib/lib) + set(libName mySharedLibrary) + + # Build common part + run_cmake(CheckLinkDirectoriesTestLib) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(CheckLinkDirectoriesTestLib ${CMAKE_COMMAND} --build .) + + # Run tests cleanly + unset(RunCMake_TEST_NO_CLEAN) + unset(RunCMake_TEST_OUTPUT_MERGE) + + set(RunCMake_TEST_VARIANT_DESCRIPTION "WithDirectories") + run_cmake_with_options("CheckLinkDirectories" + "-DCMAKE_REQUIRED_LIBRARIES=${libName}" + "-DCMAKE_REQUIRED_LINK_DIRECTORIES=${libDir}" + "-DIS_NEED_SUCCESS:BOOL=ON" + ) + set(RunCMake_TEST_VARIANT_DESCRIPTION "WithoutDirectories") + run_cmake_with_options("CheckLinkDirectories" + "-DCMAKE_REQUIRED_LIBRARIES=${libName}" + "-DIS_NEED_SUCCESS:BOOL=OFF" + ) +endblock() diff --git a/Tests/RunCMake/CheckModules/TestLib/CMakeLists.txt b/Tests/RunCMake/CheckModules/TestLib/CMakeLists.txt new file mode 100644 index 000000000..e8c4fed45 --- /dev/null +++ b/Tests/RunCMake/CheckModules/TestLib/CMakeLists.txt @@ -0,0 +1,3 @@ +project(TestLib C) +add_library(mySharedLibrary TestLib.c) +set_target_properties(mySharedLibrary PROPERTIES ARCHIVE_OUTPUT_DIRECTORY $<1:lib>) diff --git a/Tests/RunCMake/CheckModules/TestLib/TestLib.c b/Tests/RunCMake/CheckModules/TestLib/TestLib.c new file mode 100644 index 000000000..b4bc30d3d --- /dev/null +++ b/Tests/RunCMake/CheckModules/TestLib/TestLib.c @@ -0,0 +1,6 @@ +int myTestVar = 42; + +int TestSymbol(void) +{ + return 0; +} diff --git a/Tests/RunCMake/ClangTidy/CMakeLists.txt b/Tests/RunCMake/ClangTidy/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/ClangTidy/CMakeLists.txt +++ b/Tests/RunCMake/ClangTidy/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Codegen/CMakeLists.txt b/Tests/RunCMake/Codegen/CMakeLists.txt new file mode 100644 index 000000000..f65150d2a --- /dev/null +++ b/Tests/RunCMake/Codegen/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.29) +project(${RunCMake_TEST} LANGUAGES C) + +# This value is read from the top level CMakeLists.txt +cmake_policy(SET CMP0171 NEW) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Codegen/RunCMakeTest.cmake b/Tests/RunCMake/Codegen/RunCMakeTest.cmake new file mode 100644 index 000000000..bbd70b074 --- /dev/null +++ b/Tests/RunCMake/Codegen/RunCMakeTest.cmake @@ -0,0 +1,33 @@ +include(RunCMake) + +function(run_codegen case) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build) + + run_cmake(${case}) + + set(RunCMake_TEST_NO_CLEAN 1) + + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --target codegen --config Debug) +endfunction() + +# Builds codegen target when there are no custom commands marked codegen +run_codegen("no-codegen") + +# We don't want codegen to drive parts of the project that are EXCLUDE_FROM_ALL +run_codegen("exclude-from-all") + +# Ensures codegen builds minimal build graphs +run_codegen("min-graph-1") +run_codegen("min-graph-2") +run_codegen("min-graph-3") + +# Handle specific cases that can affect codegen +run_codegen("add-dependencies") +run_codegen("add-custom-command-depends") +run_codegen("byproducts") + +# Error handling +run_cmake("implicit-depends") +run_cmake("implicit-depends-append-codegen") +run_cmake("append-implicit-depends") +run_cmake("no-output") diff --git a/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake b/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake new file mode 100644 index 000000000..d371d73f8 --- /dev/null +++ b/Tests/RunCMake/Codegen/add-custom-command-depends-build-check.cmake @@ -0,0 +1,5 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/add-custom-command-depends.cmake b/Tests/RunCMake/Codegen/add-custom-command-depends.cmake new file mode 100644 index 000000000..793ab5f88 --- /dev/null +++ b/Tests/RunCMake/Codegen/add-custom-command-depends.cmake @@ -0,0 +1,16 @@ +add_custom_target(foobar + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) + +add_custom_command( + OUTPUT generated.hpp + # This test will fail if DEPENDS isn't accounted for in the codegen build graph + DEPENDS foobar + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h + ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + CODEGEN +) + +add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp) diff --git a/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake b/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake new file mode 100644 index 000000000..d371d73f8 --- /dev/null +++ b/Tests/RunCMake/Codegen/add-dependencies-build-check.cmake @@ -0,0 +1,5 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/add-dependencies.cmake b/Tests/RunCMake/Codegen/add-dependencies.cmake new file mode 100644 index 000000000..fbb7e99cd --- /dev/null +++ b/Tests/RunCMake/Codegen/add-dependencies.cmake @@ -0,0 +1,18 @@ +add_custom_target(foobar + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) + +add_custom_command( + OUTPUT generated.hpp + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h + ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + CODEGEN +) + +add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp) + +# This test will fail if add_dependencies isn't account for in the +# codegen build graph +add_dependencies(hpp_creator foobar) diff --git a/Tests/RunCMake/Codegen/append-implicit-depends-result.txt b/Tests/RunCMake/Codegen/append-implicit-depends-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/Codegen/append-implicit-depends-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt b/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt new file mode 100644 index 000000000..c8ef03eaa --- /dev/null +++ b/Tests/RunCMake/Codegen/append-implicit-depends-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: + Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom command\. diff --git a/Tests/RunCMake/Codegen/append-implicit-depends.cmake b/Tests/RunCMake/Codegen/append-implicit-depends.cmake new file mode 100644 index 000000000..d212fe5f5 --- /dev/null +++ b/Tests/RunCMake/Codegen/append-implicit-depends.cmake @@ -0,0 +1,19 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + CODEGEN +) + +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + + # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN + IMPLICIT_DEPENDS C main.c + + APPEND +) diff --git a/Tests/RunCMake/Codegen/byproducts-build-check.cmake b/Tests/RunCMake/Codegen/byproducts-build-check.cmake new file mode 100644 index 000000000..d371d73f8 --- /dev/null +++ b/Tests/RunCMake/Codegen/byproducts-build-check.cmake @@ -0,0 +1,5 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/generated.hpp") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/byproducts.cmake b/Tests/RunCMake/Codegen/byproducts.cmake new file mode 100644 index 000000000..ea0b6c71f --- /dev/null +++ b/Tests/RunCMake/Codegen/byproducts.cmake @@ -0,0 +1,19 @@ +add_custom_target(foobar + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + BYPRODUCTS + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) + +# This codegen step relies on the BYPRODUCTS of the previous command. +# If foobar isn't properly accounted for as a dependency it will fail. +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated.h + ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + CODEGEN +) + +add_custom_target(hpp_creator ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp) diff --git a/Tests/RunCMake/Codegen/error.c b/Tests/RunCMake/Codegen/error.c new file mode 100644 index 000000000..34cb35082 --- /dev/null +++ b/Tests/RunCMake/Codegen/error.c @@ -0,0 +1 @@ +#error "This file should not be compiled" diff --git a/Tests/RunCMake/Codegen/exclude-from-all.cmake b/Tests/RunCMake/Codegen/exclude-from-all.cmake new file mode 100644 index 000000000..bcd4ac0c5 --- /dev/null +++ b/Tests/RunCMake/Codegen/exclude-from-all.cmake @@ -0,0 +1,11 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + COMMAND + ${CMAKE_COMMAND} -E false + CODEGEN +) + +# We don't want codegen to drive parts of the project that are EXCLUDE_FROM_ALL. +# This tests that foobar is properly excluded from the codegen build. +add_executable(foobar EXCLUDE_FROM_ALL error.c ${CMAKE_CURRENT_BINARY_DIR}/generated.h) diff --git a/Tests/RunCMake/Codegen/generated.h.in b/Tests/RunCMake/Codegen/generated.h.in new file mode 100644 index 000000000..82ccf67ed --- /dev/null +++ b/Tests/RunCMake/Codegen/generated.h.in @@ -0,0 +1 @@ +// hello diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt new file mode 100644 index 000000000..570cf623f --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at implicit-depends-append-codegen\.cmake:[0-9]+ \(add_custom_command\): + add_custom_command CODEGEN may not be used with APPEND\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake b/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake new file mode 100644 index 000000000..76151cc05 --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends-append-codegen.cmake @@ -0,0 +1,18 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + + # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN + IMPLICIT_DEPENDS C main.c +) + +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + CODEGEN + APPEND +) diff --git a/Tests/RunCMake/Codegen/implicit-depends-result.txt b/Tests/RunCMake/Codegen/implicit-depends-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Codegen/implicit-depends-stderr.txt b/Tests/RunCMake/Codegen/implicit-depends-stderr.txt new file mode 100644 index 000000000..b9ea8f40f --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at implicit-depends\.cmake:[0-9]+ \(add_custom_command\): + add_custom_command CODEGEN is not compatible with IMPLICIT_DEPENDS\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/Codegen/implicit-depends.cmake b/Tests/RunCMake/Codegen/implicit-depends.cmake new file mode 100644 index 000000000..011d4b36f --- /dev/null +++ b/Tests/RunCMake/Codegen/implicit-depends.cmake @@ -0,0 +1,11 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c + ${CMAKE_CURRENT_BINARY_DIR}/main.cpp + CODEGEN + # ERROR out if IMPLICIT_DEPENDS is used with CODEGEN + IMPLICIT_DEPENDS C main.c +) diff --git a/Tests/RunCMake/Codegen/main.c b/Tests/RunCMake/Codegen/main.c new file mode 100644 index 000000000..8488f4e58 --- /dev/null +++ b/Tests/RunCMake/Codegen/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake new file mode 100644 index 000000000..32e155751 --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-1-build-check.cmake @@ -0,0 +1,13 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/generated.h") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() + +# foobar should be built since it was needed +# by the code generation +set(filename "${RunCMake_TEST_BINARY_DIR}/foobar.txt") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/min-graph-1.cmake b/Tests/RunCMake/Codegen/min-graph-1.cmake new file mode 100644 index 000000000..ea47b8fb5 --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-1.cmake @@ -0,0 +1,26 @@ +add_executable(foobar main.c) +add_custom_command( + TARGET foobar POST_BUILD + COMMAND ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/foobar.txt +) + +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + COMMAND + # Generate a header file that requires foobar + foobar + CODEGEN +) + +add_library(errorlib + # If this library is built error.c will cause the build to fail + error.c + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) diff --git a/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake new file mode 100644 index 000000000..fab168b6a --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-2-build-check.cmake @@ -0,0 +1,5 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/generated.h") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/min-graph-2.cmake b/Tests/RunCMake/Codegen/min-graph-2.cmake new file mode 100644 index 000000000..277a9014f --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-2.cmake @@ -0,0 +1,18 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h + CODEGEN +) + +# This target should not be built. It has no reason +# to be part of the codegen build graph +add_custom_target(error_custom_target ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated.h + + # Cause the build to fail + COMMAND ${CMAKE_COMMAND} -E false +) diff --git a/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake b/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake new file mode 100644 index 000000000..734777b25 --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-3-build-check.cmake @@ -0,0 +1,5 @@ +set(filename "${RunCMake_TEST_BINARY_DIR}/error_lib.c") +if (NOT EXISTS "${filename}") + set(RunCMake_TEST_FAILED "expected file NOT created:\n ${filename}") + return() +endif() diff --git a/Tests/RunCMake/Codegen/min-graph-3.cmake b/Tests/RunCMake/Codegen/min-graph-3.cmake new file mode 100644 index 000000000..c7d61dcf4 --- /dev/null +++ b/Tests/RunCMake/Codegen/min-graph-3.cmake @@ -0,0 +1,12 @@ +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c + COMMAND + ${CMAKE_COMMAND} -E + copy ${CMAKE_CURRENT_SOURCE_DIR}/error.c + ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c + CODEGEN +) + +# This test will fail if error_lib.c is actually compiled +add_executable(foobar ${CMAKE_CURRENT_BINARY_DIR}/error_lib.c) diff --git a/Tests/RunCMake/Codegen/no-codegen-check.cmake b/Tests/RunCMake/Codegen/no-codegen-check.cmake new file mode 100644 index 000000000..97fc46b39 --- /dev/null +++ b/Tests/RunCMake/Codegen/no-codegen-check.cmake @@ -0,0 +1,5 @@ +# Verify generated.hpp was NOT created +set(unexpected "${RunCMake_TEST_BINARY_DIR}/generated.hpp") +if(EXISTS "${unexpected}") + set(RunCMake_TEST_FAILED "unexpected file created:\n ${unexpected}") +endif() diff --git a/Tests/RunCMake/Codegen/no-codegen.cmake b/Tests/RunCMake/Codegen/no-codegen.cmake new file mode 100644 index 000000000..00ddd033e --- /dev/null +++ b/Tests/RunCMake/Codegen/no-codegen.cmake @@ -0,0 +1,6 @@ +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.hpp +) diff --git a/Tests/RunCMake/Codegen/no-output-result.txt b/Tests/RunCMake/Codegen/no-output-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/Codegen/no-output-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Codegen/no-output-stderr.txt b/Tests/RunCMake/Codegen/no-output-stderr.txt new file mode 100644 index 000000000..7aad6795f --- /dev/null +++ b/Tests/RunCMake/Codegen/no-output-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at no-output\.cmake:[0-9]+ \(add_custom_command\): + add_custom_command CODEGEN requires at least 1 OUTPUT\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/Codegen/no-output.cmake b/Tests/RunCMake/Codegen/no-output.cmake new file mode 100644 index 000000000..61eb83c22 --- /dev/null +++ b/Tests/RunCMake/Codegen/no-output.cmake @@ -0,0 +1,11 @@ +add_custom_target(foobar + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in + ${CMAKE_CURRENT_BINARY_DIR}/generated.h +) + +add_custom_command(TARGET foobar POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E true + CODEGEN +) diff --git a/Tests/RunCMake/CommandLine/CMakeLists.txt b/Tests/RunCMake/CommandLine/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CommandLine/CMakeLists.txt +++ b/Tests/RunCMake/CommandLine/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt deleted file mode 100644 index b69408e2f..000000000 --- a/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt +++ /dev/null @@ -1,5 +0,0 @@ -^CMake Warning: - The "Visual Studio 12 2013" generator is deprecated and will be removed in - a future version of CMake. - - Add CMAKE_WARN_VS12=OFF to the cache to disable this warning.$ diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 7a5a334dc..89e513bab 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) include(RunCMake) @@ -49,6 +49,16 @@ run_cmake_command(E___run_co_compile-no--- ${CMAKE_COMMAND} -E __run_co_compile run_cmake_command(E___run_co_compile-no-cc ${CMAKE_COMMAND} -E __run_co_compile --iwyu=iwyu-does-not-exist --) run_cmake_command(E___run_co_compile-tidy-remove-fixes ${CMAKE_COMMAND} -E __run_co_compile "--tidy=${CMAKE_COMMAND}\\;-E\\;true\\;--export-fixes=${RunCMake_BINARY_DIR}/tidy-fixes.yaml" -- ${CMAKE_COMMAND} -E true) +block() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/list-cache-build) + run_cmake(list-cache) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(list-cache-LR ${CMAKE_COMMAND} . -LR MIDDLE) + run_cmake_command(list-cache-LRA ${CMAKE_COMMAND} . -LRA MIDDLE) + run_cmake_command(list-cache-LRH ${CMAKE_COMMAND} . -LRH MIDDLE) + run_cmake_command(list-cache-LRAH ${CMAKE_COMMAND} . -LRAH MIDDLE) +endblock() + run_cmake_command(G_no-arg ${CMAKE_COMMAND} -B DummyBuildDir -G) run_cmake_command(G_bad-arg ${CMAKE_COMMAND} -B DummyBuildDir -G NoSuchGenerator) run_cmake_command(P_no-arg ${CMAKE_COMMAND} -P) @@ -372,21 +382,18 @@ function(run_EnvironmentGenerator) run_cmake_command(Envgen-bad-help ${CMAKE_COMMAND} --help) unset(ENV{CMAKE_GENERATOR}) - if(RunCMake_GENERATOR MATCHES "Visual Studio.*") + if(RunCMake_GENERATOR MATCHES "Visual Studio") set(ENV{CMAKE_GENERATOR} "${RunCMake_GENERATOR}") run_cmake_command(Envgen ${CMAKE_COMMAND} ${source_dir}) - # Toolset is available since VS 2010. - if(RunCMake_GENERATOR MATCHES "Visual Studio [1-9][0-9]") - set(ENV{CMAKE_GENERATOR_TOOLSET} "invalid") - # Envvar shouldn't affect existing build tree - run_cmake_command(Envgen-toolset-existing ${CMAKE_COMMAND} -E chdir .. - ${CMAKE_COMMAND} --build Envgen-build) - run_cmake_command(Envgen-toolset-invalid ${CMAKE_COMMAND} ${source_dir}) - # Command line -G implies -T"" - run_cmake_command(Envgen-G-implicit-toolset ${CMAKE_COMMAND} -G "${RunCMake_GENERATOR}" ${source_dir}) - run_cmake_command(Envgen-T-toolset ${CMAKE_COMMAND} -T "fromcli" ${source_dir}) - unset(ENV{CMAKE_GENERATOR_TOOLSET}) - endif() + set(ENV{CMAKE_GENERATOR_TOOLSET} "invalid") + # Envvar shouldn't affect existing build tree + run_cmake_command(Envgen-toolset-existing ${CMAKE_COMMAND} -E chdir .. + ${CMAKE_COMMAND} --build Envgen-build) + run_cmake_command(Envgen-toolset-invalid ${CMAKE_COMMAND} ${source_dir}) + # Command line -G implies -T"" + run_cmake_command(Envgen-G-implicit-toolset ${CMAKE_COMMAND} -G "${RunCMake_GENERATOR}" ${source_dir}) + run_cmake_command(Envgen-T-toolset ${CMAKE_COMMAND} -T "fromcli" ${source_dir}) + unset(ENV{CMAKE_GENERATOR_TOOLSET}) # Platform can be set only if not in generator name. if(RunCMake_GENERATOR MATCHES "^Visual Studio [0-9]+ [0-9]+$") set(ENV{CMAKE_GENERATOR_PLATFORM} "invalid") @@ -1120,11 +1127,18 @@ set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${P run_cmake(ProfilingTest) unset(RunCMake_TEST_OPTIONS) -if(RunCMake_GENERATOR MATCHES "^Visual Studio 12 2013") - run_cmake_with_options(DeprecateVS12-WARN-ON -DCMAKE_WARN_VS12=ON) - unset(ENV{CMAKE_WARN_VS12}) - run_cmake(DeprecateVS12-WARN-ON) - run_cmake_with_options(DeprecateVS12-WARN-OFF -DCMAKE_WARN_VS12=OFF) -endif() - run_cmake_with_options(help-arbitrary "--help" "CMAKE_CXX_IGNORE_EXTENSIONS") + +if (WIN32 OR DEFINED ENV{HOME}) + set(config_dir_test print-config-dir) + if (WIN32) + set(config_dir_test print-config-dir-win) + elseif(APPLE) + set(config_dir_test print-config-dir-apple) + endif() + unset(ENV{CMAKE_CONFIG_DIR}) + unset(ENV{XDG_CONFIG_HOME}) + run_cmake_command(${config_dir_test} ${CMAKE_COMMAND} "--print-config-dir") +endif() +set(ENV{CMAKE_CONFIG_DIR} cmake_config_dir) +run_cmake_command(print-config-dir-env ${CMAKE_COMMAND} "--print-config-dir") diff --git a/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt new file mode 100644 index 000000000..9a4e0dbd3 --- /dev/null +++ b/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt @@ -0,0 +1,3 @@ +-- Cache values +MIDDLE_ENTRY_1:STRING=1 +MIDDLE_ENTRY_2:STRING=2$ diff --git a/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt new file mode 100644 index 000000000..a452355e0 --- /dev/null +++ b/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt @@ -0,0 +1,4 @@ +-- Cache values +MIDDLE_ENTRY_1:STRING=1 +MIDDLE_ENTRY_2:STRING=2 +MIDDLE_ENTRY_3:STRING=3$ diff --git a/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt new file mode 100644 index 000000000..cf502c1b1 --- /dev/null +++ b/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt @@ -0,0 +1,9 @@ +-- Cache values +// mid 1 +MIDDLE_ENTRY_1:STRING=1 + +// mid 2 +MIDDLE_ENTRY_2:STRING=2 + +// mid 3 +MIDDLE_ENTRY_3:STRING=3$ diff --git a/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt new file mode 100644 index 000000000..0f6b3a0de --- /dev/null +++ b/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt @@ -0,0 +1,6 @@ +-- Cache values +// mid 1 +MIDDLE_ENTRY_1:STRING=1 + +// mid 2 +MIDDLE_ENTRY_2:STRING=2$ diff --git a/Tests/RunCMake/CommandLine/list-cache.cmake b/Tests/RunCMake/CommandLine/list-cache.cmake new file mode 100644 index 000000000..5617054e5 --- /dev/null +++ b/Tests/RunCMake/CommandLine/list-cache.cmake @@ -0,0 +1,6 @@ +set(EARLY_ENTRY_1 "1" CACHE STRING "early") +set(MIDDLE_ENTRY_1 "1" CACHE STRING "mid 1") +set(MIDDLE_ENTRY_2 "2" CACHE STRING "mid 2") +set(MIDDLE_ENTRY_3 "3" CACHE STRING "mid 3") +mark_as_advanced(MIDDLE_ENTRY_3) +set(LATER_ENTRY_1 "1" CACHE STRING "later") diff --git a/Tests/RunCMake/CommandLine/print-config-dir-apple-stdout.txt b/Tests/RunCMake/CommandLine/print-config-dir-apple-stdout.txt new file mode 100644 index 000000000..59db48205 --- /dev/null +++ b/Tests/RunCMake/CommandLine/print-config-dir-apple-stdout.txt @@ -0,0 +1 @@ +.*Library/Application\\ Support/CMake$ diff --git a/Tests/RunCMake/CommandLine/print-config-dir-env-stdout.txt b/Tests/RunCMake/CommandLine/print-config-dir-env-stdout.txt new file mode 100644 index 000000000..49f1d7b88 --- /dev/null +++ b/Tests/RunCMake/CommandLine/print-config-dir-env-stdout.txt @@ -0,0 +1 @@ +cmake_config_dir diff --git a/Tests/RunCMake/CommandLine/print-config-dir-stdout.txt b/Tests/RunCMake/CommandLine/print-config-dir-stdout.txt new file mode 100644 index 000000000..53f6c8b59 --- /dev/null +++ b/Tests/RunCMake/CommandLine/print-config-dir-stdout.txt @@ -0,0 +1 @@ +.*config/cmake$ diff --git a/Tests/RunCMake/CommandLine/print-config-dir-win-stdout.txt b/Tests/RunCMake/CommandLine/print-config-dir-win-stdout.txt new file mode 100644 index 000000000..9df06711f --- /dev/null +++ b/Tests/RunCMake/CommandLine/print-config-dir-win-stdout.txt @@ -0,0 +1 @@ +.*AppData\\Local\\CMake$ diff --git a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt index b90068660..36f6164cc 100644 --- a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.10 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand NONE \) diff --git a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt index 88aad0069..49588dfac 100644 --- a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.10 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand-warn-uninitialized NONE \) diff --git a/Tests/RunCMake/CommandLine/trace-stderr.txt b/Tests/RunCMake/CommandLine/trace-stderr.txt index 4bf3cffb4..f09725364 100644 --- a/Tests/RunCMake/CommandLine/trace-stderr.txt +++ b/Tests/RunCMake/CommandLine/trace-stderr.txt @@ -1,2 +1,2 @@ -^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \) +^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.10 \) .*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(\${RunCMake_TEST} NONE \) diff --git a/Tests/RunCMake/CommandLineTar/CMakeLists.txt b/Tests/RunCMake/CommandLineTar/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CommandLineTar/CMakeLists.txt +++ b/Tests/RunCMake/CommandLineTar/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt index 12a7fd463..c814f1493 100644 --- a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt +++ b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt +++ b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompileFeatures/CMakeLists.txt b/Tests/RunCMake/CompileFeatures/CMakeLists.txt index 12a7fd463..c814f1493 100644 --- a/Tests/RunCMake/CompileFeatures/CMakeLists.txt +++ b/Tests/RunCMake/CompileFeatures/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerArgs/CMakeLists.txt b/Tests/RunCMake/CompilerArgs/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CompilerArgs/CMakeLists.txt +++ b/Tests/RunCMake/CompilerArgs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerChange/CMakeLists.txt b/Tests/RunCMake/CompilerChange/CMakeLists.txt index b41f3f323..ef3648be7 100644 --- a/Tests/RunCMake/CompilerChange/CMakeLists.txt +++ b/Tests/RunCMake/CompilerChange/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(NOT RunCMake_TEST) set(RunCMake_TEST "$ENV{RunCMake_TEST}") # needed when cache is deleted endif() diff --git a/Tests/RunCMake/CompilerId/C-stdout.txt b/Tests/RunCMake/CompilerId/C-stdout.txt new file mode 100644 index 000000000..e9ce46b0f --- /dev/null +++ b/Tests/RunCMake/CompilerId/C-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_C_COMPILER='[^']+' +-- CMAKE_C_COMPILER_ID='[A-Za-z]+' +-- CMAKE_C_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/C.cmake b/Tests/RunCMake/CompilerId/C.cmake new file mode 100644 index 000000000..3bf943f49 --- /dev/null +++ b/Tests/RunCMake/CompilerId/C.cmake @@ -0,0 +1,4 @@ +enable_language(C) +message(STATUS "CMAKE_C_COMPILER='${CMAKE_C_COMPILER}'") +message(STATUS "CMAKE_C_COMPILER_ID='${CMAKE_C_COMPILER_ID}'") +message(STATUS "CMAKE_C_COMPILER_VERSION='${CMAKE_C_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/CMakeLists.txt b/Tests/RunCMake/CompilerId/CMakeLists.txt new file mode 100644 index 000000000..dda37d8bc --- /dev/null +++ b/Tests/RunCMake/CompilerId/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerId/CSharp-stdout.txt b/Tests/RunCMake/CompilerId/CSharp-stdout.txt new file mode 100644 index 000000000..e36df6ae2 --- /dev/null +++ b/Tests/RunCMake/CompilerId/CSharp-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_CSharp_COMPILER='[^']+' +-- CMAKE_CSharp_COMPILER_ID='[A-Za-z ]+' +-- CMAKE_CSharp_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/CSharp.cmake b/Tests/RunCMake/CompilerId/CSharp.cmake new file mode 100644 index 000000000..2ffd41d88 --- /dev/null +++ b/Tests/RunCMake/CompilerId/CSharp.cmake @@ -0,0 +1,4 @@ +enable_language(CSharp) +message(STATUS "CMAKE_CSharp_COMPILER='${CMAKE_CSharp_COMPILER}'") +message(STATUS "CMAKE_CSharp_COMPILER_ID='${CMAKE_CSharp_COMPILER_ID}'") +message(STATUS "CMAKE_CSharp_COMPILER_VERSION='${CMAKE_CSharp_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/CUDA-stdout.txt b/Tests/RunCMake/CompilerId/CUDA-stdout.txt new file mode 100644 index 000000000..459743f61 --- /dev/null +++ b/Tests/RunCMake/CompilerId/CUDA-stdout.txt @@ -0,0 +1,11 @@ +(-- CMAKE_CUDA_COMPILER='[^']+' +-- CMAKE_CUDA_COMPILER_ID='NVIDIA' +-- CMAKE_CUDA_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +-- CMAKE_CUDA_HOST_COMPILER_ID='[A-Za-z]+' +-- CMAKE_CUDA_HOST_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +|-- CMAKE_CUDA_COMPILER='[^']+' +-- CMAKE_CUDA_COMPILER_ID='([^N]|N[^V]|NV[^I]|NVI[^D]|NVID[^I]|NVIDI[^A])[A-Za-z]*' +-- CMAKE_CUDA_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +-- CMAKE_CUDA_HOST_COMPILER_ID='' +-- CMAKE_CUDA_HOST_COMPILER_VERSION='' +) diff --git a/Tests/RunCMake/CompilerId/CUDA.cmake b/Tests/RunCMake/CompilerId/CUDA.cmake new file mode 100644 index 000000000..874a2ffee --- /dev/null +++ b/Tests/RunCMake/CompilerId/CUDA.cmake @@ -0,0 +1,6 @@ +enable_language(CUDA) +message(STATUS "CMAKE_CUDA_COMPILER='${CMAKE_CUDA_COMPILER}'") +message(STATUS "CMAKE_CUDA_COMPILER_ID='${CMAKE_CUDA_COMPILER_ID}'") +message(STATUS "CMAKE_CUDA_COMPILER_VERSION='${CMAKE_CUDA_COMPILER_VERSION}'") +message(STATUS "CMAKE_CUDA_HOST_COMPILER_ID='${CMAKE_CUDA_HOST_COMPILER_ID}'") +message(STATUS "CMAKE_CUDA_HOST_COMPILER_VERSION='${CMAKE_CUDA_HOST_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/CXX-stdout.txt b/Tests/RunCMake/CompilerId/CXX-stdout.txt new file mode 100644 index 000000000..4062a97a9 --- /dev/null +++ b/Tests/RunCMake/CompilerId/CXX-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_CXX_COMPILER='[^']+' +-- CMAKE_CXX_COMPILER_ID='[A-Za-z]+' +-- CMAKE_CXX_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/CXX.cmake b/Tests/RunCMake/CompilerId/CXX.cmake new file mode 100644 index 000000000..4d8bd0ea8 --- /dev/null +++ b/Tests/RunCMake/CompilerId/CXX.cmake @@ -0,0 +1,4 @@ +enable_language(CXX) +message(STATUS "CMAKE_CXX_COMPILER='${CMAKE_CXX_COMPILER}'") +message(STATUS "CMAKE_CXX_COMPILER_ID='${CMAKE_CXX_COMPILER_ID}'") +message(STATUS "CMAKE_CXX_COMPILER_VERSION='${CMAKE_CXX_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/Fortran-stdout.txt b/Tests/RunCMake/CompilerId/Fortran-stdout.txt new file mode 100644 index 000000000..6a59c668d --- /dev/null +++ b/Tests/RunCMake/CompilerId/Fortran-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_Fortran_COMPILER='[^']+' +-- CMAKE_Fortran_COMPILER_ID='[A-Za-z]+' +-- CMAKE_Fortran_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/Fortran.cmake b/Tests/RunCMake/CompilerId/Fortran.cmake new file mode 100644 index 000000000..d4ed0396a --- /dev/null +++ b/Tests/RunCMake/CompilerId/Fortran.cmake @@ -0,0 +1,4 @@ +enable_language(Fortran) +message(STATUS "CMAKE_Fortran_COMPILER='${CMAKE_Fortran_COMPILER}'") +message(STATUS "CMAKE_Fortran_COMPILER_ID='${CMAKE_Fortran_COMPILER_ID}'") +message(STATUS "CMAKE_Fortran_COMPILER_VERSION='${CMAKE_Fortran_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/HIP-stdout.txt b/Tests/RunCMake/CompilerId/HIP-stdout.txt new file mode 100644 index 000000000..ab779aae4 --- /dev/null +++ b/Tests/RunCMake/CompilerId/HIP-stdout.txt @@ -0,0 +1,11 @@ +(-- CMAKE_HIP_COMPILER='[^']+' +-- CMAKE_HIP_COMPILER_ID='NVIDIA' +-- CMAKE_HIP_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +-- CMAKE_HIP_HOST_COMPILER_ID='[A-Za-z]+' +-- CMAKE_HIP_HOST_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +|-- CMAKE_HIP_COMPILER='[^']+' +-- CMAKE_HIP_COMPILER_ID='([^N]|N[^V]|NV[^I]|NVI[^D]|NVID[^I]|NVIDI[^A])[A-Za-z]*' +-- CMAKE_HIP_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' +-- CMAKE_HIP_HOST_COMPILER_ID='' +-- CMAKE_HIP_HOST_COMPILER_VERSION='' +) diff --git a/Tests/RunCMake/CompilerId/HIP.cmake b/Tests/RunCMake/CompilerId/HIP.cmake new file mode 100644 index 000000000..095c3a8fe --- /dev/null +++ b/Tests/RunCMake/CompilerId/HIP.cmake @@ -0,0 +1,6 @@ +enable_language(HIP) +message(STATUS "CMAKE_HIP_COMPILER='${CMAKE_HIP_COMPILER}'") +message(STATUS "CMAKE_HIP_COMPILER_ID='${CMAKE_HIP_COMPILER_ID}'") +message(STATUS "CMAKE_HIP_COMPILER_VERSION='${CMAKE_HIP_COMPILER_VERSION}'") +message(STATUS "CMAKE_HIP_HOST_COMPILER_ID='${CMAKE_HIP_HOST_COMPILER_ID}'") +message(STATUS "CMAKE_HIP_HOST_COMPILER_VERSION='${CMAKE_HIP_HOST_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/ISPC-stdout.txt b/Tests/RunCMake/CompilerId/ISPC-stdout.txt new file mode 100644 index 000000000..c45ea5a93 --- /dev/null +++ b/Tests/RunCMake/CompilerId/ISPC-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_ISPC_COMPILER='[^']+' +-- CMAKE_ISPC_COMPILER_ID='[A-Za-z]+' +-- CMAKE_ISPC_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/ISPC.cmake b/Tests/RunCMake/CompilerId/ISPC.cmake new file mode 100644 index 000000000..a022db4d4 --- /dev/null +++ b/Tests/RunCMake/CompilerId/ISPC.cmake @@ -0,0 +1,4 @@ +enable_language(ISPC) +message(STATUS "CMAKE_ISPC_COMPILER='${CMAKE_ISPC_COMPILER}'") +message(STATUS "CMAKE_ISPC_COMPILER_ID='${CMAKE_ISPC_COMPILER_ID}'") +message(STATUS "CMAKE_ISPC_COMPILER_VERSION='${CMAKE_ISPC_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/OBJC-stdout.txt b/Tests/RunCMake/CompilerId/OBJC-stdout.txt new file mode 100644 index 000000000..528675aff --- /dev/null +++ b/Tests/RunCMake/CompilerId/OBJC-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_OBJC_COMPILER='[^']+' +-- CMAKE_OBJC_COMPILER_ID='[A-Za-z]+' +-- CMAKE_OBJC_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/OBJC.cmake b/Tests/RunCMake/CompilerId/OBJC.cmake new file mode 100644 index 000000000..5ae4681c9 --- /dev/null +++ b/Tests/RunCMake/CompilerId/OBJC.cmake @@ -0,0 +1,4 @@ +enable_language(OBJC) +message(STATUS "CMAKE_OBJC_COMPILER='${CMAKE_OBJC_COMPILER}'") +message(STATUS "CMAKE_OBJC_COMPILER_ID='${CMAKE_OBJC_COMPILER_ID}'") +message(STATUS "CMAKE_OBJC_COMPILER_VERSION='${CMAKE_OBJC_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/OBJCXX-stdout.txt b/Tests/RunCMake/CompilerId/OBJCXX-stdout.txt new file mode 100644 index 000000000..9d7f822a1 --- /dev/null +++ b/Tests/RunCMake/CompilerId/OBJCXX-stdout.txt @@ -0,0 +1,3 @@ +-- CMAKE_OBJCXX_COMPILER='[^']+' +-- CMAKE_OBJCXX_COMPILER_ID='[A-Za-z]+' +-- CMAKE_OBJCXX_COMPILER_VERSION='([0-9]+)(\.([0-9]+))?(\.([0-9]+))?(\.([0-9]+))?' diff --git a/Tests/RunCMake/CompilerId/OBJCXX.cmake b/Tests/RunCMake/CompilerId/OBJCXX.cmake new file mode 100644 index 000000000..0e1875bdc --- /dev/null +++ b/Tests/RunCMake/CompilerId/OBJCXX.cmake @@ -0,0 +1,4 @@ +enable_language(OBJCXX) +message(STATUS "CMAKE_OBJCXX_COMPILER='${CMAKE_OBJCXX_COMPILER}'") +message(STATUS "CMAKE_OBJCXX_COMPILER_ID='${CMAKE_OBJCXX_COMPILER_ID}'") +message(STATUS "CMAKE_OBJCXX_COMPILER_VERSION='${CMAKE_OBJCXX_COMPILER_VERSION}'") diff --git a/Tests/RunCMake/CompilerId/RunCMakeTest.cmake b/Tests/RunCMake/CompilerId/RunCMakeTest.cmake new file mode 100644 index 000000000..64e397b8d --- /dev/null +++ b/Tests/RunCMake/CompilerId/RunCMakeTest.cmake @@ -0,0 +1,29 @@ +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() + +if(RunCMake_GENERATOR MATCHES "Visual Studio") + run_cmake(CSharp) +endif() diff --git a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt +++ b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CompilerNotFound/CMakeLists.txt b/Tests/RunCMake/CompilerNotFound/CMakeLists.txt index 74b3ff8de..bf2ef1506 100644 --- a/Tests/RunCMake/CompilerNotFound/CMakeLists.txt +++ b/Tests/RunCMake/CompilerNotFound/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ConfigDir/CMakeLists.txt b/Tests/RunCMake/ConfigDir/CMakeLists.txt new file mode 100644 index 000000000..dda37d8bc --- /dev/null +++ b/Tests/RunCMake/ConfigDir/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ConfigDir/RunCMakeTest.cmake b/Tests/RunCMake/ConfigDir/RunCMakeTest.cmake new file mode 100644 index 000000000..e5df20890 --- /dev/null +++ b/Tests/RunCMake/ConfigDir/RunCMakeTest.cmake @@ -0,0 +1,6 @@ +include(RunCMake) + +set(ENV{CMAKE_CONFIG_DIR} ${CMAKE_CURRENT_LIST_DIR}/config) +set(RunCMake-check-file check-reply.cmake) +run_cmake(config) +unset(RunCMake-check-file) diff --git a/Tests/RunCMake/ConfigDir/check-reply.cmake b/Tests/RunCMake/ConfigDir/check-reply.cmake new file mode 100644 index 000000000..6e0ecf9dd --- /dev/null +++ b/Tests/RunCMake/ConfigDir/check-reply.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply) + set(RunCMake_TEST_FAILED "Failed to read FileAPI query from user config directory") +endif() diff --git a/Tests/RunCMake/ConfigDir/config.cmake b/Tests/RunCMake/ConfigDir/config.cmake new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/ConfigDir/config/api/v1/query/codemodel-v2 b/Tests/RunCMake/ConfigDir/config/api/v1/query/codemodel-v2 new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/Configure/CMakeLists.txt b/Tests/RunCMake/Configure/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/Configure/CMakeLists.txt +++ b/Tests/RunCMake/Configure/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Cppcheck/CMakeLists.txt b/Tests/RunCMake/Cppcheck/CMakeLists.txt index a640c569e..bf2ef1506 100644 --- a/Tests/RunCMake/Cppcheck/CMakeLists.txt +++ b/Tests/RunCMake/Cppcheck/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Cpplint/CMakeLists.txt b/Tests/RunCMake/Cpplint/CMakeLists.txt index a640c569e..bf2ef1506 100644 --- a/Tests/RunCMake/Cpplint/CMakeLists.txt +++ b/Tests/RunCMake/Cpplint/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt +++ b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake index f7959dcb1..1981fce7c 100644 --- a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake +++ b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake @@ -60,7 +60,7 @@ run_cmake(Property) run_optimize_test(OptimizeShared SharedTop) run_optimize_test(OptimizeStatic StaticTop) -if(CMAKE_Fortran_COMPILER) +if(CMake_TEST_Fortran) run_optimize_test(OptimizeFortran FortranTop) endif() diff --git a/Tests/RunCMake/DependencyGraph/mylib.f90 b/Tests/RunCMake/DependencyGraph/mylib.f90 index 104768f89..d3fac7297 100644 --- a/Tests/RunCMake/DependencyGraph/mylib.f90 +++ b/Tests/RunCMake/DependencyGraph/mylib.f90 @@ -1,3 +1,4 @@ function mylib_fortran() + integer :: mylib_fortran mylib_fortran = 42 end function mylib_fortran diff --git a/Tests/RunCMake/DisallowedCommands/CMakeLists.txt b/Tests/RunCMake/DisallowedCommands/CMakeLists.txt index 4b3de84d9..927c9b4af 100644 --- a/Tests/RunCMake/DisallowedCommands/CMakeLists.txt +++ b/Tests/RunCMake/DisallowedCommands/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 2.8.12) # old enough to not set CMP0029 through CMP0036 project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt +++ b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExportWithoutLanguage/CMakeLists.txt b/Tests/RunCMake/ExportWithoutLanguage/CMakeLists.txt index 74b3ff8de..bf2ef1506 100644 --- a/Tests/RunCMake/ExportWithoutLanguage/CMakeLists.txt +++ b/Tests/RunCMake/ExportWithoutLanguage/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExternalData/CMakeLists.txt b/Tests/RunCMake/ExternalData/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/ExternalData/CMakeLists.txt +++ b/Tests/RunCMake/ExternalData/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExtraGenerators/CMakeLists.txt b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/ExtraGenerators/CMakeLists.txt +++ b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FPHSA/CMakeLists.txt b/Tests/RunCMake/FPHSA/CMakeLists.txt index dc3425904..2835ff784 100644 --- a/Tests/RunCMake/FPHSA/CMakeLists.txt +++ b/Tests/RunCMake/FPHSA/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(RunCMake_TEST MATCHES "^BeforeProject") include(${RunCMake_TEST}.cmake) project(${RunCMake_TEST} NONE) diff --git a/Tests/RunCMake/FeatureSummary/CMakeLists.txt b/Tests/RunCMake/FeatureSummary/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/FeatureSummary/CMakeLists.txt +++ b/Tests/RunCMake/FeatureSummary/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FetchContent/CMakeLists.txt b/Tests/RunCMake/FetchContent/CMakeLists.txt index 4f2de6c92..d5002e552 100644 --- a/Tests/RunCMake/FetchContent/CMakeLists.txt +++ b/Tests/RunCMake/FetchContent/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) if(CMP0168 STREQUAL "NEW") diff --git a/Tests/RunCMake/File_Archive/CMakeLists.txt b/Tests/RunCMake/File_Archive/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/File_Archive/CMakeLists.txt +++ b/Tests/RunCMake/File_Archive/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake index dad0dd36c..c50476c55 100644 --- a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake +++ b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake @@ -10,6 +10,8 @@ run_cmake(paxr) run_cmake(paxr-bz2) run_cmake(zip) +run_cmake(working-directory) + # Extracting only selected files or directories run_cmake(zip-filtered) diff --git a/Tests/RunCMake/File_Archive/roundtrip.cmake b/Tests/RunCMake/File_Archive/roundtrip.cmake index 404925663..bb9b25b34 100644 --- a/Tests/RunCMake/File_Archive/roundtrip.cmake +++ b/Tests/RunCMake/File_Archive/roundtrip.cmake @@ -39,8 +39,9 @@ file(ARCHIVE_CREATE OUTPUT ${FULL_OUTPUT_NAME} FORMAT "${ARCHIVE_FORMAT}" COMPRESSION "${COMPRESSION_TYPE}" + WORKING_DIRECTORY "${WORKING_DIRECTORY}" VERBOSE - PATHS ${COMPRESS_DIR}) + PATHS ${FULL_COMPRESS_DIR}) file(ARCHIVE_EXTRACT INPUT ${FULL_OUTPUT_NAME} @@ -54,10 +55,10 @@ endif() foreach(file ${CHECK_FILES}) set(input ${FULL_COMPRESS_DIR}/${file}) - set(output ${FULL_DECOMPRESS_DIR}/${COMPRESS_DIR}/${file}) + set(output ${FULL_DECOMPRESS_DIR}/${CUSTOM_OUTPUT_DIRECTORY}/${COMPRESS_DIR}/${file}) if(NOT EXISTS ${input}) - message(SEND_ERROR "Cannot find input file ${output}") + message(SEND_ERROR "Cannot find input file ${input}") endif() if(NOT EXISTS ${output}) diff --git a/Tests/RunCMake/File_Archive/working-directory.cmake b/Tests/RunCMake/File_Archive/working-directory.cmake new file mode 100644 index 000000000..18efd343f --- /dev/null +++ b/Tests/RunCMake/File_Archive/working-directory.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME "test.zip") + +set(ARCHIVE_FORMAT zip) + +get_filename_component(CURRENT_FILE_NAME ${CMAKE_CURRENT_LIST_FILE} NAME_WE) +set(CUSTOM_OUTPUT_DIRECTORY "${CURRENT_FILE_NAME}-build") + +set(WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../") + +include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake) + +check_magic("504b0304" LIMIT 4 HEX) diff --git a/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt index bb578e578..185fa5dff 100644 --- a/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt +++ b/Tests/RunCMake/File_Generate/CMP0070-OLD-stderr.txt @@ -1,4 +1,11 @@ -^CMake Deprecation Warning at CMP0070-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMP0070-OLD.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0070 will be removed from a future version of CMake. diff --git a/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt b/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt index a7a231b12..5f8144aff 100644 --- a/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt +++ b/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt @@ -1,4 +1,11 @@ -^(CMake Warning \(dev\) in CMakeLists.txt: +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +(CMake Warning \(dev\) in CMakeLists.txt: Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative paths. Run "cmake --help-policy CMP0070" for policy details. Use the cmake_policy command to set the policy and suppress this warning. diff --git a/Tests/RunCMake/File_Generate/CMakeLists.txt b/Tests/RunCMake/File_Generate/CMakeLists.txt index eec672f8e..a119dd213 100644 --- a/Tests/RunCMake/File_Generate/CMakeLists.txt +++ b/Tests/RunCMake/File_Generate/CMakeLists.txt @@ -1,4 +1,7 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) +if(RunCMake_TEST MATCHES "CMP0070-(WARN|OLD)") + cmake_policy(VERSION 3.9) # old enough to not set CMP0070 +endif() project(${RunCMake_TEST} NONE) if(NOT TEST_FILE) set(TEST_FILE ${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt index 39735d717..37cc35e45 100644 --- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt +++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt @@ -1,4 +1,11 @@ -^CMake Deprecation Warning at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0070 will be removed from a future version of CMake. diff --git a/Tests/RunCMake/FindBoost/CMakeLists.txt b/Tests/RunCMake/FindBoost/CMakeLists.txt index fe9e3ef92..191464824 100644 --- a/Tests/RunCMake/FindBoost/CMakeLists.txt +++ b/Tests/RunCMake/FindBoost/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0167 OLD) # This test covers FindBoost project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindGTK2/CMakeLists.txt b/Tests/RunCMake/FindGTK2/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/FindGTK2/CMakeLists.txt +++ b/Tests/RunCMake/FindGTK2/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindLua/CMakeLists.txt b/Tests/RunCMake/FindLua/CMakeLists.txt index e6c41a5cb..d1b0d2c3e 100644 --- a/Tests/RunCMake/FindLua/CMakeLists.txt +++ b/Tests/RunCMake/FindLua/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindMatlab/CMakeLists.txt b/Tests/RunCMake/FindMatlab/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/FindMatlab/CMakeLists.txt +++ b/Tests/RunCMake/FindMatlab/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt +++ b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Framework/CMakeLists.txt b/Tests/RunCMake/Framework/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/Framework/CMakeLists.txt +++ b/Tests/RunCMake/Framework/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Framework/FrameworkLayout-check-common.cmake b/Tests/RunCMake/Framework/FrameworkLayout-check-common.cmake new file mode 100644 index 000000000..13a91df24 --- /dev/null +++ b/Tests/RunCMake/Framework/FrameworkLayout-check-common.cmake @@ -0,0 +1,24 @@ +macro(check_plist key expect) + execute_process( + COMMAND plutil -extract "${key}" xml1 "${plist-file}" -o - + RESULT_VARIABLE result + OUTPUT_VARIABLE actual + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(actual MATCHES "([^<>]*)") + set(actual "${CMAKE_MATCH_1}") + endif() + if(NOT "${actual}" STREQUAL "${expect}") + string(CONCAT RunCMake_TEST_FAILED + "Framework Info.plist key \"${key}\" has value:\n" + " \"${actual}\"\n" + "but we expected:\n" + " \"${expect}\"" + ) + endif() +endmacro() + +check_plist(CFBundleIdentifier MyFrameworkId) +check_plist(CFBundleName MyFrameworkBundleName) +check_plist(CFBundleVersion 3.2.1) +check_plist(CFBundleShortVersionString 3) diff --git a/Tests/RunCMake/Framework/FrameworkLayout.cmake b/Tests/RunCMake/Framework/FrameworkLayout.cmake index d09e8a029..e230e073e 100644 --- a/Tests/RunCMake/Framework/FrameworkLayout.cmake +++ b/Tests/RunCMake/Framework/FrameworkLayout.cmake @@ -15,6 +15,10 @@ if("${CMAKE_FRAMEWORK}" STREQUAL "") FRAMEWORK TRUE) endif() set_target_properties(Framework PROPERTIES + MACOSX_FRAMEWORK_BUNDLE_NAME MyFrameworkBundleName + MACOSX_FRAMEWORK_BUNDLE_VERSION 3.2.1 + MACOSX_FRAMEWORK_SHORT_VERSION_STRING 3 + MACOSX_FRAMEWORK_IDENTIFIER MyFrameworkId PUBLIC_HEADER foo.h RESOURCE "res.txt") set_source_files_properties(flatresource.txt PROPERTIES MACOSX_PACKAGE_LOCATION Resources) diff --git a/Tests/RunCMake/Framework/FrameworkMultiConfigPostfix-build-final-check.cmake b/Tests/RunCMake/Framework/FrameworkMultiConfigPostfix-build-final-check.cmake index 76fe6b88f..7a1d70ed1 100644 --- a/Tests/RunCMake/Framework/FrameworkMultiConfigPostfix-build-final-check.cmake +++ b/Tests/RunCMake/Framework/FrameworkMultiConfigPostfix-build-final-check.cmake @@ -20,26 +20,32 @@ else() endif() if(NOT IS_DIRECTORY ${framework_dir}) - message(SEND_ERROR "Framework dir not found at ${framework_dir}") + set(RunCMake_TEST_FAILED "Framework dir not found at \n ${framework_dir}") + return() endif() if(IS_DIRECTORY ${non_existent_debug_framework_dir}) - message(SEND_ERROR - "A framework dir with a debug suffix should not exist at ${non_existent_debug_framework_dir}") + set(RunCMake_TEST_FAILED + "A framework dir with a debug suffix should not exist at \n ${non_existent_debug_framework_dir}") + return() endif() if(NOT IS_SYMLINK "${symlink_release_path}") - message(SEND_ERROR "Release framework symlink not found at ${symlink_release_path}") + set(RunCMake_TEST_FAILED "Release framework symlink not found at \n ${symlink_release_path}") + return() endif() if(NOT IS_SYMLINK "${symlink_debug_path}") - message(SEND_ERROR "Debug framework symlink not found at ${symlink_debug_path}") + set(RunCMake_TEST_FAILED "Debug framework symlink not found at \n ${symlink_debug_path}") + return() endif() if(NOT EXISTS "${framework_release_path}") - message(SEND_ERROR "Release framework not found at ${framework_release_path}") + set(RunCMake_TEST_FAILED "Release framework not found at \n ${framework_release_path}") + return() endif() if(NOT EXISTS "${framework_debug_path}") - message(SEND_ERROR "Debug framework not found at ${framework_debug_path}") + set(RunCMake_TEST_FAILED "Debug framework not found at \n ${framework_debug_path}") + return() endif() diff --git a/Tests/RunCMake/Framework/OSXFrameworkLayout-build-check.cmake b/Tests/RunCMake/Framework/OSXFrameworkLayout-build-check.cmake index eb71394a9..b436128e3 100644 --- a/Tests/RunCMake/Framework/OSXFrameworkLayout-build-check.cmake +++ b/Tests/RunCMake/Framework/OSXFrameworkLayout-build-check.cmake @@ -10,41 +10,53 @@ set(plist-file "${framework-resources}/Info.plist") set(framework-header "${framework-dir}/Headers/foo.h") if(NOT IS_DIRECTORY ${framework-dir}) - message(SEND_ERROR "Framework not found at ${framework-dir}") + set(RunCMake_TEST_FAILED "Framework not found at \n ${framework-dir}") + return() endif() if(NOT EXISTS ${plist-file}) - message(SEND_ERROR "plist file not found at ${plist-file}") + set(RunCMake_TEST_FAILED "plist file not found at \n ${plist-file}") + return() endif() if(NOT EXISTS ${framework-library}) - message(SEND_ERROR "Framework library not found at ${framework-library}") + set(RunCMake_TEST_FAILED "Framework library not found at \n ${framework-library}") + return() endif() if(NOT EXISTS ${framework-resource-file}) - message(SEND_ERROR "Framework resource file not found at ${framework-resource-file}") + set(RunCMake_TEST_FAILED "Framework resource file not found at \n ${framework-resource-file}") + return() endif() if(NOT EXISTS ${framework-flat-resource-file}) - message(SEND_ERROR "Framework flat resource file not found at ${framework-flat-resource-file}") + set(RunCMake_TEST_FAILED "Framework flat resource file not found at \n ${framework-flat-resource-file}") + return() endif() if(NOT EXISTS ${framework-deep-resource-file}) - message(SEND_ERROR "Framework deep resource file not found at ${framework-deep-resource-file}") + set(RunCMake_TEST_FAILED "Framework deep resource file not found at \n ${framework-deep-resource-file}") + return() endif() if(NOT EXISTS ${framework-some-file}) - message(SEND_ERROR "Framework some file not found at ${framework-some-file}") + set(RunCMake_TEST_FAILED "Framework some file not found at \n ${framework-some-file}") + return() endif() if(NOT EXISTS ${framework-versions}) - message(SEND_ERROR "Framework versions not found at ${framework-versions}") + set(RunCMake_TEST_FAILED "Framework versions not found at \n ${framework-versions}") + return() endif() if(NOT EXISTS ${framework-resources}) - message(SEND_ERROR "Framework Resources not found at ${framework-resources}") + set(RunCMake_TEST_FAILED "Framework Resources not found at \n ${framework-resources}") + return() endif() if(NOT EXISTS ${framework-header}) - message(SEND_ERROR "Framework header file not found at ${framework-header}") + set(RunCMake_TEST_FAILED "Framework header file not found at \n ${framework-header}") + return() endif() + +include(${CMAKE_CURRENT_LIST_DIR}/FrameworkLayout-check-common.cmake) diff --git a/Tests/RunCMake/Framework/iOSFrameworkLayout-build-check.cmake b/Tests/RunCMake/Framework/iOSFrameworkLayout-build-check.cmake index 2da60d2a5..be9a5fe1a 100644 --- a/Tests/RunCMake/Framework/iOSFrameworkLayout-build-check.cmake +++ b/Tests/RunCMake/Framework/iOSFrameworkLayout-build-check.cmake @@ -10,41 +10,53 @@ set(plist-file "${framework-dir}/Info.plist") set(framework-header "${framework-dir}/Headers/foo.h") if(NOT IS_DIRECTORY ${framework-dir}) - message(SEND_ERROR "Framework not found at ${framework-dir}") + set(RunCMake_TEST_FAILED "Framework not found at\n ${framework-dir}") + return() endif() if(NOT EXISTS ${plist-file}) - message(SEND_ERROR "plist file not found at ${plist-file}") + set(RunCMake_TEST_FAILED "plist file not found at\n ${plist-file}") + return() endif() if(NOT EXISTS ${framework-library}) - message(SEND_ERROR "Framework library not found at ${framework-library}") + set(RunCMake_TEST_FAILED "Framework library not found at\n ${framework-library}") + return() endif() if(NOT EXISTS ${framework-resource-file}) - message(SEND_ERROR "Framework resource file not found at ${framework-resource-file}") + set(RunCMake_TEST_FAILED "Framework resource file not found at\n ${framework-resource-file}") + return() endif() if(NOT EXISTS ${framework-flat-resource-file}) - message(SEND_ERROR "Framework flat resource file not found at ${framework-flat-resource-file}") + set(RunCMake_TEST_FAILED "Framework flat resource file not found at\n ${framework-flat-resource-file}") + return() endif() if(NOT EXISTS ${framework-deep-resource-file}) - message(SEND_ERROR "Framework deep resource file not found at ${framework-deep-resource-file}") + set(RunCMake_TEST_FAILED "Framework deep resource file not found at\n ${framework-deep-resource-file}") + return() endif() if(NOT EXISTS ${framework-some-file}) - message(SEND_ERROR "Framework some file not found at ${framework-some-file}") + set(RunCMake_TEST_FAILED "Framework some file not found at\n ${framework-some-file}") + return() endif() if(EXISTS ${framework-versions}) - message(SEND_ERROR "Framework versions found at ${framework-versions}") + set(RunCMake_TEST_FAILED "Framework versions found at\n ${framework-versions}") + return() endif() if(EXISTS ${framework-resources}) - message(SEND_ERROR "Framework Resources found at ${framework-resources}") + set(RunCMake_TEST_FAILED "Framework Resources found at\n ${framework-resources}") + return() endif() if(NOT EXISTS ${framework-header}) - message(SEND_ERROR "Framework headers not found at ${framework-header}") + set(RunCMake_TEST_FAILED "Framework headers not found at\n ${framework-header}") + return() endif() + +include(${CMAKE_CURRENT_LIST_DIR}/FrameworkLayout-check-common.cmake) diff --git a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt +++ b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt +++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt index fab2ce2d1..79bc554fb 100644 --- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt +++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/GenerateExportHeader/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/CMakeLists.txt index dc9248697..bf2ef1506 100644 --- a/Tests/RunCMake/GenerateExportHeader/CMakeLists.txt +++ b/Tests/RunCMake/GenerateExportHeader/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GenerateExportHeader/nodeprecated/CMakeLists.txt.in b/Tests/RunCMake/GenerateExportHeader/nodeprecated/CMakeLists.txt.in index 90cfa12c2..c2990bdbf 100644 --- a/Tests/RunCMake/GenerateExportHeader/nodeprecated/CMakeLists.txt.in +++ b/Tests/RunCMake/GenerateExportHeader/nodeprecated/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(nodeprecated_test) diff --git a/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt index 86d3e0472..4e05f3e38 100644 --- a/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): \$ expression requires at least one parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadAND.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -14,7 +14,7 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadAND.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -23,7 +23,7 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadAND.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -32,7 +32,7 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadAND.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -41,7 +41,7 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadAND.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -50,6 +50,6 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt index b61ccd2fe..c90db69b0 100644 --- a/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadCONFIG.cmake:1 \(add_custom_target\): Expression syntax not recognized. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadCONFIG.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -14,7 +14,7 @@ CMake Error at BadCONFIG.cmake:1 \(add_custom_target\): Expression syntax not recognized. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadCONFIG.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -23,7 +23,7 @@ CMake Error at BadCONFIG.cmake:1 \(add_custom_target\): Expression syntax not recognized. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Warning at BadCONFIG.cmake:1 \(add_custom_target\): Warning evaluating generator expression: @@ -32,6 +32,6 @@ CMake Warning at BadCONFIG.cmake:1 \(add_custom_target\): The config name of "Foo-Second" is invalid Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadIF-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadIF-stderr.txt index 7c7506c7a..957c54f65 100644 --- a/Tests/RunCMake/GeneratorExpression/BadIF-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadIF-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadIF.cmake:2 \(add_custom_target\): First parameter to \$ must resolve to exactly one '0' or '1' value. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadIF.cmake:2 \(add_custom_target\): Error evaluating generator expression: diff --git a/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt index 271eb6e6f..114d786c8 100644 --- a/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadInstallPrefix-stderr.txt @@ -6,4 +6,4 @@ CMake Error at BadInstallPrefix.cmake:1 \(add_custom_target\): INSTALL_PREFIX is a marker for install\(EXPORT\) only. It should never be evaluated. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt index 627327c12..66e7692c9 100644 --- a/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\): \$ expression requires exactly one parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) +CMake Error at BadNOT.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -13,7 +13,7 @@ Call Stack \(most recent call first\): \$ parameter must resolve to exactly one '0' or '1' value. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadNOT.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -22,7 +22,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\): \$ expression requires exactly one parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadNOT.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -31,7 +31,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\): \$ expression requires exactly one parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadNOT.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -40,7 +40,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\): \$ parameter must resolve to exactly one '0' or '1' value. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadNOT.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -49,6 +49,6 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\): \$ parameter must resolve to exactly one '0' or '1' value. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt index 56e6af00f..899a52f6e 100644 --- a/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): \$ expression requires at least one parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadOR.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -14,7 +14,7 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadOR.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -23,7 +23,7 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadOR.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -32,7 +32,7 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadOR.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -41,7 +41,7 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadOR.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -50,6 +50,6 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\): Parameters to \$ must resolve to either '0' or '1'. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt index a08e7b236..ceb32854d 100644 --- a/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadSHELL_PATH-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\): "" is not an absolute path. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\): Error evaluating generator expression: @@ -14,7 +14,7 @@ CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\): "Relative/Path" is not an absolute path. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\): Error evaluating generator expression: @@ -23,4 +23,4 @@ CMake Error at BadSHELL_PATH.cmake:[0-9]+ \(add_custom_target\): "" is not an absolute path. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/BadStrEqual-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadStrEqual-stderr.txt index 2f04c78f0..7f5e9342c 100644 --- a/Tests/RunCMake/GeneratorExpression/BadStrEqual-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadStrEqual-stderr.txt @@ -6,7 +6,7 @@ CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): \$ expression requires 2 comma separated parameters, but got 0 instead. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) +CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -15,7 +15,7 @@ Call Stack \(most recent call first\): \$ expression requires 2 comma separated parameters, but got 1 instead. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -25,7 +25,7 @@ CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): \$ expression requires 2 comma separated parameters, but got 3 instead. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): Error evaluating generator expression: @@ -35,6 +35,6 @@ CMake Error at BadStrEqual.cmake:1 \(add_custom_target\): \$ expression requires 2 comma separated parameters, but got 3 instead. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt index 98eed1f0a..2504e2a4d 100644 --- a/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadTargetName-stderr.txt @@ -5,6 +5,6 @@ CMake Error at BadTargetName.cmake:1 \(add_custom_target\): \$ expression requires literal input. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Generate step failed\. Build files cannot be regenerated correctly\.$ diff --git a/Tests/RunCMake/GeneratorExpression/BadTargetTypeObject-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadTargetTypeObject-stderr.txt index a597d79b6..9bbfc6914 100644 --- a/Tests/RunCMake/GeneratorExpression/BadTargetTypeObject-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadTargetTypeObject-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadTargetTypeObject.cmake:2 \(add_custom_target\): Target "objlib" is not an executable or library. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadTargetTypeObject.cmake:2 \(add_custom_target\): Error evaluating generator expression: @@ -14,7 +14,7 @@ CMake Error at BadTargetTypeObject.cmake:2 \(add_custom_target\): Target "objlib" is not an executable or library. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadTargetTypeObject.cmake:2 \(add_custom_target\): Error evaluating generator expression: @@ -23,4 +23,4 @@ CMake Error at BadTargetTypeObject.cmake:2 \(add_custom_target\): Target "objlib" is not an executable or library. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/BadZero-stderr.txt b/Tests/RunCMake/GeneratorExpression/BadZero-stderr.txt index 40db4ae88..898379587 100644 --- a/Tests/RunCMake/GeneratorExpression/BadZero-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/BadZero-stderr.txt @@ -5,7 +5,7 @@ CMake Error at BadZero.cmake:2 \(add_custom_target\): \$<0> expression requires a parameter. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) + CMake Error at BadZero.cmake:2 \(add_custom_target\): Error evaluating generator expression: @@ -14,4 +14,4 @@ CMake Error at BadZero.cmake:2 \(add_custom_target\): Expression did not evaluate to a known generator expression Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/CMP0044-WARN-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0044-WARN-stderr.txt index 2079c125c..074cba089 100644 --- a/Tests/RunCMake/GeneratorExpression/CMP0044-WARN-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/CMP0044-WARN-stderr.txt @@ -3,5 +3,5 @@ CMake Warning \(dev\) at CMP0044-WARN.cmake:13 \(target_compile_definitions\): expressions. Run "cmake --help-policy CMP0044" for policy details. Use the cmake_policy command to set the policy and suppress this warning. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/GeneratorExpression/CMakeLists.txt b/Tests/RunCMake/GeneratorExpression/CMakeLists.txt index 4b3de84d9..bd7fdd1b0 100644 --- a/Tests/RunCMake/GeneratorExpression/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorExpression/CMakeLists.txt @@ -1,3 +1,6 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.10) +if(RunCMake_TEST STREQUAL "CMP0044-WARN") + cmake_policy(VERSION 2.8.11) # old enough to not set CMP0044 +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt index 8c93d59ac..9fc830757 100644 --- a/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/COMPILE_ONLY-not-compiling-stderr.txt @@ -5,4 +5,4 @@ CMake Error at COMPILE_ONLY-not-compiling.cmake:1 \(add_custom_target\): \$ may only be used for linking Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/FILTER-InvalidOperator-stderr.txt b/Tests/RunCMake/GeneratorExpression/FILTER-InvalidOperator-stderr.txt index dd1092556..6c57e4378 100644 --- a/Tests/RunCMake/GeneratorExpression/FILTER-InvalidOperator-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/FILTER-InvalidOperator-stderr.txt @@ -5,4 +5,4 @@ CMake Error at FILTER-InvalidOperator.cmake:3 \(file\): \$ second parameter must be either INCLUDE or EXCLUDE Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/LINK_ONLY-not-linking-stderr.txt b/Tests/RunCMake/GeneratorExpression/LINK_ONLY-not-linking-stderr.txt index cded13018..0902c51d4 100644 --- a/Tests/RunCMake/GeneratorExpression/LINK_ONLY-not-linking-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/LINK_ONLY-not-linking-stderr.txt @@ -5,4 +5,4 @@ CMake Error at LINK_ONLY-not-linking.cmake:1 \(add_custom_target\): \$ may only be used for linking Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt index 4cf594523..a6186a94f 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-CXX_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_custom \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_ID-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_ID-stderr.txt index 0c21aa682..2112ab220 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_ID-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_ID-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-CXX_COMPILER_ID.cmake:1 \(add_custom_command\): \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_VERSION-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_VERSION-stderr.txt index fae1bd225..dc9ad221b 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_VERSION-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-CXX_COMPILER_VERSION-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-CXX_COMPILER_VERSION.cmake:1 \(add_custom_command\ \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt index c7e43a383..1bfb1bebb 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_FRONTEND_VARIANT-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-C_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_custom_c \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_ID-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_ID-stderr.txt index 337b2996e..b38eb0c71 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_ID-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_ID-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-C_COMPILER_ID.cmake:1 \(add_custom_command\): \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_VERSION-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_VERSION-stderr.txt index d8548b515..283b5c494 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_VERSION-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-C_COMPILER_VERSION-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-C_COMPILER_VERSION.cmake:1 \(add_custom_command\): \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt index 8641de98f..4ae95ae80 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-Fortran_COMPILER_FRONTEND_VARIANT.cmake:1 \(add_cu \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_ID-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_ID-stderr.txt index fc1324807..2ec6e4c1a 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_ID-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_ID-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-Fortran_COMPILER_ID.cmake:1 \(add_custom_command\) \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_VERSION-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_VERSION-stderr.txt index f8a41205c..04ed22a9f 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_VERSION-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-Fortran_COMPILER_VERSION-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-Fortran_COMPILER_VERSION.cmake:1 \(add_custom_comm \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_POLICY-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_POLICY-stderr.txt index 4babf4375..2c69cde93 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_POLICY-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_POLICY-stderr.txt @@ -6,4 +6,4 @@ CMake Error at NonValidTarget-TARGET_POLICY.cmake:1 \(add_custom_command\): \$ may only be used with binary targets. It may not be used with add_custom_command or add_custom_target. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PROPERTY-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PROPERTY-stderr.txt index c4149470a..f8b7e22f7 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PROPERTY-stderr.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PROPERTY-stderr.txt @@ -8,4 +8,4 @@ CMake Error at NonValidTarget-TARGET_PROPERTY.cmake:1 \(add_custom_command\): to read a property from using the \$ signature instead. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/GeneratorInstance/CMakeLists.txt b/Tests/RunCMake/GeneratorInstance/CMakeLists.txt index d3137f614..bf2ef1506 100644 --- a/Tests/RunCMake/GeneratorInstance/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorInstance/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt +++ b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake index b86c48197..80d1d5e86 100644 --- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake @@ -1,6 +1,6 @@ include(RunCMake) -if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[0124567]") +if("${RunCMake_GENERATOR}" MATCHES "Visual Studio") run_cmake(VsNormal) include("${RunCMake_BINARY_DIR}/VsNormal-build/defaults.cmake" OPTIONAL) message(STATUS "VsNormal: platform='${VsNormal_Platform}' toolset='${VsNormal_Toolset}'") @@ -9,7 +9,7 @@ endif() set(RunCMake_GENERATOR_TOOLSET "") run_cmake(NoToolset) -if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[0124567]") +if("${RunCMake_GENERATOR}" MATCHES "Visual Studio") set(RunCMake_GENERATOR_TOOLSET "Test Toolset") run_cmake(TestToolset) set(RunCMake_GENERATOR_TOOLSET "Test Toolset,cuda=0.0") @@ -49,7 +49,7 @@ if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[0124567]") run_cmake(TestToolsetFortranIFX) set(RunCMake_GENERATOR_TOOLSET "fortran=bad") run_cmake(BadToolsetFortran) - if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[24567]") + if("${RunCMake_GENERATOR}" MATCHES "Visual Studio") set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64") run_cmake(TestToolsetHostArchBoth) set(RunCMake_GENERATOR_TOOLSET ",host=x64") @@ -119,7 +119,7 @@ set(RunCMake_TEST_OPTIONS -T "Test Toolset" -T "Extra Toolset") run_cmake(TwoToolsets) unset(RunCMake_TEST_OPTIONS) -if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[0124567]|Xcode") +if("${RunCMake_GENERATOR}" MATCHES "Visual Studio|Xcode") set(RunCMake_TEST_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/TestToolset-toolchain.cmake) run_cmake(TestToolsetToolchain) unset(RunCMake_TEST_OPTIONS) diff --git a/Tests/RunCMake/GoogleTest/CMakeLists.txt b/Tests/RunCMake/GoogleTest/CMakeLists.txt index dc9248697..bf2ef1506 100644 --- a/Tests/RunCMake/GoogleTest/CMakeLists.txt +++ b/Tests/RunCMake/GoogleTest/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-check-test-list-extra-args.cmake b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-check-test-list-extra-args.cmake new file mode 100644 index 000000000..1a54e0b25 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-check-test-list-extra-args.cmake @@ -0,0 +1,6 @@ +list(LENGTH test_list_extra_args_TESTS LIST_SIZE) +set(EXPECTED_SIZE 4) +if(NOT LIST_SIZE EQUAL ${EXPECTED_SIZE}) + message("TEST_LIST should have ${EXPECTED_SIZE} elements but it has ${LIST_SIZE}") + message("The unexpected list: [${test_list_extra_args_TESTS}]") +endif() diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListExtraArgs.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListExtraArgs.cmake new file mode 100644 index 000000000..2e4118acd --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListExtraArgs.cmake @@ -0,0 +1,15 @@ +enable_language(CXX) +include(GoogleTest) + +enable_testing() + +include(xcode_sign_adhoc.cmake) + +add_executable(test_list_extra_args test_list_extra_args.cpp) +xcode_sign_adhoc(test_list_extra_args) +gtest_discover_tests( + test_list_extra_args + DISCOVERY_EXTRA_ARGS "how now" "" "\"brown\" cow" +) +set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/GoogleTest-discovery-check-test-list-extra-args.cmake) diff --git a/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt deleted file mode 100644 index 6313151de..000000000 --- a/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt +++ /dev/null @@ -1,17 +0,0 @@ -1: Test command: "?[^ -]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" -1: Working Directory: [^ -]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build -1: Test timeout computed to be: [0-9]+ -1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?' -1: test_launcher: got arg 1 'launcherparam' -1: test_launcher: got arg 2 '--' -1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?' -1: launching: "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" -1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?' -1: test_launcher: got arg 1 'emulatorparam' -1: test_launcher: got arg 2 '--' -1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?' -1: launching: "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" -1: launcher_test\.test1 -1/1 Test #1: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-test-stdout.txt b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-test-stdout.txt new file mode 100644 index 000000000..24cb1b18b --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-test-stdout.txt @@ -0,0 +1,38 @@ +test 1 + Start 1: launcher_test\.test1 + +1: Test command: [^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-NEW-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "" "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/test_launcher(\.exe)?" "" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "" "b" +1: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build +1: Test timeout computed to be: [0-9]+ +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-NEW-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 '' +1: test_launcher: got arg 2 'launcherparam' +1: test_launcher: got arg 3 '--' +1: test_launcher: got arg 4 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/test_launcher(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/test_launcher(\.exe)?" "" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "" "b" +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-NEW-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 '' +1: test_launcher: got arg 2 'emulatorparam' +1: test_launcher: got arg 3 '--' +1: test_launcher: got arg 4 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "" "b" +1: launcher_test\.test1 +1/2 Test #1: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec +test 2 + Start 2: launcher_test\.test1 + +2: Test command: [^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-NEW-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "" "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "" "b" +2: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build +2: Test timeout computed to be: [0-9]+ +2: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-NEW-build([/\]Debug)?[/\]test_launcher(\.exe)?' +2: test_launcher: got arg 1 '' +2: test_launcher: got arg 2 'launcherparam' +2: test_launcher: got arg 3 '--' +2: test_launcher: got arg 4 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?' +2: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "" "b" +2: launcher_test.test1 +2/2 Test #2: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW.cmake b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW.cmake new file mode 100644 index 000000000..1621abc9d --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0178 NEW) +include(Launcher.cmake) diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-test-stdout.txt b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-test-stdout.txt new file mode 100644 index 000000000..0b7620017 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-test-stdout.txt @@ -0,0 +1,35 @@ +test 1 + Start 1: launcher_test\.test1 + +1: Test command: "?[^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-OLD-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build +1: Test timeout computed to be: [0-9]+ +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-OLD-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 'launcherparam' +1: test_launcher: got arg 2 '--' +1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/test_launcher(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-OLD-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 'emulatorparam' +1: test_launcher: got arg 2 '--' +1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: launcher_test\.test1 +1/2 Test #1: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec +test 2 + Start 2: launcher_test\.test1 + +2: Test command: [^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-OLD-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "b" +2: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build +2: Test timeout computed to be: [0-9]+ +2: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-OLD-build([/\]Debug)?[/\]test_launcher(\.exe)?' +2: test_launcher: got arg 1 'launcherparam' +2: test_launcher: got arg 2 '--' +2: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?' +2: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "b" +2: launcher_test.test1 +2/2 Test #2: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD.cmake b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD.cmake new file mode 100644 index 000000000..8830cd0fd --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0178 OLD) +include(Launcher.cmake) diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt new file mode 100644 index 000000000..00b49398f --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-stderr.txt @@ -0,0 +1,38 @@ +CMake Warning \(dev\) at [^ +]*/Modules/GoogleTest\.cmake:[0-9]+ \(message\): + The 'launcher_test' target's TEST_LAUNCHER or CROSSCOMPILING_EMULATOR test + properties contain one or more empty values\. Those empty values are being + silently discarded to preserve backward compatibility\. + + Policy CMP0178 is not set: Test command lines preserve empty arguments\. + Run "cmake --help-policy CMP0178" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + Launcher\.cmake:[0-9]+ \(gtest_discover_tests\) + Launcher-CMP0178-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at [^ +]*/Modules/GoogleTest\.cmake:[0-9]+ \(message\): + The EXTRA_ARGS value contains one or more empty values\. Those empty values + are being silently discarded to preserve backward compatibility\. + + Policy CMP0178 is not set: Test command lines preserve empty arguments\. + Run "cmake --help-policy CMP0178" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + Launcher\.cmake:[0-9]+ \(gtest_discover_tests\) + Launcher-CMP0178-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) in CMakeLists\.txt: + The TEST_LAUNCHER property of target 'launcher_test' contains empty list + items\. Those empty items are being silently discarded to preserve backward + compatibility\. + + Policy CMP0178 is not set: Test command lines preserve empty arguments\. + Run "cmake --help-policy CMP0178" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-test-stdout.txt b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-test-stdout.txt new file mode 100644 index 000000000..1db6f50b7 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-test-stdout.txt @@ -0,0 +1,35 @@ +test 1 + Start 1: launcher_test\.test1 + +1: Test command: "?[^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-WARN-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build +1: Test timeout computed to be: [0-9]+ +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-WARN-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 'launcherparam' +1: test_launcher: got arg 2 '--' +1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/test_launcher(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-WARN-build([/\]Debug)?[/\]test_launcher(\.exe)?' +1: test_launcher: got arg 1 'emulatorparam' +1: test_launcher: got arg 2 '--' +1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?' +1: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests" "a" "b" +1: launcher_test\.test1 +1/2 Test #1: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec +test 2 + Start 2: launcher_test\.test1 + +2: Test command: [^ +]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-WARN-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "b" +2: Working Directory: [^ +]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build +2: Test timeout computed to be: [0-9]+ +2: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]Launcher-CMP0178-WARN-build([/\]Debug)?[/\]test_launcher(\.exe)?' +2: test_launcher: got arg 1 'launcherparam' +2: test_launcher: got arg 2 '--' +2: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?' +2: launching: "[^"]*/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "a" "b" +2: launcher_test.test1 +2/2 Test #2: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec diff --git a/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN.cmake b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN.cmake new file mode 100644 index 000000000..e10907508 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/Launcher-CMP0178-WARN.cmake @@ -0,0 +1 @@ +include(Launcher.cmake) diff --git a/Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake b/Tests/RunCMake/GoogleTest/Launcher.cmake similarity index 73% rename from Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake rename to Tests/RunCMake/GoogleTest/Launcher.cmake index 5f4e6eb0f..11ba87e36 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake +++ b/Tests/RunCMake/GoogleTest/Launcher.cmake @@ -11,14 +11,14 @@ add_executable(launcher_test launcher_test.c) xcode_sign_adhoc(launcher_test) set(launcher "$" - "" # Verify that an empty list item will be preserved + "" # Verify CMP0178's handling of an empty list item "launcherparam" "--" ) set_property(TARGET launcher_test PROPERTY TEST_LAUNCHER "${launcher}") set(emulator "$" - "" # Verify that an empty list item will be preserved + "" # Verify CMP0178's handling of an empty list item "emulatorparam" "--" ) @@ -26,4 +26,10 @@ set_property(TARGET launcher_test PROPERTY CROSSCOMPILING_EMULATOR "${emulator}" gtest_discover_tests( launcher_test + EXTRA_ARGS a "" b +) + +gtest_add_tests( + TARGET launcher_test + EXTRA_ARGS a "" b ) diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index 08cc27486..b3edd6837 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake @@ -101,7 +101,7 @@ function(run_GoogleTest DISCOVERY_MODE) ) endfunction() -function(run_GoogleTestLauncher DISCOVERY_MODE) +function(run_Launcher_CMP0178 DISCOVERY_MODE cmp0178) if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "14.0") return() endif() @@ -110,12 +110,12 @@ function(run_GoogleTestLauncher DISCOVERY_MODE) endif() # Use a single build tree for a few tests without cleaning. - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestLauncher-build) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Launcher-CMP0178-${cmp0178}-build) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif() - run_cmake_with_options(GoogleTestLauncher + run_cmake_with_options(Launcher-CMP0178-${cmp0178} -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE} ) @@ -123,14 +123,14 @@ function(run_GoogleTestLauncher DISCOVERY_MODE) # do not issue any warnings on stderr that would cause the build to fail set(RunCMake_TEST_OUTPUT_MERGE 1) - run_cmake_command(GoogleTestLauncher-build + run_cmake_command(Launcher-CMP0178-${cmp0178}-build ${CMAKE_COMMAND} --build . --config Debug ) unset(RunCMake_TEST_OUTPUT_MERGE) - run_cmake_command(GoogleTestLauncher-test + run_cmake_command(Launcher-CMP0178-${cmp0178}-test ${CMAKE_CTEST_COMMAND} -C Debug -V @@ -355,20 +355,49 @@ function(run_GoogleTest_discovery_test_list_scoped DISCOVERY_MODE) ) endfunction() +function(run_GoogleTest_discovery_test_list_extra_args DISCOVERY_MODE) + # Use a single build tree for a few tests without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-test-list-extra-args-build) + set(RunCMake_TEST_NO_CLEAN 1) + if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) + endif() + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + run_cmake_with_options(GoogleTestDiscoveryTestListExtraArgs -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE}) + + run_cmake_command(GoogleTest-discovery-test-list-extra-args-build + ${CMAKE_COMMAND} + --build . + --config Debug + --target test_list_extra_args + ) + + run_cmake_command(GoogleTest-discovery-test-list-extra-args-test + ${CMAKE_CTEST_COMMAND} + -C Debug + --no-label-summary + ) +endfunction() + foreach(DISCOVERY_MODE POST_BUILD PRE_TEST) - message("Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...") + message(STATUS "Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...") run_GoogleTest(${DISCOVERY_MODE}) - run_GoogleTestLauncher(${DISCOVERY_MODE}) run_GoogleTestXML(${DISCOVERY_MODE}) - message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...") + run_Launcher_CMP0178(${DISCOVERY_MODE} NEW) + run_Launcher_CMP0178(${DISCOVERY_MODE} OLD) + run_Launcher_CMP0178(${DISCOVERY_MODE} WARN) + message(STATUS "Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...") run_GoogleTest_discovery_timeout(${DISCOVERY_MODE}) run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE}) run_GoogleTest_discovery_test_list(${DISCOVERY_MODE}) run_GoogleTest_discovery_test_list_scoped(${DISCOVERY_MODE}) + run_GoogleTest_discovery_test_list_extra_args(${DISCOVERY_MODE}) run_GoogleTest_discovery_flush_script(${DISCOVERY_MODE}) endforeach() if(RunCMake_GENERATOR_IS_MULTI_CONFIG) - message("Testing PRE_TEST discovery multi configuration...") + message(STATUS "Testing PRE_TEST discovery multi configuration...") run_GoogleTest_discovery_multi_config() endif() diff --git a/Tests/RunCMake/GoogleTest/launcher_test.c b/Tests/RunCMake/GoogleTest/launcher_test.c index ce9156539..eed620666 100644 --- a/Tests/RunCMake/GoogleTest/launcher_test.c +++ b/Tests/RunCMake/GoogleTest/launcher_test.c @@ -10,7 +10,7 @@ TEST_F( launcher_test, test1 ) int main(int argc, char** argv) { - /* Note: GoogleTest.cmake doesn't actually depend on Google Test as such; + /* Note: Launcher.cmake doesn't actually depend on Google Test as such; * it only requires that we produces output in the expected format when * invoked with --gtest_list_tests. Thus, we fake that here. This allows us * to test the module without actually needing Google Test. */ diff --git a/Tests/RunCMake/GoogleTest/test_list_extra_args.cpp b/Tests/RunCMake/GoogleTest/test_list_extra_args.cpp new file mode 100644 index 000000000..f63ec222f --- /dev/null +++ b/Tests/RunCMake/GoogleTest/test_list_extra_args.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int argc, char** argv) +{ + // Note: This test doesn't actually depend on Google Test as such; + // it only requires that we produce output in the expected format when + // invoked with --gtest_list_tests. Thus, we fake that here. This allows us + // to test the module without actually needing Google Test. + + // Simple test of DISCOVERY_EXTRA_ARGS + if (argc > 4 && std::string(argv[1]) == "--gtest_list_tests" && + std::string(argv[2]) == "how now" && std::string(argv[3]) == "" && + std::string(argv[4]) == "\"brown\" cow") { + std::cout << "test_list_test/test.\n"; + std::cout << " case/0 # GetParam() = 'one'\n"; + std::cout << " case/1 # GetParam() = 'two'\n"; + std::cout << " case/2 # GetParam() = 'three'\n"; + std::cout << " case/3 # GetParam() = 'four'\n"; + } + return 0; +} diff --git a/Tests/RunCMake/IAR/RunCMakeTest.cmake b/Tests/RunCMake/IAR/RunCMakeTest.cmake index 2fefa6a7d..1d6bd4f43 100644 --- a/Tests/RunCMake/IAR/RunCMakeTest.cmake +++ b/Tests/RunCMake/IAR/RunCMakeTest.cmake @@ -24,8 +24,8 @@ foreach(_iar_toolchain IN LISTS _iar_toolchains) # Sets the minimal requirements for linking each target architecture if(ARCH STREQUAL "avr") string(CONCAT LINK_OPTS - "-I${TOOLKIT_DIR}/lib " - "-f ${TOOLKIT_DIR}/src/template/lnk3s.xcl " + "-I\"${TOOLKIT_DIR}/lib\" " + "-f \"${TOOLKIT_DIR}/src/template/lnk3s.xcl\" " ) elseif(ARCH STREQUAL "rl78") string(CONCAT LINK_OPTS @@ -36,7 +36,14 @@ foreach(_iar_toolchain IN LISTS _iar_toolchains) "--config_def _NEAR_CONST_LOCATION_START=0x2000 " "--config_def _NEAR_CONST_LOCATION_SIZE=0x6F00 " "--define_symbol _NEAR_CONST_LOCATION=0 " - "--config ${TOOLKIT_DIR}/config/lnkrl78_s3.icf " + "--config \"${TOOLKIT_DIR}/config/lnkrl78_s3.icf\" " + ) + elseif(ARCH STREQUAL "rh850") + string(CONCAT LINK_OPTS + "--config_def CSTACK_SIZE=0x1000 " + "--config_def HEAP_SIZE=0x1000 " + "--config_def _SELF_SIZE=0x20000 " + "--config \"${TOOLKIT_DIR}/config/lnkr7f701401.icf\" " ) else() set(LINK_OPTS "") diff --git a/Tests/RunCMake/IAR/module.asm b/Tests/RunCMake/IAR/module.asm index 1e0823648..cf4c6cdad 100644 --- a/Tests/RunCMake/IAR/module.asm +++ b/Tests/RunCMake/IAR/module.asm @@ -1,41 +1,53 @@ -#if defined(__IASM8051__) || defined(__IASM430__) - NAME main -#else - MODULE main +#if !defined(__IAR_SYSTEMS_ASM__) +#error This source file should be assembled by the IAR Assembler. #endif - PUBLIC main - PUBLIC __iar_program_start - PUBLIC __program_start +#if !defined(__A430__) && \ + !(((__TID__ >> 8) & 0x7F) == 32) && \ + !(((__TID__ >> 8) & 0x7F) == 90) && \ + !defined(__IASMARM__) && \ + !defined(__IASMRH850__) && \ + !defined(__IASMRISCV__) && \ + !defined(__IASMRL78__) && \ + !defined(__IASMRX__) && \ + !defined(__IASMSTM8__) +#error Unable to detect a supported target architecture. +#endif + NAME main + + PUBLIC main + PUBLIC __iar_program_start + PUBLIC __program_start -#if defined(__IASMSTM8__) - EXTERN CSTACK$$Limit - SECTION `.near_func.text`:CODE:NOROOT(0) -#elif defined(__IASMAVR__) - ORG $0 - RJMP main - RSEG CODE -#elif defined(__IASM8051__) - ORG 0FFFEh - DC16 main - RSEG RCODE -?cmain: -#elif defined(__IASM430__) - ORG 0FFFEh - DC16 init - RSEG CSTACK - RSEG CODE -init: - MOV #SFE(CSTACK), SP -#else - EXTERN __iar_static_base$$GPREL - SECTION CSTACK:DATA:NOROOT(4) - SECTION `.cstartup`:CODE(2) - CODE +#if defined(__A430__) + ORG 0FFFEh + DC16 init + RSEG CSTACK + RSEG CODE +init: MOV #SFE(CSTACK), SP + RSEG CODE +#elif (((__TID__ >> 8) & 0x7F) == 32) + ORG 0FFFEh ; __A8051__ + DC16 main + RSEG RCODE +#elif (((__TID__ >> 8) & 0x7F) == 90) + ORG $0 ; __AAVR__ + RJMP main +#elif defined(__IASMARM__) || \ + defined(__IASMRH850__) || \ + defined(__IASMRISCV__) || \ + defined(__IASMRL78__) || \ + defined(__IASMRX__) + EXTERN __iar_static_base$$GPREL + SECTION CSTACK:DATA:NOROOT(4) + SECTION `.cstartup`:CODE(2) + CODE +#elif defined(__IASMSTM8__) + EXTERN CSTACK$$Limit + SECTION `.near_func.text`:CODE:NOROOT(0) #endif __program_start: __iar_program_start: -main: - NOP - END +main: NOP + END diff --git a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt index d0aef2c47..48e8a016d 100644 --- a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt +++ b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/IfacePaths/CMakeLists.txt b/Tests/RunCMake/IfacePaths/CMakeLists.txt index 0d707f077..5ee2bef6c 100644 --- a/Tests/RunCMake/IfacePaths/CMakeLists.txt +++ b/Tests/RunCMake/IfacePaths/CMakeLists.txt @@ -1,7 +1,7 @@ if(RunCMake_TEST MATCHES "-CMP0052") - cmake_minimum_required(VERSION 3.0) + cmake_minimum_required(VERSION 3.0) # old enough to not set CMP0052 else() - cmake_minimum_required(VERSION 3.5) + cmake_minimum_required(VERSION 3.10) endif() project(${RunCMake_TEST} NONE) if(NOT TEST_FILE) diff --git a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt index 4db8209ba..05c69916d 100644 --- a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt +++ b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt +++ b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/IncompatibleQt/CMakeLists.txt b/Tests/RunCMake/IncompatibleQt/CMakeLists.txt index ebab7a357..c814f1493 100644 --- a/Tests/RunCMake/IncompatibleQt/CMakeLists.txt +++ b/Tests/RunCMake/IncompatibleQt/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake index ae3f1121f..b64ff38ee 100644 --- a/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallParallel/RunCMakeTest.cmake @@ -1,17 +1,59 @@ include(RunCMake) -function(install_test test parallel install_target check_script) +function(install_test test) + cmake_parse_arguments(ARGS "PARALLEL;NINJA;TOUCH_CACHE" "ARGS;COMPONENT" "" ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-install) - set(RunCMake_TEST_OPTIONS -DINSTALL_PARALLEL=${parallel}) + set(RunCMake_TEST_OPTIONS -DINSTALL_PARALLEL=${ARGS_PARALLEL} -DCMAKE_INSTALL_PREFIX=install) + set(RunCMake_TEST_OUTPUT_MERGE 1) if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif() + if (ARGS_COMPONENT) + list(APPEND ARGS_ARGS "--component ${ARGS_COMPONENT}") + endif() run_cmake(install) set(RunCMake_TEST_NO_CLEAN 1) - run_cmake_command(${test}-install ${CMAKE_COMMAND} --build . --config Debug -t ${install_target}) - set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_SOURCE_DIR}) - run_cmake_command(verify-parallel ${CMAKE_COMMAND} -P ${check_script} ${RunCMake_TEST_BINARY_DIR}/.ninja_log) + if (ARGS_TOUCH_CACHE) + run_cmake_command(${test}-sleep ${CMAKE_COMMAND} -E sleep 2) + run_cmake_command(${test}-touch + ${CMAKE_COMMAND} -E touch ${RunCMake_TEST_BINARY_DIR}/CMakeFiles/cmake.check_cache) + endif() + if (ARGS_NINJA) + if (ARGS_PARALLEL) + set(INSTALL_COUNT 5) + else() + set(INSTALL_COUNT 1) + endif() + set(RunCMake-check-file check-num-installs.cmake) + run_cmake_command(${test}-install ${CMAKE_COMMAND} --build . --config Debug ${ARGS_ARGS}) + unset(RunCMake-check-file) + else() + if (ARGS_COMPONENT) + if(ARGS_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$") + set(INSTALL_MANIFEST "install_manifest_${ARGS_COMPONENT}.txt") + else() + string(MD5 COMPONENT_HASH "${ARGS_COMPONENT}") + set(INSTALL_MANIFEST "install_manifest_${COMPONENT_HASH}.txt") + endif() + set(INSTALL_COUNT 0) + else() + set(INSTALL_MANIFEST "install_manifest.txt") + set(INSTALL_COUNT 5) + endif() + set(INSTALL_MANIFEST ${RunCMake_TEST_BINARY_DIR}/${INSTALL_MANIFEST}) + set(RunCMake-check-file check-manifest.cmake) + run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . ${ARGS_ARGS}) + unset(RunCMake-check-file) + endif() endfunction() -install_test(parallel 1 install/parallel check-parallel.cmake) -install_test(no-parallel 0 install check-single.cmake) +install_test(parallel PARALLEL ARGS "-j 4") +install_test(no-parallel ARGS "-j 4") +install_test(out-of-date-json TOUCH_CACHE PARALLEL ARGS "-j 4") +install_test(component PARALLEL ARGS "-j 4" COMPONENT "ALPHANUMERIC123") +install_test(component-hash PARALLEL ARGS "-j 4" COMPONENT "@#$") + +if(RunCMake_GENERATOR MATCHES "Ninja") + install_test(ninja-parallel ARGS "-t install/parallel" NINJA PARALLEL) + install_test(ninja-no-parallel ARGS "-t install" NINJA) +endif() diff --git a/Tests/RunCMake/InstallParallel/check-manifest.cmake b/Tests/RunCMake/InstallParallel/check-manifest.cmake new file mode 100644 index 000000000..4253e6073 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/check-manifest.cmake @@ -0,0 +1,10 @@ +if (NOT EXISTS ${INSTALL_MANIFEST}) + set(RunCMake_TEST_FAILED "Install manifest not generated: ${INSTALL_MANIFEST}") +endif() + +file(STRINGS ${INSTALL_MANIFEST} lines ENCODING UTF-8) +list(LENGTH lines len) + +if (NOT len EQUAL ${INSTALL_COUNT}) + set(RunCMake_TEST_FAILED "Install manifest missing content: ${len}/${INSTALL_COUNT}") +endif() diff --git a/Tests/RunCMake/InstallParallel/check-num-installs.cmake b/Tests/RunCMake/InstallParallel/check-num-installs.cmake new file mode 100644 index 000000000..91379973f --- /dev/null +++ b/Tests/RunCMake/InstallParallel/check-num-installs.cmake @@ -0,0 +1,7 @@ +file(STRINGS ${RunCMake_TEST_BINARY_DIR}/.ninja_log lines ENCODING UTF-8) +list(FILTER lines INCLUDE REGEX ".*install.*util") +list(LENGTH lines len) + +if (NOT ${len} STREQUAL ${INSTALL_COUNT}) + set(RunCMake_TEST_FAILED "Wrong number of cmake -P calls to install: ${len}/${INSTALL_COUNT}") +endif() diff --git a/Tests/RunCMake/InstallParallel/check-parallel.cmake b/Tests/RunCMake/InstallParallel/check-parallel.cmake deleted file mode 100644 index 4e4cf5247..000000000 --- a/Tests/RunCMake/InstallParallel/check-parallel.cmake +++ /dev/null @@ -1,15 +0,0 @@ -include(read-ninja-install.cmake) - -foreach(line ${lines}) - string(REPLACE "\t" ";" line ${line}) - list(GET line 0 start) - list(GET line 1 end) - list(APPEND start_times ${start}) - list(APPEND end_times ${end}) -endforeach() -list(GET start_times 1 start_2) -list(GET end_times 0 end_1) - -if (NOT start_2 LESS end_1) - message(FATAL_ERROR "Install is not parallel") -endif() diff --git a/Tests/RunCMake/InstallParallel/check-single.cmake b/Tests/RunCMake/InstallParallel/check-single.cmake deleted file mode 100644 index 79c4d5c07..000000000 --- a/Tests/RunCMake/InstallParallel/check-single.cmake +++ /dev/null @@ -1,5 +0,0 @@ -include(read-ninja-install.cmake) -list(LENGTH lines len) -if (NOT ${len} STREQUAL "1") - message(FATAL_ERROR "Expected single installation call") -endif() diff --git a/Tests/RunCMake/InstallParallel/install.cmake b/Tests/RunCMake/InstallParallel/install.cmake index 54b5078ff..a64b5d7c1 100644 --- a/Tests/RunCMake/InstallParallel/install.cmake +++ b/Tests/RunCMake/InstallParallel/install.cmake @@ -1,4 +1,4 @@ -install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt TYPE DATA RENAME root.txt) if (INSTALL_PARALLEL) set_property(GLOBAL PROPERTY INSTALL_PARALLEL ON) endif() diff --git a/Tests/RunCMake/InstallParallel/ninja-no-parallel-install-stdout.txt b/Tests/RunCMake/InstallParallel/ninja-no-parallel-install-stdout.txt new file mode 100644 index 000000000..aa0a9d3f3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/ninja-no-parallel-install-stdout.txt @@ -0,0 +1,12 @@ +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* diff --git a/Tests/RunCMake/InstallParallel/ninja-parallel-install-stdout.txt b/Tests/RunCMake/InstallParallel/ninja-parallel-install-stdout.txt new file mode 100644 index 000000000..f49acf1d8 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/ninja-parallel-install-stdout.txt @@ -0,0 +1,25 @@ +\[1\/5\] Installing only the local directory... +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[2\/5\] Installing only the local directory... +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[3\/5\] Installing only the local directory... +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[4\/5\] Installing only the local directory... +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[5\/5\] Installing only the local directory... +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* diff --git a/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt b/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt deleted file mode 100644 index 8f69a0429..000000000 --- a/Tests/RunCMake/InstallParallel/no-parallel-install-stderr.txt +++ /dev/null @@ -1,5 +0,0 @@ -installing:.* -installing:.* -installing:.* -installing:.* -installing:.* diff --git a/Tests/RunCMake/InstallParallel/no-parallel-install-stdout.txt b/Tests/RunCMake/InstallParallel/no-parallel-install-stdout.txt new file mode 100644 index 000000000..aa0a9d3f3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/no-parallel-install-stdout.txt @@ -0,0 +1,12 @@ +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* diff --git a/Tests/RunCMake/InstallParallel/out-of-date-json-install-stdout.txt b/Tests/RunCMake/InstallParallel/out-of-date-json-install-stdout.txt new file mode 100644 index 000000000..aa0a9d3f3 --- /dev/null +++ b/Tests/RunCMake/InstallParallel/out-of-date-json-install-stdout.txt @@ -0,0 +1,12 @@ +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* +\-\- Installing:[^ +]* diff --git a/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt b/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt index e0d2a56e8..61a52b052 100644 --- a/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt +++ b/Tests/RunCMake/InstallParallel/parallel-install-stdout.txt @@ -1,15 +1,30 @@ -\[1\/5\] Installing only the local directory... -\-\- Install configuration: \"Debug\" -installing:.* -\[2\/5\] Installing only the local directory... -\-\- Install configuration: \"Debug\" -installing:.* -\[3\/5\] Installing only the local directory... -\-\- Install configuration: \"Debug\" -installing:.* -\[4\/5\] Installing only the local directory... -\-\- Install configuration: \"Debug\" -installing:.* -\[5\/5\] Installing only the local directory... -\-\- Install configuration: \"Debug\" -installing:.* +\[1\/5\] [^ +]* +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[2\/5\] [^ +]* +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[3\/5\] [^ +]* +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[4\/5\] [^ +]* +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* +\[5\/5\] [^ +]* +\-\- Install configuration:[^ +]* +\-\- Installing:[^ +]* diff --git a/Tests/RunCMake/InstallParallel/read-ninja-install.cmake b/Tests/RunCMake/InstallParallel/read-ninja-install.cmake deleted file mode 100644 index 731c5eb51..000000000 --- a/Tests/RunCMake/InstallParallel/read-ninja-install.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(ninja_log ${CMAKE_ARGV3}) -file(STRINGS ${ninja_log} lines) -list(POP_FRONT lines) -list(FILTER lines INCLUDE REGEX ".*install.*util") diff --git a/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt index 6b235c493..139724d9a 100644 --- a/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt +++ b/Tests/RunCMake/InstallParallel/subdir-1/CMakeLists.txt @@ -1,3 +1,3 @@ -install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt TYPE DATA RENAME 1.txt) add_subdirectory(subdir-3) add_subdirectory(subdir-4) diff --git a/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt index dd7eac3c7..71dea4336 100644 --- a/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt +++ b/Tests/RunCMake/InstallParallel/subdir-1/subdir-3/CMakeLists.txt @@ -1 +1 @@ -install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt TYPE DATA RENAME 3.txt) diff --git a/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt index dd7eac3c7..bfda21e38 100644 --- a/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt +++ b/Tests/RunCMake/InstallParallel/subdir-1/subdir-4/CMakeLists.txt @@ -1 +1 @@ -install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt TYPE DATA RENAME 4.txt) diff --git a/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt b/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt index dd7eac3c7..e6efc541a 100644 --- a/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt +++ b/Tests/RunCMake/InstallParallel/subdir-2/CMakeLists.txt @@ -1 +1 @@ -install(CODE "message(installing:${CMAKE_CURRENT_SOURCE_DIR})") +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt TYPE DATA RENAME 2.txt) diff --git a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt +++ b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Languages/CMakeLists.txt b/Tests/RunCMake/Languages/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/Languages/CMakeLists.txt +++ b/Tests/RunCMake/Languages/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt index 55aa1bbe7..f96567bce 100644 --- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt +++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt index f11d8f576..91a0ad712 100644 --- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt +++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt index 6e1f8a210..9467ea4d0 100644 --- a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt +++ b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(RunCMake_TEST MATCHES "^CMP0028") - cmake_minimum_required(VERSION 2.8.12) + cmake_minimum_required(VERSION 2.8.12) # old enough to not set CMP0028 endif() project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) # policy used at end of dir diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup-reverse.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup-reverse.txt new file mode 100644 index 000000000..7e46d1ba7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup-reverse.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup.txt new file mode 100644 index 000000000..7e46d1ba7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout-dedup.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout.txt new file mode 100644 index 000000000..7e46d1ba7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-run-stdout.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup-reverse.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup-reverse.txt new file mode 100644 index 000000000..b908fbcdc --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup-reverse.txt @@ -0,0 +1,9 @@ +target \[main\] link dependency ordering: + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[B\] + target \[C\] + target \[A\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup.txt new file mode 100644 index 000000000..b908fbcdc --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr-dedup.txt @@ -0,0 +1,9 @@ +target \[main\] link dependency ordering: + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[B\] + target \[C\] + target \[A\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr.txt new file mode 100644 index 000000000..b908fbcdc --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY-stderr.txt @@ -0,0 +1,9 @@ +target \[main\] link dependency ordering: + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[B\] + target \[C\] + target \[A\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY.cmake new file mode 100644 index 000000000..ea38b5be3 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_FREELY.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER_FREELY) +include(Basic-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup-reverse.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup-reverse.txt new file mode 100644 index 000000000..7e46d1ba7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup-reverse.txt @@ -0,0 +1 @@ +^Library 'B' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup.txt new file mode 100644 index 000000000..6ef12ebe0 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout-dedup.txt @@ -0,0 +1 @@ +^Library 'A' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout.txt new file mode 100644 index 000000000..6ef12ebe0 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-run-stdout.txt @@ -0,0 +1 @@ +^Library 'A' was linked first\.$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup-reverse.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup-reverse.txt new file mode 100644 index 000000000..7d91beee6 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup-reverse.txt @@ -0,0 +1,10 @@ +target \[main\] link dependency ordering: + target \[A\] + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[B\] + target \[C\] + target \[A\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup.txt new file mode 100644 index 000000000..d4bccc434 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr-dedup.txt @@ -0,0 +1,10 @@ +target \[main\] link dependency ordering: + target \[A\] + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[A\] + target \[B\] + target \[C\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr.txt new file mode 100644 index 000000000..7e9a1cdf4 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY-stderr.txt @@ -0,0 +1,11 @@ +target \[main\] link dependency ordering: + target \[A\] + target \[B\] + target \[C\] + target \[A\] + +target \[main\] link line: + target \[A\] + target \[B\] + target \[C\] + target \[A\]$ diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY.cmake new file mode 100644 index 000000000..12876b11f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-REORDER_MINIMALLY.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER_MINIMALLY) +include(Basic-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake new file mode 100644 index 000000000..8dbbd3975 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic-common.cmake @@ -0,0 +1,15 @@ +enable_language(C) + +add_library(A STATIC BasicA.c BasicX.c) +add_library(B STATIC BasicB.c BasicX.c) +add_library(C STATIC BasicC.c BasicX.c) +target_link_libraries(B PRIVATE A) +target_link_libraries(C PRIVATE A) +target_compile_definitions(A PRIVATE BASIC_ID="A") +target_compile_definitions(B PRIVATE BASIC_ID="B") +target_compile_definitions(C PRIVATE BASIC_ID="C") + +add_executable(main Basic.c) +target_link_libraries(main PRIVATE A B C) +set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented +set_property(TARGET main PROPERTY RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_BINARY_DIR}>") diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Basic.c b/Tests/RunCMake/LinkLibrariesStrategy/Basic.c new file mode 100644 index 000000000..124d48938 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Basic.c @@ -0,0 +1,11 @@ +extern int BasicB(void); +extern int BasicC(void); + +/* Use a symbol provided by a dedicated object file in A, B, and C. + The first library linked will determine the return value. */ +extern int BasicX(void); + +int main(void) +{ + return BasicB() + BasicC() + BasicX(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c new file mode 100644 index 000000000..d3fe95d8a --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicA.c @@ -0,0 +1,4 @@ +int BasicA(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c new file mode 100644 index 000000000..fd7a120aa --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicB.c @@ -0,0 +1,5 @@ +extern int BasicA(void); +int BasicB(void) +{ + return BasicA(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c new file mode 100644 index 000000000..7171dd101 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicC.c @@ -0,0 +1,5 @@ +extern int BasicA(void); +int BasicC(void) +{ + return BasicA(); +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c b/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c new file mode 100644 index 000000000..39f786392 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/BasicX.c @@ -0,0 +1,7 @@ +#include + +int BasicX(void) +{ + printf("Library '%s' was linked first.\n", BASIC_ID); + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt b/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt new file mode 100644 index 000000000..dda37d8bc --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY-stderr.txt new file mode 100644 index 000000000..2353288be --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY-stderr.txt @@ -0,0 +1,9 @@ +target \[main\] link dependency ordering: + item \[-Flag1\] + target \[A\] + target \[B\] + item \[-Flag2\] + target \[C\] + item \[-Flag3\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY.cmake new file mode 100644 index 000000000..c205fad6b --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_FREELY.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER_FREELY) +include(Duplicate-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY-stderr.txt new file mode 100644 index 000000000..2637f9326 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY-stderr.txt @@ -0,0 +1,15 @@ +target \[main\] link dependency ordering: + item \[-Flag1\] + target \[A\] + item \[-Flag1\] + target \[B\] + item \[-Flag2\] + target \[C\] + item \[-Flag2\] + target \[A\] + item \[-Flag2\] + target \[B\] + item \[-Flag3\] + target \[C\] + +target \[main\] link line: diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY.cmake new file mode 100644 index 000000000..6d4545b39 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-REORDER_MINIMALLY.cmake @@ -0,0 +1,2 @@ +set(CMAKE_LINK_LIBRARIES_STRATEGY REORDER_MINIMALLY) +include(Duplicate-common.cmake) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake new file mode 100644 index 000000000..5050a0ab6 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate-common.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +add_library(A INTERFACE IMPORTED) +add_library(B INTERFACE IMPORTED) +add_library(C INTERFACE IMPORTED) +set_property(TARGET A PROPERTY IMPORTED_LIBNAME A) +set_property(TARGET B PROPERTY IMPORTED_LIBNAME B) +set_property(TARGET C PROPERTY IMPORTED_LIBNAME C) + +add_executable(main Duplicate.c) +target_link_libraries(main PRIVATE -Flag1 A -Flag1 B -Flag2 C -Flag2 A -Flag2 B -Flag3 C) +set_property(TARGET main PROPERTY LINK_DEPENDS_DEBUG_MODE 1) # undocumented diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c new file mode 100644 index 000000000..8488f4e58 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Duplicate.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake new file mode 100644 index 000000000..fd5da9142 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Inspect.cmake @@ -0,0 +1,12 @@ +enable_language(C) + +set(info "") +foreach(var + CMAKE_C_LINK_LIBRARIES_PROCESSING + ) + if(DEFINED ${var}) + string(APPEND info "set(${var} \"${${var}}\")\n") + endif() +endforeach() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake b/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake new file mode 100644 index 000000000..787b63632 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/RunCMakeTest.cmake @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.30) + +include(RunCMake) + +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug) +else() + set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) +endif() + +# Detect information from the toolchain: +# - CMAKE_C_LINK_LIBRARIES_PROCESSING +run_cmake(Inspect) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + +run_cmake(Unknown) + +function(run_strategy case exe) + foreach(cmp0179 OLD NEW) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-CMP0179-${cmp0179}-build) + set(RunCMake_TEST_VARIANT_DESCRIPTION "...CMP0179-${cmp0179}") + if("DEDUPLICATION=ALL" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING) + if("ORDER=REVERSE" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING AND cmp0179 STREQUAL "OLD") + set(RunCMake-stderr-file ${case}-stderr-dedup-reverse.txt) + else() + set(RunCMake-stderr-file ${case}-stderr-dedup.txt) + endif() + endif() + run_cmake_with_options(${case} -DCMAKE_POLICY_DEFAULT_CMP0179=${cmp0179}) + unset(RunCMake-stderr-file) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug) + if(exe) + if("DEDUPLICATION=ALL" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING) + if("ORDER=REVERSE" IN_LIST CMAKE_C_LINK_LIBRARIES_PROCESSING AND cmp0179 STREQUAL "OLD") + set(RunCMake-stdout-file ${case}-run-stdout-dedup-reverse.txt) + else() + set(RunCMake-stdout-file ${case}-run-stdout-dedup.txt) + endif() + endif() + run_cmake_command(${case}-run ${RunCMake_TEST_BINARY_DIR}/${exe}) + unset(RunCMake-stdout-file) + endif() + endforeach() +endfunction() + +run_strategy(Basic-REORDER_MINIMALLY "main") +run_strategy(Basic-REORDER_FREELY "main") + +run_cmake(Duplicate-REORDER_MINIMALLY) +run_cmake(Duplicate-REORDER_FREELY) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt new file mode 100644 index 000000000..3081f3259 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown-stderr.txt @@ -0,0 +1,4 @@ +^CMake Error at Unknown.cmake:5 \(add_executable\): + LINK_LIBRARIES_STRATEGY value 'unknown' is not recognized\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9] \(include\) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake b/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake new file mode 100644 index 000000000..d3ad5862f --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/Unknown.cmake @@ -0,0 +1,5 @@ +enable_language(C) + +set(CMAKE_LINK_LIBRARIES_STRATEGY unknown) + +add_executable(main main.c) diff --git a/Tests/RunCMake/LinkLibrariesStrategy/main.c b/Tests/RunCMake/LinkLibrariesStrategy/main.c new file mode 100644 index 000000000..8488f4e58 --- /dev/null +++ b/Tests/RunCMake/LinkLibrariesStrategy/main.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/RunCMake/LinkStatic/CMakeLists.txt b/Tests/RunCMake/LinkStatic/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/LinkStatic/CMakeLists.txt +++ b/Tests/RunCMake/LinkStatic/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt +++ b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Make/CMakeLists.txt b/Tests/RunCMake/Make/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/Make/CMakeLists.txt +++ b/Tests/RunCMake/Make/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/MultiLint/CMakeLists copy.txt b/Tests/RunCMake/MultiLint/CMakeLists copy.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/MultiLint/CMakeLists copy.txt +++ b/Tests/RunCMake/MultiLint/CMakeLists copy.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt index 6d340b0f1..1daf1149a 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0058-OLD-by\.cmake:[0-9] \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt index 834c78135..bc864beb8 100644 --- a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0058-OLD-no\.cmake:[0-9] \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt index 2927f527b..ecf461648 100644 --- a/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0058-WARN-by\.cmake:[0-9] \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt index 1ffb4169d..b5d5b0b6e 100644 --- a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt +++ b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0058-WARN-no\.cmake:[0-9] \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/Ninja/CMakeLists.txt b/Tests/RunCMake/Ninja/CMakeLists.txt index 8eb57486a..30c0273a6 100644 --- a/Tests/RunCMake/Ninja/CMakeLists.txt +++ b/Tests/RunCMake/Ninja/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 8b24c1611..d1f99b217 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -108,7 +108,10 @@ run_cmake(JobPoolUsesTerminal) run_cmake(RspFileC) run_cmake(RspFileCXX) -if(TEST_Fortran) +if(CMake_TEST_Fortran + # FIXME(lfortran): The compiler does not support response files. + AND NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran" + ) run_cmake(RspFileFortran) endif() diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandsAndTargets.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandsAndTargets.cmake index 7bed09090..b7e4aeab2 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandsAndTargets.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandsAndTargets.cmake @@ -16,11 +16,11 @@ function(create_targets prefix) get_write_file_command(cmd ${prefix}PostBuild.txt) add_executable(${prefix}PostBuild ${CMAKE_SOURCE_DIR}/main.c) - add_custom_command(TARGET ${prefix}PostBuild COMMAND ${cmd} BYPRODUCTS ${prefix}PostBuild.txt) + add_custom_command(TARGET ${prefix}PostBuild POST_BUILD COMMAND ${cmd} BYPRODUCTS ${prefix}PostBuild.txt) get_write_file_command(cmd ${prefix}TargetPostBuild.txt) add_custom_target(${prefix}TargetPostBuild) - add_custom_command(TARGET ${prefix}TargetPostBuild COMMAND ${cmd} BYPRODUCTS ${prefix}TargetPostBuild.txt) + add_custom_command(TARGET ${prefix}TargetPostBuild POST_BUILD COMMAND ${cmd} BYPRODUCTS ${prefix}TargetPostBuild.txt) file(APPEND "${CMAKE_BINARY_DIR}/target_files_custom.cmake" "set(TARGET_DEPENDS_${prefix}Command [==[${CMAKE_CURRENT_BINARY_DIR}/${prefix}Command.txt]==]) diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index 88fd1e832..f1e8b3028 100644 --- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake @@ -17,7 +17,7 @@ function(check_files dir) list(SORT expected) file(GLOB_RECURSE actual "${dir}/*") - list(FILTER actual EXCLUDE REGEX "/CMakeFiles/|\\.ninja$|/CMakeCache\\.txt$|/target_files[^/]*\\.cmake$|/\\.ninja_[^/]*$|/cmake_install\\.cmake$|\\.ilk$|\\.manifest$|\\.odx$|\\.pdb$|\\.exp$|/install_manifest\\.txt$|/\\.qt/QtDeploySupport[^/]*\\.cmake$") + list(FILTER actual EXCLUDE REGEX "/CMakeFiles/|\\.ninja$|/CMakeCache\\.txt$|/target_files[^/]*\\.cmake$|/\\.ninja_[^/]*$|/cmake_install\\.cmake$|\\.ilk$|\\.manifest$|\\.odx$|\\.pdb$|\\.exp$|/install_manifest\\.txt$|/\\.qt/(QtDeploySupport|QtDeployTargets)[^/]*\\.cmake$") foreach(f IN LISTS _check_files_INCLUDE _check_files_EXCLUDE) if(EXISTS ${f}) list(APPEND actual ${f}) diff --git a/Tests/RunCMake/ObsoleteQtMacros/CMakeLists.txt b/Tests/RunCMake/ObsoleteQtMacros/CMakeLists.txt index 44b5d30c0..86d47a7d5 100644 --- a/Tests/RunCMake/ObsoleteQtMacros/CMakeLists.txt +++ b/Tests/RunCMake/ObsoleteQtMacros/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST}) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/PackageInfo/Appendix-check.cmake b/Tests/RunCMake/PackageInfo/Appendix-check.cmake new file mode 100644 index 000000000..864e73186 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Appendix-check.cmake @@ -0,0 +1,16 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Appendix-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "mammal" "type") +expect_value("${content}" "1.0" "version") + +file(READ "${out_dir}/foo-dog.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "canine" "type") +expect_missing("${content}" "version") + +expect_array("${content}" 1 "components" "canine" "requires") +expect_value("${content}" ":mammal" "components" "canine" "requires" 0) diff --git a/Tests/RunCMake/PackageInfo/Appendix.cmake b/Tests/RunCMake/PackageInfo/Appendix.cmake new file mode 100644 index 000000000..fe6777803 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Appendix.cmake @@ -0,0 +1,9 @@ +add_library(mammal INTERFACE) +add_library(canine INTERFACE) +target_link_libraries(canine INTERFACE mammal) + +install(TARGETS mammal EXPORT mammal DESTINATION .) +install(TARGETS canine EXPORT canine DESTINATION .) + +install(PACKAGE_INFO foo DESTINATION cps EXPORT mammal VERSION 1.0) +install(PACKAGE_INFO foo DESTINATION cps EXPORT canine APPENDIX dog) diff --git a/Tests/RunCMake/PackageInfo/Assertions.cmake b/Tests/RunCMake/PackageInfo/Assertions.cmake new file mode 100644 index 000000000..815761841 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Assertions.cmake @@ -0,0 +1,34 @@ +macro(_expect entity op actual expected) + if(NOT "${actual}" ${op} "${expected}") + list(JOIN ARGN "." name) + set(RunCMake_TEST_FAILED + "Attribute '${name}' ${entity} '${actual}' does not match expected ${entity} '${expected}'" PARENT_SCOPE) + return() + endif() +endmacro() + +function(expect_value content expected_value) + string(JSON actual_value GET "${content}" ${ARGN}) + _expect("value" STREQUAL "${actual_value}" "${expected_value}" ${ARGN}) +endfunction() + +function(expect_array content expected_length) + string(JSON actual_type TYPE "${content}" ${ARGN}) + _expect("type" STREQUAL "${actual_type}" "ARRAY" ${ARGN}) + + string(JSON actual_length LENGTH "${content}" ${ARGN}) + _expect("length" EQUAL "${actual_length}" "${expected_length}" ${ARGN}) +endfunction() + +function(expect_null content) + string(JSON actual_type TYPE "${content}" ${ARGN}) + _expect("type" STREQUAL "${actual_type}" "NULL" ${ARGN}) +endfunction() + +function(expect_missing content) + string(JSON value ERROR_VARIABLE error GET "${content}" ${ARGN}) + if(NOT value MATCHES "^(.*-)?NOTFOUND$") + set(RunCMake_TEST_FAILED + "Attribute '${ARGN}' is unexpectedly present" PARENT_SCOPE) + endif() +endfunction() diff --git a/Tests/RunCMake/PackageInfo/BadArgs1-result.txt b/Tests/RunCMake/PackageInfo/BadArgs1-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt new file mode 100644 index 000000000..92ba6fbf0 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs1.cmake:3 \(install\): + install COMPAT_VERSION requires VERSION. diff --git a/Tests/RunCMake/PackageInfo/BadArgs1.cmake b/Tests/RunCMake/PackageInfo/BadArgs1.cmake new file mode 100644 index 000000000..b99997c67 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs1.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo COMPAT_VERSION 1.0) diff --git a/Tests/RunCMake/PackageInfo/BadArgs2-result.txt b/Tests/RunCMake/PackageInfo/BadArgs2-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt new file mode 100644 index 000000000..636335ccd --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs2.cmake:3 \(install\): + install VERSION_SCHEMA requires VERSION. diff --git a/Tests/RunCMake/PackageInfo/BadArgs2.cmake b/Tests/RunCMake/PackageInfo/BadArgs2.cmake new file mode 100644 index 000000000..265d93c92 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs2.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo VERSION_SCHEMA simple) diff --git a/Tests/RunCMake/PackageInfo/BadArgs3-result.txt b/Tests/RunCMake/PackageInfo/BadArgs3-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt new file mode 100644 index 000000000..11e1a8c73 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs3.cmake:3 \(install\): + install APPENDIX and VERSION are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs3.cmake b/Tests/RunCMake/PackageInfo/BadArgs3.cmake new file mode 100644 index 000000000..5f57f6a38 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs3.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo APPENDIX test VERSION 1.0) diff --git a/Tests/RunCMake/PackageInfo/BadArgs4-result.txt b/Tests/RunCMake/PackageInfo/BadArgs4-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt new file mode 100644 index 000000000..067a07bac --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs4.cmake:3 \(install\): + install APPENDIX and DEFAULT_TARGETS are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs4.cmake b/Tests/RunCMake/PackageInfo/BadArgs4.cmake new file mode 100644 index 000000000..426d10b9b --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs4.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo APPENDIX test DEFAULT_TARGETS foo) diff --git a/Tests/RunCMake/PackageInfo/BadArgs5-result.txt b/Tests/RunCMake/PackageInfo/BadArgs5-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt b/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt new file mode 100644 index 000000000..4f7d28524 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at BadArgs5.cmake:3 \(install\): + install APPENDIX and DEFAULT_CONFIGURATIONS are mutually exclusive. diff --git a/Tests/RunCMake/PackageInfo/BadArgs5.cmake b/Tests/RunCMake/PackageInfo/BadArgs5.cmake new file mode 100644 index 000000000..356c856b8 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadArgs5.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO test EXPORT foo APPENDIX test DEFAULT_CONFIGURATIONS test) diff --git a/Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt b/Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt b/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt new file mode 100644 index 000000000..6467a14f3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget-stderr.txt @@ -0,0 +1,2 @@ +CMake Error: Package "test" specifies DEFAULT_TARGETS "dog", which is not a target in the export set "foo". +CMake Error: Package "test" specifies DEFAULT_TARGETS "cat", which is not a target in the export set "foo". diff --git a/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake b/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake new file mode 100644 index 000000000..d3d993a65 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/BadDefaultTarget.cmake @@ -0,0 +1,5 @@ +add_library(foo INTERFACE) +add_library(dog INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(TARGETS dog EXPORT dog DESTINATION .) +install(PACKAGE_INFO test EXPORT foo DEFAULT_TARGETS dog cat) diff --git a/Tests/RunCMake/PackageInfo/CMakeLists.txt b/Tests/RunCMake/PackageInfo/CMakeLists.txt new file mode 100644 index 000000000..dda37d8bc --- /dev/null +++ b/Tests/RunCMake/PackageInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt b/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt b/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt new file mode 100644 index 000000000..40799b71c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at ExperimentalGate.cmake:5 \(install\): + install does not recognize sub-command PACKAGE_INFO diff --git a/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake b/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake new file mode 100644 index 000000000..327d3bb25 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalGate.cmake @@ -0,0 +1,5 @@ +unset(CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt b/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt new file mode 100644 index 000000000..960d54114 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalWarning-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at ExperimentalWarning.cmake:8 \(install\): + CMake's support for exporting package information in the Common Package + Specification format. 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. diff --git a/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake b/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake new file mode 100644 index 000000000..df6604cd6 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ExperimentalWarning.cmake @@ -0,0 +1,8 @@ +set( + CMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO + "b80be207-778e-46ba-8080-b23bba22639e" + ) + +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake b/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake new file mode 100644 index 000000000..2c3272ec3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/InterfaceProperties-check.cmake @@ -0,0 +1,24 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/InterfaceProperties-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") + +string(JSON component GET "${content}" "components" "foo") + +expect_value("${component}" "interface" "type") +expect_array("${component}" 1 "includes") +expect_value("${component}" "@prefix@/include/foo" "includes" 0) +expect_array("${component}" 1 "compile_features") +expect_value("${component}" "c++23" "compile_features" 0) +expect_array("${component}" 1 "compile_flags") +expect_value("${component}" "-ffast-math" "compile_flags" 0) +expect_null("${component}" "compile_definitions" "*" "FOO") +expect_value("${component}" "BAR" "compile_definitions" "*" "BAR") +expect_array("${component}" 1 "link_directories") +expect_value("${component}" "/opt/foo/lib" "link_directories" 0) +expect_array("${component}" 1 "link_flags") +expect_value("${component}" "--needed" "link_flags" 0) +expect_array("${component}" 1 "link_libraries") +expect_value("${component}" "/usr/lib/libm.so" "link_libraries" 0) diff --git a/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake b/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake new file mode 100644 index 000000000..42edc21ff --- /dev/null +++ b/Tests/RunCMake/PackageInfo/InterfaceProperties.cmake @@ -0,0 +1,15 @@ +add_library(foo INTERFACE) + +target_compile_features(foo INTERFACE cxx_std_23) +target_compile_options(foo INTERFACE -ffast-math) +target_compile_definitions(foo INTERFACE -DFOO -DBAR=BAR) +target_include_directories( + foo INTERFACE + $ + ) +target_link_directories(foo INTERFACE /opt/foo/lib) +target_link_options(foo INTERFACE --needed) +target_link_libraries(foo INTERFACE /usr/lib/libm.so) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake b/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake new file mode 100644 index 000000000..d8de37203 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/LowerCaseFile-check.cmake @@ -0,0 +1,9 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/LowerCaseFile-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/lowercase.cps" content) +expect_value("${content}" "LowerCase" "name") + +file(READ "${out_dir}/PreserveCase.cps" content) +expect_value("${content}" "PreserveCase" "name") diff --git a/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake b/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake new file mode 100644 index 000000000..dc6827fa3 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/LowerCaseFile.cmake @@ -0,0 +1,4 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO LowerCase DESTINATION cps EXPORT foo LOWER_CASE_FILE) +install(PACKAGE_INFO PreserveCase DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/Metadata-check.cmake b/Tests/RunCMake/PackageInfo/Metadata-check.cmake new file mode 100644 index 000000000..8db8c298a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Metadata-check.cmake @@ -0,0 +1,16 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Metadata-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "1.2.3" "version") +expect_value("${content}" "1.2.0" "compat_version") +expect_value("${content}" "simple" "version_schema") + +expect_array("${content}" 1 "default_components") +expect_value("${content}" "foo" "default_components" 0) + +expect_array("${content}" 2 "configurations") +expect_value("${content}" "release" "configurations" 0) +expect_value("${content}" "debug" "configurations" 1) diff --git a/Tests/RunCMake/PackageInfo/Metadata.cmake b/Tests/RunCMake/PackageInfo/Metadata.cmake new file mode 100644 index 000000000..f8fc9b873 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Metadata.cmake @@ -0,0 +1,12 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install( + PACKAGE_INFO foo + DESTINATION cps + EXPORT foo + VERSION 1.2.3 + VERSION_SCHEMA simple + COMPAT_VERSION 1.2.0 + DEFAULT_TARGETS foo + DEFAULT_CONFIGURATIONS release debug + ) diff --git a/Tests/RunCMake/PackageInfo/Minimal-check.cmake b/Tests/RunCMake/PackageInfo/Minimal-check.cmake new file mode 100644 index 000000000..9608ed8fa --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Minimal-check.cmake @@ -0,0 +1,18 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Minimal-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "foo" "type") +expect_missing("${content}" "version") +expect_missing("${content}" "configurations") +expect_missing("${content}" "default_targets") +expect_missing("${content}" "components" "foo" "compile_definitions") +expect_missing("${content}" "components" "foo" "compile_features") +expect_missing("${content}" "components" "foo" "compile_flags") +expect_missing("${content}" "components" "foo" "link_directories") +expect_missing("${content}" "components" "foo" "link_features") +expect_missing("${content}" "components" "foo" "link_flags") +expect_missing("${content}" "components" "foo" "link_libraries") +expect_missing("${content}" "components" "foo" "requires") diff --git a/Tests/RunCMake/PackageInfo/Minimal.cmake b/Tests/RunCMake/PackageInfo/Minimal.cmake new file mode 100644 index 000000000..6c060b91c --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Minimal.cmake @@ -0,0 +1,3 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/MinimalVersion-check.cmake b/Tests/RunCMake/PackageInfo/MinimalVersion-check.cmake new file mode 100644 index 000000000..8facefa57 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/MinimalVersion-check.cmake @@ -0,0 +1,21 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/MinimalVersion-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo1.cps" content) +expect_value("${content}" "foo1" "name") +expect_value("${content}" "1.0" "version") +expect_missing("${content}" "compat_version") +expect_missing("${content}" "version_schema") + +file(READ "${out_dir}/foo2.cps" content) +expect_value("${content}" "foo2" "name") +expect_value("${content}" "1.5" "version") +expect_value("${content}" "1.0" "compat_version") +expect_missing("${content}" "version_schema") + +file(READ "${out_dir}/foo3.cps" content) +expect_value("${content}" "foo3" "name") +expect_value("${content}" "1.0" "version") +expect_missing("${content}" "compat_version") +expect_value("${content}" "simple" "version_schema") diff --git a/Tests/RunCMake/PackageInfo/MinimalVersion.cmake b/Tests/RunCMake/PackageInfo/MinimalVersion.cmake new file mode 100644 index 000000000..ea4679c4a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/MinimalVersion.cmake @@ -0,0 +1,19 @@ +add_library(foo INTERFACE) +install(TARGETS foo EXPORT foo DESTINATION .) + +install(PACKAGE_INFO foo1 + EXPORT foo + VERSION 1.0 + DESTINATION cps) + +install(PACKAGE_INFO foo2 + EXPORT foo + VERSION 1.5 + COMPAT_VERSION 1.0 + DESTINATION cps) + +install(PACKAGE_INFO foo3 + EXPORT foo + VERSION 1.0 + VERSION_SCHEMA simple + DESTINATION cps) diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt new file mode 100644 index 000000000..c68d51882 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget-stderr.txt @@ -0,0 +1 @@ +CMake Error: install\(PACKAGE_INFO "dog" \.\.\.\) includes target "canine" which requires target "mammal" that is not in any export set. diff --git a/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake new file mode 100644 index 000000000..a83558262 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesNonExportedTarget.cmake @@ -0,0 +1,6 @@ +add_library(mammal INTERFACE) +add_library(canine INTERFACE) +target_link_libraries(canine INTERFACE mammal) + +install(TARGETS canine EXPORT dog DESTINATION .) +install(PACKAGE_INFO dog EXPORT dog) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt new file mode 100644 index 000000000..0a74e185a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget-stderr.txt @@ -0,0 +1,7 @@ +CMake Error in CMakeLists.txt: + Target "test" references target "foo", which does not use the standard + namespace separator. This is not allowed. +.* +CMake Error in CMakeLists.txt: + Target "test" references target "bar_bar", which does not use the standard + namespace separator. This is not allowed. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake new file mode 100644 index 000000000..3e3d21d23 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyExportedTarget.cmake @@ -0,0 +1,14 @@ +add_library(foo INTERFACE) +add_library(bar INTERFACE) + +add_library(test INTERFACE) +target_link_libraries(test INTERFACE foo bar) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(TARGETS bar EXPORT bar DESTINATION .) + +install(EXPORT foo DESTINATION .) +install(EXPORT bar DESTINATION . NAMESPACE bar_) + +install(TARGETS test EXPORT test DESTINATION .) +install(PACKAGE_INFO test EXPORT test) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt new file mode 100644 index 000000000..cc4e824d8 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget-stderr.txt @@ -0,0 +1,3 @@ +CMake Error in CMakeLists.txt: + Target "foo" references imported target "bar" which does not come from any + known package. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake new file mode 100644 index 000000000..8addd64c2 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyImportedTarget.cmake @@ -0,0 +1,7 @@ +add_library(bar INTERFACE IMPORTED) + +add_library(foo INTERFACE) +target_link_libraries(foo INTERFACE bar) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt new file mode 100644 index 000000000..0a6872e32 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget-stderr.txt @@ -0,0 +1,4 @@ +CMake Error in CMakeLists.txt: + Target "foo" references target "wrong::lib", which comes from the "broken" + package, but does not belong to the package's canonical namespace. This is + not allowed. diff --git a/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake new file mode 100644 index 000000000..7a4f9c015 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/ReferencesWronglyNamespacedTarget.cmake @@ -0,0 +1,11 @@ +find_package( + broken REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} + ) + +add_library(foo INTERFACE) +target_link_libraries(foo INTERFACE wrong::lib) + +install(TARGETS foo EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/Requirements-check.cmake b/Tests/RunCMake/PackageInfo/Requirements-check.cmake new file mode 100644 index 000000000..59a212f64 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Requirements-check.cmake @@ -0,0 +1,20 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/Requirements-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "interface" "components" "libb" "type") + +file(READ "${out_dir}/bar.cps" content) +expect_value("${content}" "bar" "name") +expect_null("${content}" "requires" "foo") +expect_null("${content}" "requires" "test") +expect_value("${content}" "interface" "components" "libc" "type") +expect_value("${content}" "interface" "components" "libd" "type") + +string(JSON component GET "${content}" "components" "libd") +expect_array("${component}" 3 "requires") +expect_value("${component}" "test:liba" "requires" 0) +expect_value("${component}" "foo:libb" "requires" 1) +expect_value("${component}" ":libc" "requires" 2) diff --git a/Tests/RunCMake/PackageInfo/Requirements.cmake b/Tests/RunCMake/PackageInfo/Requirements.cmake new file mode 100644 index 000000000..a4e947c52 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/Requirements.cmake @@ -0,0 +1,20 @@ +find_package( + test REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} + ) + +add_library(libb INTERFACE) +add_library(libc INTERFACE) +add_library(libd INTERFACE) + +add_library(foo ALIAS libb) +add_library(bar ALIAS libc) + +target_link_libraries(libd INTERFACE test::liba foo bar) + +install(TARGETS libb EXPORT foo DESTINATION .) +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) + +install(TARGETS libc libd EXPORT bar DESTINATION .) +install(PACKAGE_INFO bar DESTINATION cps EXPORT bar) diff --git a/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake b/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake new file mode 100644 index 000000000..e90c37112 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/RunCMakeTest.cmake @@ -0,0 +1,33 @@ +include(RunCMake) + +# Test experimental gate +run_cmake(ExperimentalGate) +run_cmake(ExperimentalWarning) + +# Enable experimental feature and suppress warnings +set(RunCMake_TEST_OPTIONS + -Wno-dev + "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e" + ) + +# Test incorrect usage +run_cmake(BadArgs1) +run_cmake(BadArgs2) +run_cmake(BadArgs3) +run_cmake(BadArgs4) +run_cmake(BadArgs5) +run_cmake(BadDefaultTarget) +run_cmake(ReferencesNonExportedTarget) +run_cmake(ReferencesWronglyExportedTarget) +run_cmake(ReferencesWronglyImportedTarget) +run_cmake(ReferencesWronglyNamespacedTarget) + +# Test functionality +run_cmake(Appendix) +run_cmake(InterfaceProperties) +run_cmake(Metadata) +run_cmake(Minimal) +run_cmake(MinimalVersion) +run_cmake(LowerCaseFile) +run_cmake(Requirements) +run_cmake(TargetTypes) diff --git a/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake b/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake new file mode 100644 index 000000000..34ca5abc5 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/TargetTypes-check.cmake @@ -0,0 +1,11 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(out_dir "${RunCMake_BINARY_DIR}/TargetTypes-build/CMakeFiles/Export/510c5684a4a8a792eadfb55bc9744983") + +file(READ "${out_dir}/foo.cps" content) +expect_value("${content}" "foo" "name") +expect_value("${content}" "archive" "components" "foo-static" "type") +expect_value("${content}" "dylib" "components" "foo-shared" "type") +expect_value("${content}" "module" "components" "foo-module" "type") +expect_value("${content}" "interface" "components" "bar" "type") +expect_value("${content}" "executable" "components" "test" "type") diff --git a/Tests/RunCMake/PackageInfo/TargetTypes.cmake b/Tests/RunCMake/PackageInfo/TargetTypes.cmake new file mode 100644 index 000000000..755c3a36a --- /dev/null +++ b/Tests/RunCMake/PackageInfo/TargetTypes.cmake @@ -0,0 +1,20 @@ +project(TargetTypes CXX) + +add_library(foo-static STATIC foo.cxx) +add_library(foo-shared SHARED foo.cxx) +add_library(foo-module MODULE foo.cxx) +add_library(bar INTERFACE) +add_executable(test test.cxx) + +install( + TARGETS + foo-static + foo-shared + foo-module + bar + test + EXPORT foo + DESTINATION . + ) + +install(PACKAGE_INFO foo DESTINATION cps EXPORT foo) diff --git a/Tests/RunCMake/PackageInfo/broken-config.cmake b/Tests/RunCMake/PackageInfo/broken-config.cmake new file mode 100644 index 000000000..09e40df24 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/broken-config.cmake @@ -0,0 +1 @@ +add_library(wrong::lib INTERFACE IMPORTED) diff --git a/Tests/RunCMake/VS10ProjectWinCE/foo.cpp b/Tests/RunCMake/PackageInfo/foo.cxx similarity index 100% rename from Tests/RunCMake/VS10ProjectWinCE/foo.cpp rename to Tests/RunCMake/PackageInfo/foo.cxx diff --git a/Tests/RunCMake/PackageInfo/test-config.cmake b/Tests/RunCMake/PackageInfo/test-config.cmake new file mode 100644 index 000000000..3c46ea684 --- /dev/null +++ b/Tests/RunCMake/PackageInfo/test-config.cmake @@ -0,0 +1 @@ +add_library(test::liba INTERFACE IMPORTED) diff --git a/Tests/RunCMake/Autogen/exe.cpp b/Tests/RunCMake/PackageInfo/test.cxx similarity index 100% rename from Tests/RunCMake/Autogen/exe.cpp rename to Tests/RunCMake/PackageInfo/test.cxx diff --git a/Tests/RunCMake/ParseImplicitData/CMakeLists.txt b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt index 6ba691347..a9d5fdd5a 100644 --- a/Tests/RunCMake/ParseImplicitData/CMakeLists.txt +++ b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt @@ -11,7 +11,7 @@ # -DUNAME="Darwin" -- operating system name (def: CMAKE_SYSTEM_NAME) # -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0089) cmake_policy(SET CMP0089 NEW) endif() diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-13.3.0-static-libquadmath.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-13.3.0-static-libquadmath.input new file mode 100644 index 000000000..b1ab2f049 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-13.3.0-static-libquadmath.input @@ -0,0 +1,80 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER=/usr/bin/ld +CMAKE_Fortran_COMPILER_ABI= +CMAKE_Fortran_COMPILER_AR=/usr/bin/gcc-ar-13 +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID= +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=GNU +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB=/usr/bin/gcc-ranlib-13 +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=13.3.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +CMAKE_Fortran_IMPLICIT_LINK_LIBRARY_quadmath=/usr/lib/gcc/x86_64-linux-gnu/13/libquadmath.a +Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp' + +Run Build Command(s): /tmp/CMake/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_9b1c8/fast +/usr/bin/gmake -f CMakeFiles/cmTC_9b1c8.dir/build.make CMakeFiles/cmTC_9b1c8.dir/build +gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp' +Building Fortran object CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o +/usr/bin/gfortran -static-libquadmath -v -c /tmp/CMake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o +Using built-in specs. +COLLECT_GCC=/usr/bin/gfortran +OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Debian 13.3.0-1' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3 +Thread model: posix +Supported LTO compression algorithms: zlib zstd +gcc version 13.3.0 (Debian 13.3.0-1) +COLLECT_GCC_OPTIONS='-static-libquadmath' '-v' '-c' '-o' 'CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9b1c8.dir/' + /usr/libexec/gcc/x86_64-linux-gnu/13/f951 /tmp/CMake/Modules/CMakeFortranCompilerABI.F -ffixed-form -cpp=/tmp/ccPxd9HW.f90 -quiet -v -imultiarch x86_64-linux-gnu /tmp/CMake/Modules/CMakeFortranCompilerABI.F -quiet -dumpdir CMakeFiles/cmTC_9b1c8.dir/ -dumpbase CMakeFortranCompilerABI.F.F -dumpbase-ext .F -mtune=generic -march=x86-64 -version -fintrinsic-modules-path /usr/lib/gcc/x86_64-linux-gnu/13/finclude -fpre-include=/usr/include/finclude/x86_64-linux-gnu/math-vector-fortran.h -o /tmp/ccWH03h1.s +GNU Fortran (Debian 13.3.0-1) version 13.3.0 (x86_64-linux-gnu) + compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP + +GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 +ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include" +#include "..." search starts here: +#include <...> search starts here: + /usr/lib/gcc/x86_64-linux-gnu/13/finclude + /usr/lib/gcc/x86_64-linux-gnu/13/include + /usr/local/include + /usr/include/x86_64-linux-gnu + /usr/include +End of search list. +COLLECT_GCC_OPTIONS='-static-libquadmath' '-v' '-c' '-o' 'CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9b1c8.dir/' + as -v --64 -o CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o /tmp/ccWH03h1.s +GNU assembler version 2.42.50 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.42.50.20240625 +COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-static-libquadmath' '-v' '-c' '-o' 'CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.' +Linking Fortran executable cmTC_9b1c8 +/tmp/CMake/bin/cmake -E cmake_link_script CMakeFiles/cmTC_9b1c8.dir/link.txt --verbose=1 +/usr/bin/gfortran -v -Wl,-v -static-libquadmath CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o -o cmTC_9b1c8 +Driving: /usr/bin/gfortran -v -Wl,-v -static-libquadmath CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o -o cmTC_9b1c8 -l gfortran -l m -shared-libgcc +Using built-in specs. +COLLECT_GCC=/usr/bin/gfortran +COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper +OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Debian 13.3.0-1' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=3 +Thread model: posix +Supported LTO compression algorithms: zlib zstd +gcc version 13.3.0 (Debian 13.3.0-1) +Reading specs from /usr/lib/gcc/x86_64-linux-gnu/13/libgfortran.spec +rename spec lib to liborig +COLLECT_GCC_OPTIONS='-v' '-static-libquadmath' '-o' 'cmTC_9b1c8' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_9b1c8-' +COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-v' '-static-libquadmath' '-o' 'cmTC_9b1c8' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_9b1c8.' + /usr/libexec/gcc/x86_64-linux-gnu/13/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccBedNT7.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_9b1c8 /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. -v CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc --as-needed -Bstatic -lquadmath -Bdynamic --no-as-needed -lm -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o +collect2 version 13.3.0 +/usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccBedNT7.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_9b1c8 /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. -v CMakeFiles/cmTC_9b1c8.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc --as-needed -Bstatic -lquadmath -Bdynamic --no-as-needed -lm -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o +GNU ld (GNU Binutils for Debian) 2.42.50.20240625 +COLLECT_GCC_OPTIONS='-v' '-static-libquadmath' '-o' 'cmTC_9b1c8' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_9b1c8.' +gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp' diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-clang.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-clang.input new file mode 100644 index 000000000..f31460893 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-clang.input @@ -0,0 +1,25 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER=/usr/bin/ld +CMAKE_Fortran_COMPILER_ABI=ELF +CMAKE_Fortran_COMPILER_AR= +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID= +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=LFortran +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB= +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=0.35.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp' + +Run Build Command(s): /tmp/CMake/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_48bf9/fast +/usr/bin/gmake -f CMakeFiles/cmTC_48bf9.dir/build.make CMakeFiles/cmTC_48bf9.dir/build +gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp' +Building Fortran object CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o +/usr/bin/lfortran --cpp-infer -v --generate-object-code -c /tmp/CMake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o +Linking Fortran executable cmTC_48bf9 +/tmp/CMake/bin/cmake -E cmake_link_script CMakeFiles/cmTC_48bf9.dir/link.txt --verbose=1 +/usr/bin/lfortran -v -Wl,-export-dynamic CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o -o cmTC_48bf9 +clang -o cmTC_48bf9 CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o -L"/usr/bin/../lib64" -Wl,-rpath,"/usr/bin/../lib64" -Wl,-export-dynamic -llfortran_runtime -lm +gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp' diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-gcc.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-gcc.input new file mode 100644 index 000000000..7879a3a7c --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.35.0-gcc.input @@ -0,0 +1,25 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER=/usr/bin/ld +CMAKE_Fortran_COMPILER_ABI=ELF +CMAKE_Fortran_COMPILER_AR= +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID= +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=LFortran +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB= +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=0.35.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp' + +Run Build Command(s): /tmp/CMake/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_48bf9/fast +/usr/bin/gmake -f CMakeFiles/cmTC_48bf9.dir/build.make CMakeFiles/cmTC_48bf9.dir/build +gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp' +Building Fortran object CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o +/usr/bin/lfortran --cpp-infer --link-with-gcc -v --generate-object-code -c /tmp/CMake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o +Linking Fortran executable cmTC_48bf9 +/tmp/CMake/bin/cmake -E cmake_link_script CMakeFiles/cmTC_48bf9.dir/link.txt --verbose=1 +/usr/bin/lfortran -v -Wl,-export-dynamic --link-with-gcc CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o -o cmTC_48bf9 +gcc -o cmTC_48bf9 CMakeFiles/cmTC_48bf9.dir/CMakeFortranCompilerABI.F.o -L"/usr/bin/../lib64" -Wl,-rpath,"/usr/bin/../lib64" -Wl,-export-dynamic -llfortran_runtime -lm +gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp' diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-clang.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-clang.input new file mode 100644 index 000000000..7f84f4486 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-clang.input @@ -0,0 +1,38 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER=/usr/bin/ld +CMAKE_Fortran_COMPILER_ABI=ELF +CMAKE_Fortran_COMPILER_AR= +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID= +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=LFortran +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB= +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=0.41.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp' + +Run Build Command(s): /tmp/CMake/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_e3038/fast +/usr/bin/gmake -f CMakeFiles/cmTC_e3038.dir/build.make CMakeFiles/cmTC_e3038.dir/build +gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp' +Building Fortran object CMakeFiles/cmTC_e3038.dir/CMakeFortranCompilerABI.F.o +/usr/bin/lfortran --cpp-infer -v -Wl,-v --generate-object-code -c /tmp/CMake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_e3038.dir/CMakeFortranCompilerABI.F.o +Linking Fortran executable cmTC_e3038 +/tmp/CMake/bin/cmake -E cmake_link_script CMakeFiles/cmTC_e3038.dir/link.txt --verbose=1 +clang -o cmTC_e3038 CMakeFiles/cmTC_e3038.dir/CMakeFortranCompilerABI.F.o -L"/usr/bin/../lib64" -Wl,-rpath,"/usr/bin/../lib64" -Wl,-v -llfortran_runtime -lm -v +clang version 19.1.0 (Fedora 19.1.0-1.fc41) +Target: x86_64-redhat-linux-gnu +Thread model: posix +InstalledDir: /usr/bin +Configuration file: /etc/clang/x86_64-redhat-linux-gnu-clang.cfg +System configuration file directory: /etc/clang/ +Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/14 +Selected GCC installation: /usr/bin/../lib/gcc/x86_64-redhat-linux/14 +Candidate multilib: .@m64 +Candidate multilib: 32@m32 +Selected multilib: .@m64 + "/usr/bin/ld" --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_e3038 /usr/bin/../lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o /usr/bin/../lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crti.o /usr/bin/../lib/gcc/x86_64-redhat-linux/14/crtbegin.o -L/usr/bin/../lib64 -L/usr/bin/../lib/clang/19/lib/x86_64-redhat-linux-gnu -L/usr/bin/../lib/gcc/x86_64-redhat-linux/14 -L/usr/bin/../lib/gcc/x86_64-redhat-linux/14/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/lib -L/usr/lib CMakeFiles/cmTC_e3038.dir/CMakeFortranCompilerABI.F.o -rpath /usr/bin/../lib64 -v -llfortran_runtime -lm -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/../lib/gcc/x86_64-redhat-linux/14/crtend.o /usr/bin/../lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crtn.o +GNU ld version 2.43.1-2.fc41 +/usr/bin/lfortran -v -Wl,-v CMakeFiles/cmTC_e3038.dir/CMakeFortranCompilerABI.F.o -o cmTC_e3038 +gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp' diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-gcc.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-gcc.input new file mode 100644 index 000000000..4a7926252 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-LFortran-0.41.0-gcc.input @@ -0,0 +1,43 @@ +CMAKE_LANG=Fortran +CMAKE_LINKER=/usr/bin/ld +CMAKE_Fortran_COMPILER_ABI=ELF +CMAKE_Fortran_COMPILER_AR= +CMAKE_Fortran_COMPILER_ARCHITECTURE_ID= +CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN= +CMAKE_Fortran_COMPILER_ID=LFortran +CMAKE_Fortran_COMPILER_LAUNCHER= +CMAKE_Fortran_COMPILER_LOADED=1 +CMAKE_Fortran_COMPILER_RANLIB= +CMAKE_Fortran_COMPILER_TARGET= +CMAKE_Fortran_COMPILER_VERSION=0.41.0 +CMAKE_Fortran_COMPILER_VERSION_INTERAL= +Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp' + +Run Build Command(s): /tmp/CMake/bin/cmake -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_fdc77/fast +/usr/bin/gmake -f CMakeFiles/cmTC_fdc77.dir/build.make CMakeFiles/cmTC_fdc77.dir/build +gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp' +Building Fortran object CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o +/usr/bin/lfortran --cpp-infer --link-with-gcc -v -Wl,-v --generate-object-code -c /tmp/CMake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o +Linking Fortran executable cmTC_fdc77 +/tmp/CMake/bin/cmake -E cmake_link_script CMakeFiles/cmTC_fdc77.dir/link.txt --verbose=1 +gcc -o cmTC_fdc77 CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o -L"/usr/bin/../lib64" -Wl,-rpath,"/usr/bin/../lib64" -Wl,-v -llfortran_runtime -lm -v +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/14/lto-wrapper +OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-redhat-linux +Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-14.2.1-build/gcc-14.2.1-20240912/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 +Thread model: posix +Supported LTO compression algorithms: zlib zstd +gcc version 14.2.1 20240912 (Red Hat 14.2.1-3) (GCC) +COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/14/:/usr/libexec/gcc/x86_64-redhat-linux/14/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/14/:/usr/lib/gcc/x86_64-redhat-linux/ +LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/14/:/usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/14/../../../:/lib/:/usr/lib/ +COLLECT_GCC_OPTIONS='-o' 'cmTC_fdc77' '-L/usr/bin/../lib64' '-foffload-options=-l_GCC_m' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_fdc77.' + /usr/libexec/gcc/x86_64-redhat-linux/14/collect2 -plugin /usr/libexec/gcc/x86_64-redhat-linux/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccg4ypde.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_fdc77 /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/14/crtbegin.o -L/usr/bin/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/14 -L/usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/14/../../.. CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o -rpath /usr/bin/../lib64 -v -llfortran_runtime -lm -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-redhat-linux/14/crtend.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crtn.o +collect2 version 14.2.1 20240912 (Red Hat 14.2.1-3) +/usr/bin/ld -plugin /usr/libexec/gcc/x86_64-redhat-linux/14/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-redhat-linux/14/lto-wrapper -plugin-opt=-fresolution=/tmp/ccg4ypde.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_fdc77 /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/14/crtbegin.o -L/usr/bin/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/14 -L/usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/14/../../.. CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o -rpath /usr/bin/../lib64 -v -llfortran_runtime -lm -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-redhat-linux/14/crtend.o /usr/lib/gcc/x86_64-redhat-linux/14/../../../../lib64/crtn.o +GNU ld version 2.43.1-2.fc41 +COLLECT_GCC_OPTIONS='-o' 'cmTC_fdc77' '-L/usr/bin/../lib64' '-foffload-options=-l_GCC_m' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_fdc77.' +/usr/bin/lfortran -v -Wl,-v --link-with-gcc CMakeFiles/cmTC_fdc77.dir/CMakeFortranCompilerABI.F.o -o cmTC_fdc77 +gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp' diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake index c5bb5d7bb..c58722d6e 100644 --- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake +++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake @@ -24,6 +24,7 @@ set(targets linux-C-GNU-10.2.1-static-libgcc linux-CXX-GNU-10.2.1-static-libstdc++ linux-Fortran-GNU-10.2.1-static-libgfortran + linux-Fortran-GNU-13.3.0-static-libquadmath linux-C-GNU-12.2.0 linux-CXX-GNU-12.2.0 linux-Fortran-GNU-12.2.0 linux-C-Intel-18.0.0.20170811 linux-CXX-Intel-18.0.0.20170811 linux-C-Intel-2021.10.0.20230609 linux-CXX-Intel-2021.10.0.20230609 linux-Fortran-Intel-2021.10.0.20230609 diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output new file mode 100644 index 000000000..defc3750c --- /dev/null +++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output @@ -0,0 +1 @@ +/usr/lib/gcc/x86_64-linux-gnu/13/finclude;/usr/lib/gcc/x86_64-linux-gnu/13/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake index 8d6c73938..5b4b8d88e 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake @@ -24,6 +24,7 @@ set(targets linux-C-GNU-10.2.1-static-libgcc linux-CXX-GNU-10.2.1-static-libstdc++ linux-Fortran-GNU-10.2.1-static-libgfortran + linux-Fortran-GNU-13.3.0-static-libquadmath linux-C-GNU-12.2.0 linux-CXX-GNU-12.2.0 linux-Fortran-GNU-12.2.0 linux-C-Intel-18.0.0.20170811 linux-CXX-Intel-18.0.0.20170811 linux-C-Intel-2021.10.0.20230609 linux-CXX-Intel-2021.10.0.20230609 linux-Fortran-Intel-2021.10.0.20230609 @@ -40,6 +41,10 @@ set(targets linux-CUDA-NVIDIA-10.1.168-CLANG linux-CUDA-NVIDIA-10.1.168-XLClang-v linux-CUDA-NVIDIA-9.2.148-GCC linux-Fortran-LLVMFlang-15.0.0 + linux-Fortran-LFortran-0.35.0-clang + linux-Fortran-LFortran-0.35.0-gcc + linux-Fortran-LFortran-0.41.0-clang + linux-Fortran-LFortran-0.41.0-gcc linux-custom_clang-C-Clang-13.0.0 linux-custom_clang-CXX-Clang-13.0.0 mingw.org-C-GNU-4.9.3 mingw.org-CXX-GNU-4.9.3 netbsd-C-GNU-4.8.5 netbsd-CXX-GNU-4.8.5 diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output new file mode 100644 index 000000000..159556090 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-13.3.0-static-libquadmath.output @@ -0,0 +1,4 @@ +libs=gfortran;m;gcc_s;gcc;/usr/lib/gcc/x86_64-linux-gnu/13/libquadmath.a;m;c;gcc_s;gcc +dirs=/usr/lib/gcc/x86_64-linux-gnu/13;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib +library_arch=x86_64-linux-gnu +linker_tool=/usr/bin/ld diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-clang.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-clang.output new file mode 100644 index 000000000..859e33027 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-clang.output @@ -0,0 +1,3 @@ +libs=lfortran_runtime;m +dirs=/usr/lib64 +linker_tool= diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-gcc.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-gcc.output new file mode 100644 index 000000000..859e33027 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.35.0-gcc.output @@ -0,0 +1,3 @@ +libs=lfortran_runtime;m +dirs=/usr/lib64 +linker_tool= diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-clang.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-clang.output new file mode 100644 index 000000000..e0af3e3c1 --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-clang.output @@ -0,0 +1,4 @@ +libs=lfortran_runtime;m;gcc;gcc_s;c;gcc;gcc_s +dirs=/usr/lib64;/usr/lib/clang/19/lib/x86_64-redhat-linux-gnu;/usr/lib/gcc/x86_64-redhat-linux/14;/lib64;/lib;/usr/lib +library_arch=x86_64-redhat-linux-gnu +linker_tool=/usr/bin/ld diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-gcc.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-gcc.output new file mode 100644 index 000000000..f1b19936f --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LFortran-0.41.0-gcc.output @@ -0,0 +1,4 @@ +libs=lfortran_runtime;m;gcc;gcc_s;c;gcc;gcc_s +dirs=/usr/lib64;/usr/lib/gcc/x86_64-redhat-linux/14;/lib64;/usr/lib +library_arch= +linker_tool=/usr/bin/ld diff --git a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt index 42b057706..bfbc37261 100644 --- a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt +++ b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) # MSVC creates extra targets which pollute the stderr unless we set this. diff --git a/Tests/RunCMake/README.rst b/Tests/RunCMake/README.rst index ea6db54d5..f4bcc572c 100644 --- a/Tests/RunCMake/README.rst +++ b/Tests/RunCMake/README.rst @@ -6,9 +6,11 @@ precisely checking their return code and stdout/stderr content. The RunCMake infrastructure is useful for testing error cases and diagnostic output. -See also `../README.rst`_ and the `CMake Source Code Guide`_. +See also `../README.rst`_, the `CMake Testing Guide`_, +and the `CMake Source Code Guide`_. .. _`../README.rst`: ../README.rst +.. _`CMake Testing Guide`: ../../Help/dev/testing.rst .. _`CMake Source Code Guide`: ../../Help/dev/source.rst .. _`CMakeLists.txt`: CMakeLists.txt diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index ea3099ff9..bf124ddcc 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -224,7 +224,7 @@ function(run_cmake test) string(REGEX REPLACE [[ ^CMake Deprecation Warning at [^ ]*CMakeLists.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt b/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt index affd4d4f7..50d9b43af 100644 --- a/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt +++ b/Tests/RunCMake/RunCPack/AppWiX/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CPackWiXGenerator) diff --git a/Tests/RunCMake/RuntimePath/CMakeLists.txt b/Tests/RunCMake/RuntimePath/CMakeLists.txt index a640c569e..bf2ef1506 100644 --- a/Tests/RunCMake/RuntimePath/CMakeLists.txt +++ b/Tests/RunCMake/RuntimePath/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/SourceProperties/CMakeLists.txt b/Tests/RunCMake/SourceProperties/CMakeLists.txt index e93f0b69e..d1b0d2c3e 100644 --- a/Tests/RunCMake/SourceProperties/CMakeLists.txt +++ b/Tests/RunCMake/SourceProperties/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/StandardLinkDirectories/CMakeLists.txt b/Tests/RunCMake/StandardLinkDirectories/CMakeLists.txt new file mode 100644 index 000000000..ccf88ee43 --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} LANGUAGES C) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/StandardLinkDirectories/RunCMakeTest.cmake b/Tests/RunCMake/StandardLinkDirectories/RunCMakeTest.cmake new file mode 100644 index 000000000..6760ffc84 --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/RunCMakeTest.cmake @@ -0,0 +1,22 @@ +include(RunCMake) + +# Link should succeed +block() + set(libdir ${RunCMake_BINARY_DIR}/TestLib-build/TestLib/lib) + run_cmake(TestLib) + run_cmake_with_options(TestApp "-DCMAKE_C_STANDARD_LINK_DIRECTORIES=${libdir}") + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(TestLib ${CMAKE_COMMAND} --build .) + run_cmake_command(TestAppGood ${CMAKE_COMMAND} --build ../TestApp-build) +endblock() + +# Link should fail +block() + run_cmake(TestLib) + run_cmake(TestApp) + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(TestLib ${CMAKE_COMMAND} --build .) + run_cmake_command(TestAppBad ${CMAKE_COMMAND} --build ../TestApp-build) +endblock() diff --git a/Tests/RunCMake/StandardLinkDirectories/TestApp.cmake b/Tests/RunCMake/StandardLinkDirectories/TestApp.cmake new file mode 100644 index 000000000..53a9c9a6f --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestApp.cmake @@ -0,0 +1 @@ +add_subdirectory(TestApp) diff --git a/Tests/RunCMake/StandardLinkDirectories/TestApp/CMakeLists.txt b/Tests/RunCMake/StandardLinkDirectories/TestApp/CMakeLists.txt new file mode 100644 index 000000000..92dd52fa3 --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestApp/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(TestApp TestApp.c) +target_link_libraries(TestApp PRIVATE SLD) diff --git a/Tests/RunCMake/StandardLinkDirectories/TestApp/TestApp.c b/Tests/RunCMake/StandardLinkDirectories/TestApp/TestApp.c new file mode 100644 index 000000000..66630d8ed --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestApp/TestApp.c @@ -0,0 +1,6 @@ +int TestSymbol(void); + +int main(void) +{ + return TestSymbol(); +} diff --git a/Tests/RunCMake/StandardLinkDirectories/TestAppBad-result.txt b/Tests/RunCMake/StandardLinkDirectories/TestAppBad-result.txt new file mode 100644 index 000000000..d197c913c --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestAppBad-result.txt @@ -0,0 +1 @@ +[^0] diff --git a/Tests/RunCMake/StandardLinkDirectories/TestLib.cmake b/Tests/RunCMake/StandardLinkDirectories/TestLib.cmake new file mode 100644 index 000000000..aa8a6c37d --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestLib.cmake @@ -0,0 +1 @@ +add_subdirectory(TestLib) diff --git a/Tests/RunCMake/StandardLinkDirectories/TestLib/CMakeLists.txt b/Tests/RunCMake/StandardLinkDirectories/TestLib/CMakeLists.txt new file mode 100644 index 000000000..bf41631f8 --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestLib/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(SLD TestLib.c) +set_target_properties(SLD PROPERTIES ARCHIVE_OUTPUT_DIRECTORY $<1:lib>) diff --git a/Tests/RunCMake/StandardLinkDirectories/TestLib/TestLib.c b/Tests/RunCMake/StandardLinkDirectories/TestLib/TestLib.c new file mode 100644 index 000000000..e5d2b1c1f --- /dev/null +++ b/Tests/RunCMake/StandardLinkDirectories/TestLib/TestLib.c @@ -0,0 +1,4 @@ +int TestSymbol(void) +{ + return 0; +} diff --git a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt index 919a1685c..c0c9c12d1 100644 --- a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt +++ b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) message(STATUS "source: '${CMAKE_SOURCE_DIR}'") diff --git a/Tests/RunCMake/TargetObjects/CMakeLists.txt b/Tests/RunCMake/TargetObjects/CMakeLists.txt index 86f640d25..902f0bb35 100644 --- a/Tests/RunCMake/TargetObjects/CMakeLists.txt +++ b/Tests/RunCMake/TargetObjects/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} LANGUAGES NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/TargetPolicies/CMakeLists.txt b/Tests/RunCMake/TargetPolicies/CMakeLists.txt index 74b3ff8de..bf2ef1506 100644 --- a/Tests/RunCMake/TargetPolicies/CMakeLists.txt +++ b/Tests/RunCMake/TargetPolicies/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 415f4048d..4015ae8a2 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -43,6 +43,7 @@ \* CMP0157 \* CMP0160 \* CMP0162 + \* CMP0179 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/TargetProperties/CMakeLists.txt b/Tests/RunCMake/TargetProperties/CMakeLists.txt index 26f536be5..86d47a7d5 100644 --- a/Tests/RunCMake/TargetProperties/CMakeLists.txt +++ b/Tests/RunCMake/TargetProperties/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST}) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ToolchainFile/CMakeLists.txt b/Tests/RunCMake/ToolchainFile/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/ToolchainFile/CMakeLists.txt +++ b/Tests/RunCMake/ToolchainFile/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake index 0ec8c4278..78996e415 100644 --- a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake +++ b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake @@ -21,6 +21,11 @@ if(CMake_TEST_OBJC) run_cmake(unitybuild_objcxx_group) run_cmake(unitybuild_c_and_cxx_and_objc_and_objcxx) endif() +if(CMake_TEST_CUDA) + run_cmake(unitybuild_cuda) + run_cmake(unitybuild_cuda_group) + run_cmake(unitybuild_cxx_and_cuda) +endif() run_cmake(unitybuild_batchsize) run_cmake(unitybuild_default_batchsize) run_cmake(unitybuild_skip) diff --git a/Tests/RunCMake/UnityBuild/f.cu b/Tests/RunCMake/UnityBuild/f.cu new file mode 100644 index 000000000..d5813c67c --- /dev/null +++ b/Tests/RunCMake/UnityBuild/f.cu @@ -0,0 +1,5 @@ +int f(int x) +{ + (void)x; + return 0; +} diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cuda-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cuda-check.cmake new file mode 100644 index 000000000..9a078cd2e --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cuda-check.cmake @@ -0,0 +1,5 @@ +set(unitybuild_cu "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_0_cu.cu") +if(NOT EXISTS "${unitybuild_cu}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_cu} does not exist.") + return() +endif() diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cuda.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cuda.cmake new file mode 100644 index 000000000..399a9b193 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cuda.cmake @@ -0,0 +1,14 @@ + +set(CMAKE_CUDA_ARCHITECTURES all-major) +project(unitybuild_cuda CUDA) + +set(srcs "") +foreach(s RANGE 1 8) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cu") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cuda_group-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cuda_group-check.cmake new file mode 100644 index 000000000..7d2a2dc8b --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cuda_group-check.cmake @@ -0,0 +1,27 @@ +set(unitybuild_a_cu "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_a_cu.cu") +if(NOT EXISTS "${unitybuild_a_cu}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_a_cu} does not exist.") + return() +else() + #verify that odr2 is not part of this source set + file(STRINGS ${unitybuild_a_cu} unitybuild_a_cu_strings) + string(REGEX MATCH ".*#include.*odr2.cu" matched_code ${unitybuild_a_cu_strings}) + if(matched_code) + set(RunCMake_TEST_FAILED "Generated unity file includes un-expected ord2.cu source file") + return() + endif() +endif() + +set(unitybuild_b_cu "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_b_cu.cu") +if(NOT EXISTS "${unitybuild_b_cu}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_b_cu} does not exist.") + return() +else() + #verify that odr1 is not part of this source set + file(STRINGS ${unitybuild_b_cu} unitybuild_b_cu_strings) + string(REGEX MATCH ".*#include.*odr1.cu" matched_code ${unitybuild_b_cu_strings}) + if(matched_code) + set(RunCMake_TEST_FAILED "Generated unity file includes un-expected ord1.cu source file") + return() + endif() +endif() diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cuda_group.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cuda_group.cmake new file mode 100644 index 000000000..d4a052289 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cuda_group.cmake @@ -0,0 +1,28 @@ +set(CMAKE_CUDA_ARCHITECTURES all-major) +project(unitybuild_cu CUDA) + +set(srcs "") +foreach(s RANGE 1 4) + set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cu") + file(WRITE "${src}" "int s${s}(void) { return 0; }\n") + list(APPEND srcs "${src}") +endforeach() + +foreach(s RANGE 1 2) + set(src "${CMAKE_CURRENT_BINARY_DIR}/odr${s}.cu") + file(WRITE "${src}" "namespace odr { int s${s}(void) { return 0; } }\n") + list(APPEND srcs "${src}") +endforeach() + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON + UNITY_BUILD_MODE GROUP + ) + +set_source_files_properties(s1.cu s2.cu odr1.cu + PROPERTIES UNITY_GROUP "a" + ) +set_source_files_properties(s3.cu s4.cu odr2.cu + PROPERTIES UNITY_GROUP "b" + ) diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda-check.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda-check.cmake new file mode 100644 index 000000000..d53242ad7 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda-check.cmake @@ -0,0 +1,42 @@ +set(unitybuild_a_cu "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_a_cu.cu") +if(NOT EXISTS "${unitybuild_a_cu}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_a_cu} does not exist.") + return() +else() + #verify that the 4 c file is part of this grouping and therefore UNITY_BUILD_BATCH_SIZE + #was ignored + file(STRINGS ${unitybuild_a_cu} unitybuild_a_c_strings) + string(REGEX MATCH ".*#include.*s1.cu.*#include.*s2.cu.*#include.*s3.cu.*#include.*s4.cu.*" matched_code ${unitybuild_a_c_strings}) + if(NOT matched_code) + set(RunCMake_TEST_FAILED "Generated unity file doesn't include expected source files") + return() + endif() +endif() + +set(unitybuild_b_cu "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_b_cu.cu") +if(NOT EXISTS "${unitybuild_b_cu}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_b_cu} does not exist.") + return() +endif() + + +set(unitybuild_a_cxx "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_a_cxx.cxx") +if(NOT EXISTS "${unitybuild_a_cxx}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_a_cxx} does not exist.") + return() +else() + #verify that the 4 cxx file are part of this grouping and therefore UNITY_BUILD_BATCH_SIZE + #was ignored + file(STRINGS ${unitybuild_a_cxx} unitybuild_a_cxx_strings) + string(REGEX MATCH ".*#include.*s1.cxx.*#include.*s2.cxx.*#include.*s3.cxx.*#include.*s4.cxx.*" matched_code ${unitybuild_a_cxx_strings}) + if(NOT matched_code) + set(RunCMake_TEST_FAILED "Generated unity file doesn't include expected source files") + return() + endif() +endif() + +set(unitybuild_b_cxx "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Unity/unity_b_cxx.cxx") +if(NOT EXISTS "${unitybuild_b_cxx}") + set(RunCMake_TEST_FAILED "Generated unity source files ${unitybuild_b_cxx} does not exist.") + return() +endif() diff --git a/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda.cmake b/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda.cmake new file mode 100644 index 000000000..c633ed120 --- /dev/null +++ b/Tests/RunCMake/UnityBuild/unitybuild_cxx_and_cuda.cmake @@ -0,0 +1,40 @@ +set(CMAKE_CUDA_ARCHITECTURES all-major) +project(unitybuild_c_and_cxx CUDA CXX) + +set(srcs f.cu) +foreach(s RANGE 1 8) + set(src_cu "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cu") + file(WRITE "${src_cu}" " +int f(int);\n +int s${s}(void) { return f(${s}); }\n" + ) + + set(src_cxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cxx") + file(WRITE "${src_cxx}" " +extern \"C\" { \n + int f(int); \n +}\n + int s${s}(void) { return f(${s}); }\n" + ) + + list(APPEND srcs "${src_cu}") + list(APPEND srcs "${src_cxx}") +endforeach() + + + +add_library(tgt SHARED ${srcs}) + +set_target_properties(tgt PROPERTIES UNITY_BUILD ON + UNITY_BUILD_MODE GROUP + #UNITY_BUILD_BATCH_SIZE will be ignored + UNITY_BUILD_BATCH_SIZE 2) + +set_source_files_properties(s1.cu s2.cu s3.cu s4.cu + s1.cxx s2.cxx s3.cxx s4.cxx + PROPERTIES UNITY_GROUP "a" + ) +set_source_files_properties(s5.cu s6.cu s7.cu s8.cu + s5.cxx s6.cxx s7.cxx s8.cxx + PROPERTIES UNITY_GROUP "b" + ) diff --git a/Tests/RunCMake/VS10Project/CMakeLists.txt b/Tests/RunCMake/VS10Project/CMakeLists.txt index 91baae7eb..bf2ef1506 100644 --- a/Tests/RunCMake/VS10Project/CMakeLists.txt +++ b/Tests/RunCMake/VS10Project/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5.0) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake index e0d74cfce..bbd8c8b1a 100644 --- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake +++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake @@ -50,6 +50,7 @@ run_cmake(VsSettings) run_cmake(VsSourceSettingsTool) run_cmake(VsPlatformToolset) run_cmake(VsControlFlowGuardLinkSetting) +run_cmake(VsToolOverride) run_cmake(VsWinRTByDefault) @@ -98,6 +99,7 @@ run_cmake(DebugInformationFormat) run_cmake(VsCLREmpty) run_cmake(VsCLRPure) run_cmake(VsCLRSafe) +run_cmake(VsFrameworkReference) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.20) run_cmake(VsCLRNetcore) diff --git a/Tests/RunCMake/VS10Project/VsFrameworkReference-check.cmake b/Tests/RunCMake/VS10Project/VsFrameworkReference-check.cmake new file mode 100644 index 000000000..7abe8a0cf --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsFrameworkReference-check.cmake @@ -0,0 +1,24 @@ +set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") +if(NOT EXISTS "${vcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") + return() +endif() + +set(frameworkReferenceSet FALSE) + +file(STRINGS "${vcProjectFile}" lines) +foreach(line IN LISTS lines) + if(line MATCHES "^ *$") + if("${CMAKE_MATCH_1}" STREQUAL "Microsoft.WindowsDesktop.App.WPF") + message(STATUS "foo.vcxproj has FrameworkReference set") + set(frameworkReferenceSet TRUE) + else() + message(STATUS "foo.vcxproj has FrameworkReference incorrectly set to ${CMAKE_MATCH_1}") + endif() + endif() +endforeach() + +if(NOT frameworkReferenceSet) + set(RunCMake_TEST_FAILED "FrameworkReference not found or not set correctly.") + return() +endif() diff --git a/Tests/RunCMake/VS10Project/VsFrameworkReference.cmake b/Tests/RunCMake/VS10Project/VsFrameworkReference.cmake new file mode 100644 index 000000000..0e4d6fb79 --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsFrameworkReference.cmake @@ -0,0 +1,8 @@ +enable_language(CXX) + +add_executable(foo foo.cpp) + +set_target_properties(foo PROPERTIES + COMMON_LANGUAGE_RUNTIME "netcore" + DOTNET_TARGET_FRAMEWORK "net8.0-windows" + VS_FRAMEWORK_REFERENCES "Microsoft.WindowsDesktop.App.WPF") diff --git a/Tests/RunCMake/VS10Project/VsToolOverride-check.cmake b/Tests/RunCMake/VS10Project/VsToolOverride-check.cmake new file mode 100644 index 000000000..8d9adbb67 --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsToolOverride-check.cmake @@ -0,0 +1,64 @@ +# Figure out which build tool the test files in a project are using +macro(get_build_tools_from_project_file projectFile) + set(_s "[ \t\r\n]") # Whitespace character class + + set(ItemGroupBeginRegex "<${_s}*ItemGroup${_s}*>") + set(ItemGroupEndRegex "") + set(GroupItemRegex ".*<${_s}*([A-Za-z0-9_]+)${_s}+Include${_s}*=${_s}*\"([^\"]*)\".*") + + if(NOT EXISTS "${projectFile}") + set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.") + return() + endif() + + file(STRINGS "${projectFile}" lines) + + foreach(line IN LISTS lines) + if(line MATCHES "${ItemGroupBeginRegex}") + set(InItemGroup TRUE) + elseif(line MATCHES "${ItemGroupEndRegex}") + set(InItemGroup FALSE) + elseif(line MATCHES "${GroupItemRegex}") + if(InItemGroup) + string(REGEX REPLACE "${GroupItemRegex}" "\\1" itemTool "${line}") + string(REGEX REPLACE "${GroupItemRegex}" "\\2" itemPath "${line}") + + if(itemPath MATCHES ".*foo\\.cpp") + set(fooCppTool "${itemTool}") + elseif(itemPath MATCHES ".*foo\\.txt") + set(fooTxtTool "${itemTool}") + elseif(itemPath MATCHES ".*bar\\.cpp") + set(barCppTool "${itemTool}") + elseif(itemPath MATCHES ".*bar\\.txt") + set(barTxtTool "${itemTool}") + endif() + endif() + endif() + endforeach() +endmacro() + +# Verify a build tool is as expected +macro(verify_build_tool fileName expectedBuildTool actualBuildTool) + if("${actualBuildTool}" STREQUAL "${expectedBuildTool}") + message(STATUS "File '${fileName}' in project file '${projectFile}' has expected build tool '${expectedBuildTool}'") + else() + set(RunCMake_TEST_FAILED "File '${fileName}' in project file '${projectFile}' has unexpected build tool '${actualBuildTool}'! Expected: '${expectedBuildTool}'" PARENT_SCOPE) + return() + endif() +endmacro() + +# Test using VS_TOOL_OVERRIDE +block() + set(projectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") + get_build_tools_from_project_file("${projectFile}") + verify_build_tool("foo.cpp" "CustomFooCppTool" "${fooCppTool}") + verify_build_tool("foo.txt" "CustomFooTxtTool" "${fooTxtTool}") +endblock() + +# Test default behavior without using VS_TOOL_OVERRIDE +block() + set(projectFile "${RunCMake_TEST_BINARY_DIR}/bar.vcxproj") + get_build_tools_from_project_file("${projectFile}") + verify_build_tool("bar.cpp" "ClCompile" "${barCppTool}") + verify_build_tool("bar.txt" "None" "${barTxtTool}") +endblock() diff --git a/Tests/RunCMake/VS10Project/VsToolOverride.cmake b/Tests/RunCMake/VS10Project/VsToolOverride.cmake new file mode 100644 index 000000000..62ae5e845 --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsToolOverride.cmake @@ -0,0 +1,7 @@ +enable_language(CXX) + +set_property(SOURCE "foo.cpp" PROPERTY VS_TOOL_OVERRIDE CustomFooCppTool) +set_property(SOURCE "foo.txt" PROPERTY VS_TOOL_OVERRIDE CustomFooTxtTool) +add_library(foo foo.cpp foo.txt) + +add_library(bar bar.cpp bar.txt) diff --git a/Tests/RunCMake/VS10Project/bar.txt b/Tests/RunCMake/VS10Project/bar.txt new file mode 100644 index 000000000..ebd7525b3 --- /dev/null +++ b/Tests/RunCMake/VS10Project/bar.txt @@ -0,0 +1 @@ +Bar diff --git a/Tests/RunCMake/VS10Project/foo.txt b/Tests/RunCMake/VS10Project/foo.txt new file mode 100644 index 000000000..bc56c4d89 --- /dev/null +++ b/Tests/RunCMake/VS10Project/foo.txt @@ -0,0 +1 @@ +Foo diff --git a/Tests/RunCMake/VS10ProjectWinCE/RunCMakeTest.cmake b/Tests/RunCMake/VS10ProjectWinCE/RunCMakeTest.cmake deleted file mode 100644 index 2c9067f75..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/RunCMakeTest.cmake +++ /dev/null @@ -1,9 +0,0 @@ -include(RunCMake) - -set(RunCMake_GENERATOR "Visual Studio 12 2013") -set(RunCMake_GENERATOR_TOOLSET CE800) -set(RunCMake_GENERATOR_INSTANCE "") -set(RunCMake_TEST_OPTIONS -DCMAKE_SYSTEM_NAME=WindowsCE ) - -run_cmake(VsCEDebuggerDeploy) -run_cmake(VSCSharpCFProject) diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake deleted file mode 100644 index b1deb9907..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake +++ /dev/null @@ -1,118 +0,0 @@ -set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") -if(NOT EXISTS "${vcProjectFile}") - set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") - return() -endif() - - -if( NOT ${CMAKE_SYSTEM_NAME} STREQUAL "WindowsCE" ) - set(RunCMake_TEST_FAILED "Test only valid for WindowsCE") - return() -endif() - - -set(FoundCEAdditionalFiles FALSE) -set(FoundRemoteDirectory FALSE) -set(FoundToolsVersion4 FALSE) -set(FoundEnableRedirectPlatform FALSE) -set(FoundRedirectPlatformValue FALSE) - - -file(STRINGS "${vcProjectFile}" lines) -foreach(line IN LISTS lines) - if(line MATCHES "^ * *foo\\.dll\\|\\\\foo\\\\src\\\\dir\\\\on\\\\host\\|\\$\\(RemoteDirectory\\)\\|0;bar\\.dll\\|\\\\bar\\\\src\\\\dir\\|\\$\\(RemoteDirectory\\)bardir\\|0.* *$") - set(FoundCEAdditionalFiles TRUE) - elseif(line MATCHES " *[A-Za-z0-9\\]+ *$") - set(FoundRemoteDirectory TRUE) - elseif(line MATCHES " * *$") - set(FoundToolsVersion4 TRUE) - elseif(line MATCHES "^ *true *$") - set(FoundEnableRedirectPlatform TRUE) - elseif(line MATCHES "^ *.+ *$") - set(FoundRedirectPlatformValue TRUE) - endif() -endforeach() - -if(NOT FoundCEAdditionalFiles) - set(RunCMake_TEST_FAILED "CEAddionalFiles not found or not set correctly.") - return() -endif() - -if(NOT FoundRemoteDirectory) - set(RunCMake_TEST_FAILED "RemoteDirectory not found or not set correctly.") - return() -endif() - -if(NOT FoundToolsVersion4) - set(RunCMake_TEST_FAILED "Failed to find correct ToolsVersion=\"4.0\" .") - return() -endif() - -if(NOT FoundEnableRedirectPlatform) - set(RunCMake_TEST_FAILED "Failed to find EnableRedirectPlatform true property.") - return() -endif() - -if(NOT FoundRedirectPlatformValue) - set(RunCMake_TEST_FAILED "Failed to find RedirectPlatformValue property.") - return() -endif() - -# -# Test solution file deployment items. -# - -set(vcSlnFile "${RunCMake_TEST_BINARY_DIR}/VsCEDebuggerDeploy.sln") -if(NOT EXISTS "${vcSlnFile}") - set(RunCMake_TEST_FAILED "Solution file ${vcSlnFile} does not exist.") - return() -endif() - - -if( NOT ${CMAKE_SYSTEM_NAME} STREQUAL "WindowsCE" ) - set(RunCMake_TEST_FAILED "Test only valid for WindowsCE") - return() -endif() - - -set(FooProjGUID "") -set(FoundFooProj FALSE) -set(InFooProj FALSE) -set(FoundReleaseDeploy FALSE) -set(DeployConfigs Debug MinSizeRel RelWithDebInfo ) - -file(STRINGS "${vcSlnFile}" lines) -foreach(line IN LISTS lines) -#message(STATUS "${line}") - if( (NOT InFooProj ) AND (line MATCHES "^[ \\t]*Project\\(\"{[A-F0-9-]+}\"\\) = \"foo\", \"foo.vcxproj\", \"({[A-F0-9-]+})\"[ \\t]*$")) - # First, identify the GUID for the foo project, and record it. - set(FoundFooProj TRUE) - set(InFooProj TRUE) - set(FooProjGUID ${CMAKE_MATCH_1}) - elseif(InFooProj AND line MATCHES "EndProject") - set(InFooProj FALSE) - elseif((NOT InFooProj) AND line MATCHES "${FooProjGUID}\\.Release.*\\.Deploy\\.0") - # If foo's Release configuration is set to deploy, this is the error. - set(FoundReleaseDeploy TRUE) - endif() - if( line MATCHES "{[A-F0-9-]+}\\.([^\\|]+).*\\.Deploy\\.0" ) - # Check that the other configurations ARE set to deploy. - list( REMOVE_ITEM DeployConfigs ${CMAKE_MATCH_1}) - endif() -endforeach() - -if(FoundReleaseDeploy) - set(RunCMake_TEST_FAILED "Release deployment not inhibited by VS_NO_SOLUTION_DEPLOY_Release.") - return() -endif() - -if(NOT FoundFooProj) - set(RunCMake_TEST_FAILED "Failed to find foo project in the solution.") - return() -endif() - -list(LENGTH DeployConfigs length) -if( length GREATER 0 ) - set(RunCMake_TEST_FAILED "Failed to find Deploy lines for non-Release configurations. (${length})") - return() -endif() diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy.cmake deleted file mode 100644 index 611db0adb..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy.cmake +++ /dev/null @@ -1,14 +0,0 @@ -enable_language(CXX) - -set(DEPLOY_DIR - "temp\\foodir" -) - -add_library(foo SHARED foo.cpp) - -set_target_properties(foo - PROPERTIES - DEPLOYMENT_ADDITIONAL_FILES "foo.dll|\\foo\\src\\dir\\on\\host|$(RemoteDirectory)|0;bar.dll|\\bar\\src\\dir|$(RemoteDirectory)bardir|0" - DEPLOYMENT_REMOTE_DIRECTORY ${DEPLOY_DIR} - VS_NO_SOLUTION_DEPLOY $ -) diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject-check.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject-check.cmake deleted file mode 100644 index 618896ec0..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject-check.cmake +++ /dev/null @@ -1,54 +0,0 @@ -# -# Check C# Compact Framework 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() - -if( NOT ${CMAKE_SYSTEM_NAME} STREQUAL "WindowsCE" ) - set(RunCMake_TEST_FAILED "Test only valid for WindowsCE") - return() -endif() - -set(FoundTargetFrameworkTargetsVersion FALSE) -set(FoundDotNetFrameworkVersion FALSE) -set(FoundTargetFrameworkIdentifier FALSE) -set(FoundCFTargetsImport FALSE) - - -file(STRINGS "${csProjectFile}" lines) -foreach(line IN LISTS lines) - #message(STATUS ${line}) - if(line MATCHES "^ *WindowsEmbeddedCompact *$") - set(FoundTargetFrameworkIdentifier TRUE) - elseif(line MATCHES " *v3.9 *$") - set(FoundDotNetFrameworkVersion TRUE) - elseif(line MATCHES " *v8.0 *$") - set(FoundTargetFrameworkTargetsVersion TRUE) - elseif( line MATCHES " * *" ) - set(FoundCFTargetsImport TRUE) - endif() -endforeach() - - -if(NOT FoundTargetFrameworkTargetsVersion) - set(RunCMake_TEST_FAILED "TargetFrameworkIdentifier not found or not set correctly.") - return() -endif() - -if(NOT FoundDotNetFrameworkVersion) - set(RunCMake_TEST_FAILED "TargetFrameworkVersion not found or not set correctly.") - return() -endif() - -if(NOT FoundTargetFrameworkIdentifier) - set(RunCMake_TEST_FAILED "TargetFrameworkTargetsVersion not found or not set correctly.") - return() -endif() - -if(NOT FoundCFTargetsImport) - set(RunCMake_TEST_FAILED "Import of Compact Framework targets file not found or not set correctly.") - return() -endif() diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject.cmake deleted file mode 100644 index fb2acbbe6..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/VsCSharpCFProject.cmake +++ /dev/null @@ -1,8 +0,0 @@ -enable_language(CSharp) - -add_library(foo SHARED foo.cs ) - -set_target_properties(foo - PROPERTIES - DOTNET_TARGET_FRAMEWORK_VERSION "v3.9" -) diff --git a/Tests/RunCMake/VS10ProjectWinCE/foo.cs b/Tests/RunCMake/VS10ProjectWinCE/foo.cs deleted file mode 100644 index 3695dc91e..000000000 --- a/Tests/RunCMake/VS10ProjectWinCE/foo.cs +++ /dev/null @@ -1,3 +0,0 @@ -void foo() -{ -} diff --git a/Tests/RunCMake/VSSolution/CMakeLists.txt b/Tests/RunCMake/VSSolution/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/VSSolution/CMakeLists.txt +++ b/Tests/RunCMake/VSSolution/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VisibilityPreset/CMP0063-OLD-stderr.txt b/Tests/RunCMake/VisibilityPreset/CMP0063-OLD-stderr.txt index 34ac57d20..2c04fa090 100644 --- a/Tests/RunCMake/VisibilityPreset/CMP0063-OLD-stderr.txt +++ b/Tests/RunCMake/VisibilityPreset/CMP0063-OLD-stderr.txt @@ -1,4 +1,11 @@ -^CMake Deprecation Warning at CMP0063-OLD.cmake:[0-9]+ \(cmake_policy\): +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ +CMake Deprecation Warning at CMP0063-OLD.cmake:[0-9]+ \(cmake_policy\): The OLD behavior for policy CMP0063 will be removed from a future version of CMake. diff --git a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-exe-stderr.txt b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-exe-stderr.txt index b8d726f8f..a8e610eaf 100644 --- a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-exe-stderr.txt +++ b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-exe-stderr.txt @@ -1,3 +1,10 @@ +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ CMake Warning \(dev\) at CMP0063-WARN-exe.cmake:[0-9]+ \(add_executable\): Policy CMP0063 is not set: Honor visibility properties for all target types. Run "cmake --help-policy CMP0063" for policy details. Use the diff --git a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-no-stderr.txt b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-no-stderr.txt new file mode 100644 index 000000000..6f8719ec2 --- /dev/null +++ b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-no-stderr.txt @@ -0,0 +1,6 @@ +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\.$ diff --git a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-obj-stderr.txt b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-obj-stderr.txt index 3a7732a65..150eebbd5 100644 --- a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-obj-stderr.txt +++ b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-obj-stderr.txt @@ -1,3 +1,10 @@ +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ CMake Warning \(dev\) at CMP0063-WARN-obj.cmake:[0-9]+ \(add_library\): Policy CMP0063 is not set: Honor visibility properties for all target types. Run "cmake --help-policy CMP0063" for policy details. Use the diff --git a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-sta-stderr.txt b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-sta-stderr.txt index 1efa1b5b0..3ea8864ef 100644 --- a/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-sta-stderr.txt +++ b/Tests/RunCMake/VisibilityPreset/CMP0063-WARN-sta-stderr.txt @@ -1,3 +1,10 @@ +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ CMake Warning \(dev\) at CMP0063-WARN-sta.cmake:[0-9]+ \(add_library\): Policy CMP0063 is not set: Honor visibility properties for all target types. Run "cmake --help-policy CMP0063" for policy details. Use the diff --git a/Tests/RunCMake/VisibilityPreset/CMakeLists.txt b/Tests/RunCMake/VisibilityPreset/CMakeLists.txt index 18dfd2686..69e023688 100644 --- a/Tests/RunCMake/VisibilityPreset/CMakeLists.txt +++ b/Tests/RunCMake/VisibilityPreset/CMakeLists.txt @@ -1,3 +1,6 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) +if(RunCMake_TEST MATCHES "CMP0063-(OLD|WARN)") + cmake_policy(VERSION 3.2) # old enough to not set CMP0063 +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake b/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake index 34259b709..151b907aa 100644 --- a/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake +++ b/Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake @@ -1,12 +1,12 @@ cmake_policy(SET CMP0053 NEW) include(RunCMake) -run_cmake(VsDotnetSdkCustomCommandsTarget) -run_cmake(VsDotnetSdkCustomCommandsSource) run_cmake(VsDotnetSdkStartupObject) run_cmake(VsDotnetSdkDefines) run_cmake(DotnetSdkVariables) run_cmake(VsDotnetSdkXamlFiles) +run_cmake(VsDotnetSdkAssemblyName) +run_cmake(VsDotnetSdkConfigurations) function(run_VsDotnetSdk) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VsDotnetSdk-build) @@ -18,3 +18,16 @@ function(run_VsDotnetSdk) run_cmake_command(VsDotnetSdk-build ${CMAKE_COMMAND} --build . -- ${build_flags}) endfunction() run_VsDotnetSdk() + +function(runCmakeAndBuild CASE) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build) + set(RunCMake_TEST_NO_CLEAN 1) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + run_cmake(${CASE}) + run_cmake_command(${CASE}-build ${CMAKE_COMMAND} --build .) +endfunction() + +runCmakeAndBuild(VsDotnetSdkCustomCommandsTarget) +runCmakeAndBuild(VsDotnetSdkNugetRestore) +runCmakeAndBuild(VsDotnetSdkCustomCommandsSource) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName-check.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName-check.cmake new file mode 100644 index 000000000..3d865b8b6 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName-check.cmake @@ -0,0 +1,22 @@ +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(hasAssemblyName FALSE) + +file(STRINGS "${csProjectFile}" lines) + +foreach(line IN LISTS lines) + if(NOT inLib1) + if(line MATCHES "longer name") + set(hasAssemblyName TRUE) + endif() + endif() +endforeach() + +if(NOT hasAssemblyName) + set(RunCMake_TEST_FAILED " not found in ${csProjectFile}.") +endif() diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName.cmake new file mode 100644 index 000000000..b057b723e --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkAssemblyName.cmake @@ -0,0 +1,9 @@ +enable_language(CSharp) + +if(NOT CMAKE_CSharp_COMPILER) + return() +endif() + +set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") +add_library(foo SHARED lib1.cs) +set_target_properties(foo PROPERTIES OUTPUT_NAME "longer name") diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations-check.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations-check.cmake new file mode 100644 index 000000000..f7fc6de77 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations-check.cmake @@ -0,0 +1,20 @@ +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(hasConfigurations FALSE) + +file(STRINGS "${csProjectFile}" lines) + +foreach(line IN LISTS lines) + if(line MATCHES "Debug;Release;MinSizeRel;RelWithDebInfo;ExtraTestConfig") + set(hasConfigurations TRUE) + endif() +endforeach() + +if(NOT hasConfigurations) + set(RunCMake_TEST_FAILED " not found in ${csProjectFile}.") +endif() diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations.cmake new file mode 100644 index 000000000..178cd00f1 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkConfigurations.cmake @@ -0,0 +1,11 @@ +enable_language(CSharp) + +if(NOT CMAKE_CSharp_COMPILER) + return() +endif() + +set(CMAKE_SHARED_LINKER_FLAGS_EXTRATESTCONFIG "${CMAKE_SHARED_LINKER_FLAGS_Debug}") +list(APPEND CMAKE_CONFIGURATION_TYPES ExtraTestConfig) + +set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") +add_library(foo SHARED lib1.cs) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-build-stdout.txt b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-build-stdout.txt new file mode 100644 index 000000000..9afaa2be9 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-build-stdout.txt @@ -0,0 +1 @@ +Generating bar.cs diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-stderr.txt b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-stderr.txt deleted file mode 100644 index 90af627f3..000000000 --- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource-stderr.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error in CMakeLists.txt: - The target "foo" does not currently support add_custom_command as the - Visual Studio generators have not yet learned how to generate custom - commands in .Net SDK-style projects. - - -CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource.cmake index af1894603..d6166dbcf 100644 --- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource.cmake +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsSource.cmake @@ -7,9 +7,8 @@ endif() set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") add_custom_command( OUTPUT bar.cs - COMMAND copy /A ${CMAKE_CURRENT_SOURCE_DIR}/lib1.cs - bar.cs - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/lib1.cs + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/lib1.cs" bar.cs + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/lib1.cs" VERBATIM) add_library(foo SHARED bar.cs) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-build-stdout.txt b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-build-stdout.txt new file mode 100644 index 000000000..890a8f1f9 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-build-stdout.txt @@ -0,0 +1 @@ +.*"This should happen!".* diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-stderr.txt b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-stderr.txt deleted file mode 100644 index 90af627f3..000000000 --- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget-stderr.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error in CMakeLists.txt: - The target "foo" does not currently support add_custom_command as the - Visual Studio generators have not yet learned how to generate custom - commands in .Net SDK-style projects. - - -CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget.cmake index f5cd3174e..078d7ddfb 100644 --- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget.cmake +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkCustomCommandsTarget.cmake @@ -8,5 +8,5 @@ set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") add_library(foo SHARED lib1.cs) add_custom_command(TARGET foo PRE_BUILD - COMMAND echo "This shouldn't happen!" + COMMAND echo "This should happen!" VERBATIM) diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore-build-stdout.txt b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore-build-stdout.txt new file mode 100644 index 000000000..b0c8da709 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore-build-stdout.txt @@ -0,0 +1 @@ +Determining projects to restore diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore.cmake new file mode 100644 index 000000000..5feb26577 --- /dev/null +++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkNugetRestore.cmake @@ -0,0 +1,8 @@ +enable_language(CSharp) +if(NOT CMAKE_CSharp_COMPILER) + return() +endif() + +set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk") + +add_executable(foo csharponly.cs lib1.cs) diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt index 1d0bd703c..0ee47f46a 100644 --- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} CXX) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake index 0d181ca00..64505ffd3 100644 --- a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake @@ -1,5 +1,11 @@ include(RunCMake) +if(RunCMake_GENERATOR STREQUAL "Xcode") + set(maybe_ios_catalyst "") +else() + set(maybe_ios_catalyst ios-catalyst) +endif() + function(create_library type platform system_name archs sysroot) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build) run_cmake_with_options(create-${type}-${platform} -DCMAKE_SYSTEM_NAME=${system_name} -DCMAKE_OSX_ARCHITECTURES=${archs} -DCMAKE_OSX_SYSROOT=${sysroot} -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_BINARY_DIR}/install) @@ -12,6 +18,9 @@ endfunction() function(create_libraries type) create_library(${type} macos Darwin "${macos_archs_2}" macosx) create_library(${type} ios iOS "arm64" iphoneos) + if(maybe_ios_catalyst) + create_library(${type} ios-catalyst iOS "${macos_archs_2}" macosx) + endif() create_library(${type} tvos tvOS "arm64" appletvos) create_library(${type} watchos watchOS "armv7k\\\\;arm64_32" watchos) if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15.2) @@ -58,6 +67,9 @@ endfunction() function(create_executables name type) create_executable(${name}-macos ${type} Darwin "${macos_archs_2}" macosx) create_executable(${name}-ios ${type} iOS "arm64" iphoneos) + if(maybe_ios_catalyst) + create_executable(${name}-ios-catalyst ${type} iOS "${macos_archs_2}" macosx) + endif() create_executable(${name}-tvos ${type} tvOS "arm64" appletvos) create_executable(${name}-watchos ${type} watchOS "armv7k\\\\;arm64_32" watchos) if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15.2) @@ -71,7 +83,7 @@ function(create_executables name type) endif() endfunction() -set(xcframework_platforms macos ios tvos watchos ios-simulator tvos-simulator watchos-simulator) +set(xcframework_platforms macos ios ${maybe_ios_catalyst} tvos watchos ios-simulator tvos-simulator watchos-simulator) if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15.2) list(APPEND xcframework_platforms visionos visionos-simulator) endif() @@ -141,6 +153,20 @@ run_cmake_command(export-ios-install ${CMAKE_COMMAND} --install . ${_config_arg} unset(RunCMake_TEST_NO_CLEAN) unset(RunCMake_TEST_BINARY_DIR) +if(maybe_ios_catalyst) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-catalyst-build) + run_cmake_with_options(export-ios-catalyst -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=macosx "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install) + set(RunCMake_TEST_NO_CLEAN 1) + set(_config_arg) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(_config_arg --config Release) + endif() + run_cmake_command(export-ios-catalyst-build ${CMAKE_COMMAND} --build . ${_config_arg}) + run_cmake_command(export-ios-catalyst-install ${CMAKE_COMMAND} --install . ${_config_arg}) + unset(RunCMake_TEST_NO_CLEAN) + unset(RunCMake_TEST_BINARY_DIR) +endif() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-simulator-build) run_cmake_with_options(export-ios-simulator -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install) set(RunCMake_TEST_NO_CLEAN 1) @@ -175,6 +201,19 @@ else() set(src_dir "${RunCMake_SOURCE_DIR}") set(bld_dir "${RunCMake_BINARY_DIR}") endif() +if(maybe_ios_catalyst) + set(maybe_ios_catalyst_mylib + -library ${bld_dir}/export-install/lib/ios-catalyst/libmylib.a + -headers ${src_dir}/mylib/include + ) + set(maybe_ios_catalyst_mylib_genex + -library ${bld_dir}/export-install/lib/ios-catalyst/libmylib-genex.a + -headers ${src_dir}/mylib/include + ) +else() + set(maybe_ios_catalyst_mylib "") + set(maybe_ios_catalyst_mylib_genex "") +endif() set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-install) run_cmake_command(export-install-xcframework xcodebuild -create-xcframework -output ${bld_dir}/export-install/lib/mylib.xcframework @@ -182,6 +221,7 @@ run_cmake_command(export-install-xcframework xcodebuild -create-xcframework -headers ${src_dir}/mylib/include -library ${bld_dir}/export-install/lib/ios/libmylib.a -headers ${src_dir}/mylib/include + ${maybe_ios_catalyst_mylib} -library ${bld_dir}/export-install/lib/ios-simulator/libmylib.a -headers ${src_dir}/mylib/include ) @@ -191,6 +231,7 @@ run_cmake_command(export-install-xcframework-genex xcodebuild -create-xcframewor -headers ${src_dir}/mylib/include -library ${bld_dir}/export-install/lib/ios/libmylib-genex.a -headers ${src_dir}/mylib/include + ${maybe_ios_catalyst_mylib_genex} -library ${bld_dir}/export-install/lib/ios-simulator/libmylib-genex.a -headers ${src_dir}/mylib/include ) diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-ios-catalyst.cmake new file mode 100644 index 000000000..ce415ef5a --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-framework-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-catalyst.cmake new file mode 100644 index 000000000..9351dd42d --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-link-phase.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-library-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-library-ios-catalyst.cmake new file mode 100644 index 000000000..ce415ef5a --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-library-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-catalyst.cmake new file mode 100644 index 000000000..9351dd42d --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-link-phase.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-catalyst.cmake new file mode 100644 index 000000000..1682ed310 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-target.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-catalyst.cmake new file mode 100644 index 000000000..55788f915 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-target-link-phase.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-ios-catalyst.cmake new file mode 100644 index 000000000..1682ed310 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-target-library-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-target.cmake) diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-catalyst.cmake new file mode 100644 index 000000000..55788f915 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-executable-target-link-phase.cmake) diff --git a/Tests/RunCMake/XcFramework/create-framework-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-framework-ios-catalyst.cmake new file mode 100644 index 000000000..e5dcb9f19 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-framework-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-framework.cmake) diff --git a/Tests/RunCMake/XcFramework/create-library-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/create-library-ios-catalyst.cmake new file mode 100644 index 000000000..31128c1e5 --- /dev/null +++ b/Tests/RunCMake/XcFramework/create-library-ios-catalyst.cmake @@ -0,0 +1,2 @@ +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(create-library.cmake) diff --git a/Tests/RunCMake/XcFramework/export-common.cmake b/Tests/RunCMake/XcFramework/export-common.cmake index 844b7d3b5..2e2e5ee07 100644 --- a/Tests/RunCMake/XcFramework/export-common.cmake +++ b/Tests/RunCMake/XcFramework/export-common.cmake @@ -36,6 +36,7 @@ generate_apple_platform_selection_file(mylib-config-top.cmake INSTALL_DESTINATION lib/cmake/mylib MACOS_INCLUDE_FILE lib/macos/cmake/mylib/mylib-config.cmake IOS_INCLUDE_FILE lib/ios/cmake/mylib/mylib-config.cmake + IOS_CATALYST_INCLUDE_FILE lib/ios-catalyst/cmake/mylib/mylib-config.cmake IOS_SIMULATOR_INCLUDE_FILE lib/ios-simulator/cmake/mylib/mylib-config.cmake ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-top.cmake DESTINATION lib/cmake/mylib RENAME mylib-config.cmake) diff --git a/Tests/RunCMake/XcFramework/export-ios-catalyst.cmake b/Tests/RunCMake/XcFramework/export-ios-catalyst.cmake new file mode 100644 index 000000000..28a7a203a --- /dev/null +++ b/Tests/RunCMake/XcFramework/export-ios-catalyst.cmake @@ -0,0 +1,4 @@ +set(platform_name ios-catalyst) +set(platform_arg IOS_CATALYST_INCLUDE_FILE) +set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0") +include(export-common.cmake) diff --git a/Tests/RunCMake/XcodeProject-Device/CMakeLists.txt b/Tests/RunCMake/XcodeProject-Device/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/XcodeProject-Device/CMakeLists.txt +++ b/Tests/RunCMake/XcodeProject-Device/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/XcodeProject/CMakeLists.txt b/Tests/RunCMake/XcodeProject/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/XcodeProject/CMakeLists.txt +++ b/Tests/RunCMake/XcodeProject/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_custom_command/CMP0175-NEW-result.txt b/Tests/RunCMake/add_custom_command/CMP0175-NEW-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/add_custom_command/CMP0175-NEW-stderr.txt b/Tests/RunCMake/add_custom_command/CMP0175-NEW-stderr.txt new file mode 100644 index 000000000..c3569713e --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-NEW-stderr.txt @@ -0,0 +1,29 @@ +CMake Error at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + The following keywords are not supported when using + add_custom_command\(TARGET\): DEPENDS, DEPENDS_EXPLICIT_ONLY, DEPFILE, + JOB_POOL, MAIN_DEPENDENCY +Call Stack \(most recent call first\): + CMP0175-NEW\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) + + +CMake Error at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + The following keywords are not supported when using + add_custom_command\(TARGET\): IMPLICIT_DEPENDS, USES_TERMINAL +Call Stack \(most recent call first\): + CMP0175-NEW\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) + + +CMake Error at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. +Call Stack \(most recent call first\): + CMP0175-NEW\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) + + +CMake Error at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. +Call Stack \(most recent call first\): + CMP0175-NEW\.cmake:2 \(include\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/add_custom_command/CMP0175-NEW.cmake b/Tests/RunCMake/add_custom_command/CMP0175-NEW.cmake new file mode 100644 index 000000000..d8cb4fb90 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0175 NEW) +include(CMP0175.cmake) diff --git a/Tests/RunCMake/add_custom_command/CMP0175-OLD.cmake b/Tests/RunCMake/add_custom_command/CMP0175-OLD.cmake new file mode 100644 index 000000000..a66c02d39 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0175 OLD) +include(CMP0175.cmake) diff --git a/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt b/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt new file mode 100644 index 000000000..f49dd7344 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-WARN-stderr.txt @@ -0,0 +1,72 @@ +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + The following keywords are not supported when using + add_custom_command\(TARGET\): DEPENDS, DEPENDS_EXPLICIT_ONLY, DEPFILE, + JOB_POOL, MAIN_DEPENDENCY\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + The following keywords are not supported when using + add_custom_command\(TARGET\): IMPLICIT_DEPENDS, USES_TERMINAL\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. Assuming + POST_BUILD to preserve backward compatibility\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + Exactly one of PRE_BUILD, PRE_LINK, or POST_BUILD must be given\. Assuming + POST_BUILD to preserve backward compatibility\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + At least one COMMAND must be given\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0175\.cmake:[0-9]+ \(add_custom_command\): + The following keywords are not supported when using + add_custom_command\(OUTPUT\): OUTPUTS, POST_BUILD, PRE_BUILD, PRE_LINK, + SOURCE\. + + Policy CMP0175 is not set: add_custom_command\(\) rejects invalid arguments\. + Run "cmake --help-policy CMP0175" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0175-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/add_custom_command/CMP0175-WARN.cmake b/Tests/RunCMake/add_custom_command/CMP0175-WARN.cmake new file mode 100644 index 000000000..cd89b5358 --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175-WARN.cmake @@ -0,0 +1 @@ +include(CMP0175.cmake) diff --git a/Tests/RunCMake/add_custom_command/CMP0175.cmake b/Tests/RunCMake/add_custom_command/CMP0175.cmake new file mode 100644 index 000000000..0c20f4e3b --- /dev/null +++ b/Tests/RunCMake/add_custom_command/CMP0175.cmake @@ -0,0 +1,65 @@ +enable_language(CXX) +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.cpp "int main() {}") +add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.cpp) + +#============================================================================ +# add_custom_command(TARGET) +#============================================================================ + +# Unsupported keywords. Need to test them in batches to avoid other checks. +add_custom_command(TARGET main + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E true + + # None of the following are allowed for the TARGET form + + #APPEND # Has its own check requiring OUTPUT to be set + #CODEGEN # Other checks will fail before the CMP0175 check + DEPENDS valueDoesNotMatterHere + DEPENDS_EXPLICIT_ONLY YES + DEPFILE valueDoesNotMatterHere + #IMPLICIT_DEPENDS # Earlier check fails when DEPFILE is present + JOB_POOL valueDoesNotMatterHere + MAIN_DEPENDENCY valueDoesNotMatterHere + #OUTPUT # Other checks will fail before the CMP0175 check + #OUTPUTS # Special case, not a documented keyword (used for deprecated form) + #SOURCE # Old signature, special handling makes it hard to check + #USES_TERMINAL +) +add_custom_command(TARGET main + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E true + # Has to be tested separately due to separate check for clash with DEPFILE + IMPLICIT_DEPENDS valueDoesNotMatterHere + # Has to be tested separately due to separate check for clash with JOB_POOL + USES_TERMINAL NO +) + +# Missing any PRE_BUILD, PRE_LINK, or POST_BUILD +add_custom_command(TARGET main + COMMAND ${CMAKE_COMMAND} -E true +) + +# More than one of PRE_BUILD, PRE_LINK, or POST_BUILD +add_custom_command(TARGET main + PRE_BUILD PRE_LINK POST_BUILD + COMMAND ${CMAKE_COMMAND} -E true +) + +# Missing COMMAND +add_custom_command(TARGET main + POST_BUILD + COMMENT "Need at least 4 arguments, so added this comment" +) + +#============================================================================ +# add_custom_command(OUTPUT) +#============================================================================ + +add_custom_command(OUTPUT blah.txt + OUTPUTS + POST_BUILD + PRE_BUILD + PRE_LINK + SOURCE +) diff --git a/Tests/RunCMake/add_custom_command/CMakeLists.txt b/Tests/RunCMake/add_custom_command/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/add_custom_command/CMakeLists.txt +++ b/Tests/RunCMake/add_custom_command/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_custom_command/RemoveEmptyCommands.cmake b/Tests/RunCMake/add_custom_command/RemoveEmptyCommands.cmake index eb190cc99..5354d0ac1 100644 --- a/Tests/RunCMake/add_custom_command/RemoveEmptyCommands.cmake +++ b/Tests/RunCMake/add_custom_command/RemoveEmptyCommands.cmake @@ -9,14 +9,17 @@ add_executable(exe "${main_file}") # add one command for all and one for debug only add_custom_command(TARGET exe + POST_BUILD COMMAND "cmd_1" "cmd_1_arg" COMMAND $<$:cmd_1_dbg> $<$:cmd_1_dbg_arg>) # add command for debug only add_custom_command(TARGET exe + POST_BUILD COMMAND $<$:cmd_2_dbg> $<$:cmd_2_dbg_arg>) # add separate commands for configurations add_custom_command(TARGET exe + POST_BUILD COMMAND $<$:cmd_3_dbg> $<$:cmd_3_dbg_arg> COMMAND $<$:cmd_3_rel> $<$:cmd_3_rel_arg>) diff --git a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake index 46e7baea1..820591c93 100644 --- a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake @@ -1,5 +1,8 @@ include(RunCMake) +run_cmake(CMP0175-OLD) +run_cmake(CMP0175-WARN) +run_cmake(CMP0175-NEW) run_cmake(AppendLiteralQuotes) run_cmake(AppendNoOutput) run_cmake(AppendNotOutput) diff --git a/Tests/RunCMake/add_custom_command/TargetImported.cmake b/Tests/RunCMake/add_custom_command/TargetImported.cmake index c0f2d66a0..1bfd3f929 100644 --- a/Tests/RunCMake/add_custom_command/TargetImported.cmake +++ b/Tests/RunCMake/add_custom_command/TargetImported.cmake @@ -1,2 +1,2 @@ add_library(TargetImported UNKNOWN IMPORTED) -add_custom_command(TARGET TargetImported COMMAND ${CMAKE_COMMAND} -E echo tada) +add_custom_command(TARGET TargetImported POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo tada) diff --git a/Tests/RunCMake/add_custom_command/TargetNotInDir.cmake b/Tests/RunCMake/add_custom_command/TargetNotInDir.cmake index a5510262a..0bd4fbd4d 100644 --- a/Tests/RunCMake/add_custom_command/TargetNotInDir.cmake +++ b/Tests/RunCMake/add_custom_command/TargetNotInDir.cmake @@ -1,2 +1,2 @@ add_subdirectory(TargetNotInDir) -add_custom_command(TARGET TargetNotInDir COMMAND ${CMAKE_COMMAND} -E echo tada) +add_custom_command(TARGET TargetNotInDir POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo tada) diff --git a/Tests/RunCMake/add_custom_target/CMakeLists.txt b/Tests/RunCMake/add_custom_target/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/add_custom_target/CMakeLists.txt +++ b/Tests/RunCMake/add_custom_target/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_dependencies/CMakeLists.txt b/Tests/RunCMake/add_dependencies/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/add_dependencies/CMakeLists.txt +++ b/Tests/RunCMake/add_dependencies/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_executable/CMakeLists.txt b/Tests/RunCMake/add_executable/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/add_executable/CMakeLists.txt +++ b/Tests/RunCMake/add_executable/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_library/CMakeLists.txt b/Tests/RunCMake/add_library/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/add_library/CMakeLists.txt +++ b/Tests/RunCMake/add_library/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/add_subdirectory/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/CMakeLists.txt index b5d72625d..56770f452 100644 --- a/Tests/RunCMake/add_subdirectory/CMakeLists.txt +++ b/Tests/RunCMake/add_subdirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) # Have to set policy here due to policy scope if(DEFINED CMP0082_VALUE) diff --git a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake index 801abaeb9..f29697cab 100644 --- a/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake +++ b/Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake @@ -53,7 +53,7 @@ elseif(RunCMake_GENERATOR MATCHES "Make") set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY ${RunCMake_BINARY_DIR}/ExcludeFromAll-build/ExcludeFromAll) run_cmake_command(ExcludeFromAll-build-sub "${RunCMake_MAKE_PROGRAM}") -elseif(RunCMake_GENERATOR MATCHES "^Visual Studio [1-9][0-9]") +elseif(RunCMake_GENERATOR MATCHES "Visual Studio") set(RunCMake-check-file ExcludeFromAll/check-sub.cmake) run_cmake_command(ExcludeFromAll-build-sub ${CMAKE_COMMAND} --build ExcludeFromAll --config Debug) elseif(RunCMake_GENERATOR STREQUAL "Xcode") diff --git a/Tests/RunCMake/alias_targets/CMakeLists.txt b/Tests/RunCMake/alias_targets/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/alias_targets/CMakeLists.txt +++ b/Tests/RunCMake/alias_targets/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/build_command/CMakeLists.txt b/Tests/RunCMake/build_command/CMakeLists.txt index 1319aecc3..9bfae1237 100644 --- a/Tests/RunCMake/build_command/CMakeLists.txt +++ b/Tests/RunCMake/build_command/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(NOT NoProject) project(${RunCMake_TEST} NONE) endif() diff --git a/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake index f942d9554..f9e22c5c3 100644 --- a/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake +++ b/Tests/RunCMake/cmake_language/Experimental/WindowsKernelModeDriver-set.cmake @@ -1,5 +1,5 @@ set(CMAKE_EXPERIMENTAL_WINDOWS_KERNEL_MODE_DRIVER - "5c2d848d-4efa-4529-a768-efd57171bf68") + "fac18f65-504e-4dbb-b068-f356bb1f2ddb") cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED "WindowsKernelModeDriver" diff --git a/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt b/Tests/RunCMake/cmake_minimum_required/Before3_10-stderr.txt similarity index 61% rename from Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt rename to Tests/RunCMake/cmake_minimum_required/Before3_10-stderr.txt index 79812352f..570968425 100644 --- a/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt +++ b/Tests/RunCMake/cmake_minimum_required/Before3_10-stderr.txt @@ -1,5 +1,5 @@ -^CMake Deprecation Warning at Before3_5\.cmake:1 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of +^CMake Deprecation Warning at Before3_10\.cmake:1 \(cmake_minimum_required\): + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell @@ -7,8 +7,8 @@ Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + -CMake Deprecation Warning at Before3_5\.cmake:2 \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of +CMake Deprecation Warning at Before3_10\.cmake:2 \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell @@ -16,8 +16,8 @@ CMake Deprecation Warning at Before3_5\.cmake:2 \(cmake_policy\): Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + -CMake Deprecation Warning at Before3_5\.cmake:6 \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of +CMake Deprecation Warning at Before3_10\.cmake:6 \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake b/Tests/RunCMake/cmake_minimum_required/Before3_10.cmake similarity index 67% rename from Tests/RunCMake/cmake_minimum_required/Before3_5.cmake rename to Tests/RunCMake/cmake_minimum_required/Before3_10.cmake index 220e359d4..0b010ef87 100644 --- a/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake +++ b/Tests/RunCMake/cmake_minimum_required/Before3_10.cmake @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 2.8.11) +cmake_minimum_required(VERSION 3.9) cmake_policy(VERSION 2.6) cmake_policy(PUSH) cmake_policy(VERSION 2.6) # simulate pre-3.18 install(EXPORT)-generated call cmake_policy(POP) -cmake_policy(VERSION 2.8.11) +cmake_policy(VERSION 3.9) diff --git a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt index 8eb57486a..30c0273a6 100644 --- a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt +++ b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt index 3eb980a58..816cdacba 100644 --- a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt +++ b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CompatBefore24\.cmake:1 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake index 36c44cbc7..2851c7242 100644 --- a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake @@ -4,7 +4,7 @@ run_cmake(Before24) run_cmake(CompatBefore24) run_cmake(Future) run_cmake(PolicyBefore24) -run_cmake(Before3_5) +run_cmake(Before3_10) run_cmake(Range) run_cmake(RangeBad) run_cmake(Unknown) diff --git a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt +++ b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt new file mode 100644 index 000000000..0815ed39d --- /dev/null +++ b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN-stderr.txt @@ -0,0 +1,11 @@ +Testing CMP0174 = NEW +Testing CMP0174 = OLD +Testing CMP0174 = WARN +CMake Warning \(dev\) at CornerCasesArgvN\.cmake:[0-9]+ \(cmake_parse_arguments\): + The P1 keyword was followed by an empty string or no value at all\. Policy + CMP0174 is not set, so cmake_parse_arguments\(\) will unset the arg_P1 + variable rather than setting it to an empty string\. +Call Stack \(most recent call first\): + CornerCasesArgvN\.cmake:[0-9]+ \(test_cmp0174_warn\) + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN.cmake b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN.cmake index 807ed03e1..487a48fc4 100644 --- a/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN.cmake +++ b/Tests/RunCMake/cmake_parse_arguments/CornerCasesArgvN.cmake @@ -51,3 +51,76 @@ function(test4) TEST(upref_UNPARSED_ARGUMENTS foo "bar;none") endfunction() test4(MULTI foo bar\\ none) + +# Single-value keyword with empty string as value +message(NOTICE "Testing CMP0174 = NEW") +block(SCOPE_FOR POLICIES) + cmake_policy(SET CMP0174 NEW) + function(test_cmp0174_new_missing) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P2") + TEST(arg_P1 "") + TEST(arg_P2 "") + endfunction() + test_cmp0174_new_missing(P1 "" P2) + + function(test_cmp0174_new_no_missing) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2" "") + TEST(arg_KEYWORDS_MISSING_VALUES "UNDEFINED") + TEST(arg_P1 "") + TEST(arg_P2 "UNDEFINED") + endfunction() + test_cmp0174_new_no_missing(P1 "") + + # For repeated keywords, the keyword will be reported as missing a value if + # any one of them is missing a value. + function(test_cmp0174_new_repeated_arg) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P1") + TEST(arg_P1 "abc") + TEST(arg_P2 "UNDEFINED") + endfunction() + test_cmp0174_new_repeated_arg(P1 P1 abc) +endblock() + +message(NOTICE "Testing CMP0174 = OLD") +block(SCOPE_FOR POLICIES) + cmake_policy(SET CMP0174 OLD) + function(test_cmp0174_old) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2;P3" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P2") + TEST(arg_P1 "UNDEFINED") + TEST(arg_P2 "UNDEFINED") + TEST(arg_P3 "UNDEFINED") + endfunction() + test_cmp0174_old(P1 "" P2) + + function(test_cmp0174_old_repeated_arg) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P1") + TEST(arg_P1 "abc") + TEST(arg_P2 "UNDEFINED") + endfunction() + test_cmp0174_old_repeated_arg(P1 P1 abc) +endblock() + +message(NOTICE "Testing CMP0174 = WARN") +block(SCOPE_FOR POLICIES) + cmake_policy(VERSION 3.30) # WARN + function(test_cmp0174_warn) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2;P3" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P2") + TEST(arg_P1 "UNDEFINED") + TEST(arg_P2 "UNDEFINED") + TEST(arg_P3 "UNDEFINED") + endfunction() + test_cmp0174_warn(P1 "" P2) + + function(test_cmp0174_warn_repeated_arg) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "P1;P2" "") + TEST(arg_KEYWORDS_MISSING_VALUES "P1") + TEST(arg_P1 "abc") + TEST(arg_P2 "UNDEFINED") + endfunction() + test_cmp0174_warn_repeated_arg(P1 P1 abc) +endblock() diff --git a/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake b/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake index 9ce99b8a4..23b804e35 100644 --- a/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake +++ b/Tests/RunCMake/cmake_parse_arguments/test_utils.cmake @@ -13,6 +13,8 @@ function(TEST variable) if(DEFINED ${variable}) message(FATAL_ERROR "'${variable}' shall be undefined but has value '${${variable}}'") endif() + elseif(NOT DEFINED ${variable}) + message(FATAL_ERROR "'${variable}' should be defined but is not") elseif("${expected}" STREQUAL "FALSE") if(NOT ${variable} STREQUAL "FALSE") message(FATAL_ERROR "'${variable}' shall be FALSE") @@ -23,7 +25,7 @@ function(TEST variable) endif() else() if(NOT ${variable} STREQUAL "${expected}") - message(FATAL_ERROR "'${variable}' shall be '${expected}'") + message(FATAL_ERROR "'${variable}' shall be '${expected}' but has value '${${variable}}'") endif() endif() endif() diff --git a/Tests/RunCMake/cmake_path/GET.cmake b/Tests/RunCMake/cmake_path/GET.cmake index 463bc471b..b0bda98ee 100644 --- a/Tests/RunCMake/cmake_path/GET.cmake +++ b/Tests/RunCMake/cmake_path/GET.cmake @@ -245,4 +245,71 @@ if (NOT output STREQUAL ".file") list (APPEND errors "STEM returns bad data: ${output}") endif() +################################################## +## tests for subcommands' handling of "." and ".." +################################################## +if (WIN32) + set (dot "C:/aa/bb/.") + set (dotdot "C:/ee/ff/..") +else() + set (dot "/aa/bb/.") + set (dotdot "/ee/ff/..") +endif() + +cmake_path(GET dot FILENAME dot_out) +cmake_path(GET dotdot FILENAME dotdot_out) + +if (NOT dot_out STREQUAL ".") + list(APPEND errors "FILENAME returns bad data for '/.': ${dot_out}") +endif() +if (NOT dotdot_out STREQUAL "..") + list(APPEND errors "FILENAME returns bad data for '/..': ${dotdot_out}") +endif() + +cmake_path(GET dot STEM dot_out) +cmake_path(GET dotdot STEM dotdot_out) + +if (NOT dot_out STREQUAL ".") + list(APPEND errors "STEM returns bad data for '/.': ${dot_out}") +endif() +if (NOT dotdot_out STREQUAL "..") + list(APPEND errors "STEM returns bad data for '/..': ${dotdot_out}") +endif() + +cmake_path(GET dot STEM LAST_ONLY dot_out) +cmake_path(GET dotdot STEM LAST_ONLY dotdot_out) + +if (NOT dot_out STREQUAL ".") + list(APPEND errors + "STEM LAST_ONLY returns bad data for '/.': ${dot_out}") +endif() +if (NOT dotdot_out STREQUAL "..") + list(APPEND errors + "STEM LAST_ONLY returns bad data for '/..': ${dotdot_out}") +endif() + +cmake_path(GET dot EXTENSION dot_out) +cmake_path(GET dotdot EXTENSION dotdot_out) + +if (NOT dot_out STREQUAL "") + list(APPEND errors + "EXTENSION returns bad data for '/.': ${dot_out}") +endif() +if (NOT dotdot_out STREQUAL "") + list(APPEND errors + "EXTENSION returns bad data for '/..': ${dotdot_out}") +endif() + +cmake_path(GET dot EXTENSION LAST_ONLY dot_out) +cmake_path(GET dotdot EXTENSION LAST_ONLY dotdot_out) + +if (NOT dot_out STREQUAL "") + list(APPEND errors + "EXTENSION LAST_ONLY returns bad data for '/.': ${dot_out}") +endif() +if (NOT dotdot_out STREQUAL "") + list(APPEND errors + "EXTENSION LAST_ONLY returns bad data for '/..': ${dotdot_out}") +endif() + check_errors (GET ${errors}) diff --git a/Tests/RunCMake/cmake_pkg_config/CMakeLists.txt b/Tests/RunCMake/cmake_pkg_config/CMakeLists.txt new file mode 100644 index 000000000..b3f15ede1 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.30) +project(${RunCMake_TEST} NONE) + +set(CMAKE_PKG_CONFIG_PC_LIB_DIRS ${CMAKE_CURRENT_LIST_DIR}/PackageRoot) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/a.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/a.pc new file mode 100644 index 000000000..7492f305d --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/a.pc @@ -0,0 +1,3 @@ +Name: +Version: aa +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/one.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/one.pc new file mode 100644 index 000000000..ec939ff24 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/one.pc @@ -0,0 +1,3 @@ +Name: +Version: 11 +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/onedot.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/onedot.pc new file mode 100644 index 000000000..e7d3cfdd5 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/onedot.pc @@ -0,0 +1,3 @@ +Name: +Version: 1.1.1 +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/pseudo-empty.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/pseudo-empty.pc new file mode 100644 index 000000000..66d4163cb --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/pseudo-empty.pc @@ -0,0 +1,3 @@ +Name: +Version: ~0 +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/tilde.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/tilde.pc new file mode 100644 index 000000000..8d8b38d41 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/tilde.pc @@ -0,0 +1,3 @@ +Name: +Version: ~~1 +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/zeroone.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/zeroone.pc new file mode 100644 index 000000000..265e8aa49 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/VersionPackages/zeroone.pc @@ -0,0 +1,3 @@ +Name: +Version: 01 +Description: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/all-extract-fields.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/all-extract-fields.pc new file mode 100644 index 000000000..bd361fcd5 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/all-extract-fields.pc @@ -0,0 +1,15 @@ +Name: Extract All +Description: All flags example +Version: 1.0.0 + +Conflicts: Alpha Beta +Provides: Gamma Delta + +Requires: Epsilon Zea +Requires.private: Eta Theta + +Cflags: Iota -IKappa Lambda -IMu +Cflags.private: Nu -IXi Omnicron -IPi + +Libs: Rho -LSigma -lTau Upsilon -LPhi -lChi +Libs.private: Psi -LOmega -lMoe Larry -LCurly -lShemp diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/bar.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/bar.pc new file mode 100644 index 000000000..bf2942cda --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/bar.pc @@ -0,0 +1,3 @@ +Name: Bar +Description: Bar Description +Version: 1.0.0 diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/baz.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/baz.pc new file mode 100644 index 000000000..f1152d42e --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/baz.pc @@ -0,0 +1,3 @@ +Name: Baz +Description: Baz Description +Version: 1.0.0 diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-bothcase-f.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-bothcase-f.pc new file mode 100644 index 000000000..d242c11fd --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-bothcase-f.pc @@ -0,0 +1,6 @@ +Name: Cflags Bothcase +Description: The f is lowercase and uppercase +Version: 1.0.0 + +Cflags: lowercase +CFlags: uppercase diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-lowercase-f.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-lowercase-f.pc new file mode 100644 index 000000000..1640d8b5f --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-lowercase-f.pc @@ -0,0 +1,5 @@ +Name: Cflags Lowercase +Description: The f is lowercase +Version: 1.0.0 + +Cflags: lowercase diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-uppercase-f.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-uppercase-f.pc new file mode 100644 index 000000000..0f7c3087b --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/cflags-uppercase-f.pc @@ -0,0 +1,5 @@ +Name: CFlags Uppercase +Description: The f is uppercase +Version: 1.0.0 + +CFlags: uppercase diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/empty-key.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/empty-key.pc new file mode 100644 index 000000000..93ec6949a --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/empty-key.pc @@ -0,0 +1,3 @@ +Name: +Description: +Version: diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/foo.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/foo.pc new file mode 100644 index 000000000..0a98509e1 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/foo.pc @@ -0,0 +1,3 @@ +Name: Foo +Description: Foo Description +Version: 1.0.0 diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/invalid.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/invalid.pc new file mode 100644 index 000000000..be1f43312 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/invalid.pc @@ -0,0 +1,4 @@ +Name: Invalid +Description: Will cause a parse error +Version: 1.0.0 +BrokenKey diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-description.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-description.pc new file mode 100644 index 000000000..69007b5f7 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-description.pc @@ -0,0 +1,2 @@ +Name: name +Version: version diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-name.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-name.pc new file mode 100644 index 000000000..5b878c0e0 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-name.pc @@ -0,0 +1,2 @@ +Description: description +Version: version diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-version.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-version.pc new file mode 100644 index 000000000..850e26e64 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/no-version.pc @@ -0,0 +1,2 @@ +Name: name +Description: description diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux-uninstalled.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux-uninstalled.pc new file mode 100644 index 000000000..8f9462f82 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux-uninstalled.pc @@ -0,0 +1,5 @@ +Name: Qux +Description: Qux Description +Version: 1.0.0 + +Cflags: QuxUninstalled diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux.pc new file mode 100644 index 000000000..6cf66b1ca --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/qux.pc @@ -0,0 +1,5 @@ +Name: Qux +Description: Qux Description +Version: 1.0.0 + +Cflags: QuxInstalled diff --git a/Tests/RunCMake/cmake_pkg_config/PackageRoot/relocate.pc b/Tests/RunCMake/cmake_pkg_config/PackageRoot/relocate.pc new file mode 100644 index 000000000..5abce769b --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/PackageRoot/relocate.pc @@ -0,0 +1,6 @@ +Name: Relocate +Description: For testing relocation and flag mangling +Version: 1.0.0 + +Cflags: -I/Alpha Beta -I/Gamma +Libs: -L/Delta Epsilon -L/Zeta diff --git a/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake b/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake new file mode 100644 index 000000000..4f9200b12 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/RunCMakeTest.cmake @@ -0,0 +1,18 @@ +include(RunCMake) + +set(cmd ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR} -G ${RunCMake_GENERATOR}) + +foreach(strictness IN ITEMS STRICT PERMISSIVE BEST_EFFORT) + run_cmake_command(TestStrictness-${strictness} ${cmd} + -DRunCMake_TEST=TestStrictness -DSTRICTNESS=${strictness} + ) +endforeach() + +run_cmake(TestEnv) +run_cmake(TestExtract) +run_cmake(TestMangle) +run_cmake(TestQuiet) +run_cmake(TestRequired) +run_cmake(TestReroot) +run_cmake(TestUninstalled) +run_cmake(TestVersion) diff --git a/Tests/RunCMake/cmake_pkg_config/TestEnv-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestEnv-stderr.txt new file mode 100644 index 000000000..67713c903 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestEnv-stderr.txt @@ -0,0 +1,15 @@ +Includes: -I/Alpha;-I/Gamma +LibDirs: -L/Delta;-L/Zeta +Cflags: QuxInstalled +PC_LIB_DIRS: Alpha;Beta +PC_PATH: [^ +]*/PackageRoot +DISABLE_UNINSTALLED: ON +SYSROOT_DIR: Delta +TOP_BUILD_DIR: Epsilon +SYSTEM_INCLUDE_DIRS: Zeta;Eta +SYSTEM_LIB_DIRS: Theta;Iota +ALLOW_SYSTEM_INCLUDES: ON +ALLOW_SYSTEM_LIBRARIES: ON +PKGCONF_INCLUDES: Mu;Nu;Xi;Omnicron;Pi;Rho;Sigma;Tau +PKGCONF_LIB_DIRS: Upsilon;Phi diff --git a/Tests/RunCMake/cmake_pkg_config/TestEnv.cmake b/Tests/RunCMake/cmake_pkg_config/TestEnv.cmake new file mode 100644 index 000000000..dcbb9584b --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestEnv.cmake @@ -0,0 +1,75 @@ +set(CMAKE_PKG_CONFIG_PC_LIB_DIRS) + +set(ENV{PKG_CONFIG_PATH} ${CMAKE_CURRENT_LIST_DIR}/PackageRoot) + +if(WIN32) + set(sep ";") +else() + set(sep ":") +endif() + +set(ENV{PKG_CONFIG_LIBDIR} "Alpha${sep}Beta") +set(ENV{PKG_CONFIG_DISABLE_UNINSTALLED} Gamma) +set(ENV{PKG_CONFIG_SYSROOT_DIR} Delta) +set(ENV{PKG_CONFIG_TOP_BUILD_DIR} Epsilon) +set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "Zeta${sep}Eta") +set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "Theta${sep}Iota") +set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} Kappa) +set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} Lambda) + +set(ENV{CPATH} "Mu${sep}Nu") +set(ENV{C_INCLUDE_PATH} "Xi${sep}Omnicron") +set(ENV{CPLUS_INCLUDE_PATH} "Pi${sep}Rho") + +if(WIN32) + set(ENV{OBJC_INCLUDE_PATH} Sigma) + set(ENV{INCLUDE} Tau) +else() + set(ENV{OBJC_INCLUDE_PATH} Sigma:Tau) +endif() + +set(ENV{LIBRARY_PATH} "Upsilon${sep}Phi") + +cmake_pkg_config( + EXTRACT relocate + ENV_MODE IGNORE + PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot + SYSTEM_INCLUDE_DIRS /Alpha + SYSTEM_LIBRARY_DIRS /Beta +) + +# Shouldn't mangle, ALLOW_SYSTEM_* should default to on under ENV IGNORE +message("Includes: ${CMAKE_PKG_CONFIG_INCLUDES}") +message("LibDirs: ${CMAKE_PKG_CONFIG_LIBDIRS}") + +cmake_pkg_config( + EXTRACT qux + ENV_MODE IGNORE + PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot +) + +# Shouldn't find uninstalled package +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") + +cmake_pkg_config( + EXTRACT foo + ENV_MODE FDO +) + +message("PC_LIB_DIRS: ${CMAKE_PKG_CONFIG_PC_LIB_DIRS}") +message("PC_PATH: ${CMAKE_PKG_CONFIG_PC_PATH}") +message("DISABLE_UNINSTALLED: ${CMAKE_PKG_CONFIG_DISABLE_UNINSTALLED}") +message("SYSROOT_DIR: ${CMAKE_PKG_CONFIG_SYSROOT_DIR}") +message("TOP_BUILD_DIR: ${CMAKE_PKG_CONFIG_TOP_BUILD_DIR}") +message("SYSTEM_INCLUDE_DIRS: ${CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS}") +message("SYSTEM_LIB_DIRS: ${CMAKE_PKG_CONFIG_SYS_LIB_DIRS}") +message("ALLOW_SYSTEM_INCLUDES: ${CMAKE_PKG_CONFIG_ALLOW_SYS_INCLUDES}") +message("ALLOW_SYSTEM_LIBRARIES: ${CMAKE_PKG_CONFIG_ALLOW_SYS_LIBS}") + +cmake_pkg_config( + EXTRACT foo + ENV_MODE PKGCONF +) + +message("PKGCONF_INCLUDES: ${CMAKE_PKG_CONFIG_PKGCONF_INCLUDES}") +message("PKGCONF_LIB_DIRS: ${CMAKE_PKG_CONFIG_PKGCONF_LIB_DIRS}") diff --git a/Tests/RunCMake/cmake_pkg_config/TestExtract-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestExtract-stderr.txt new file mode 100644 index 000000000..42e453470 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestExtract-stderr.txt @@ -0,0 +1,21 @@ +Name: Extract All +Description: All flags example +Version: 1.0.0 +Conflicts: Alpha;Beta +Provides: Gamma;Delta +Requires: Epsilon;Zea +Requires.private: Eta;Theta +Cflags: Iota -IKappa Lambda -IMu +Includes: -IKappa;-IMu +CompileOptions: Iota;Lambda +Cflags.private: Nu -IXi Omnicron -IPi +Includes.private: -IXi;-IPi +CompileOptions.private: Nu;Omnicron +Libs: Rho -LSigma -lTau Upsilon -LPhi -lChi +LibDirs: -LSigma;-LPhi +LibNames: -lTau;-lChi +LinkOptions: Rho;Upsilon +Libs.private: Psi -LOmega -lMoe Larry -LCurly -lShemp +LibDirs.private: -LOmega;-LCurly +LibNames.private: -lMoe;-lShemp +LinkOptions.private: Psi;Larry diff --git a/Tests/RunCMake/cmake_pkg_config/TestExtract.cmake b/Tests/RunCMake/cmake_pkg_config/TestExtract.cmake new file mode 100644 index 000000000..eb0596692 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestExtract.cmake @@ -0,0 +1,29 @@ +cmake_pkg_config(EXTRACT all-extract-fields) + +message("Name: ${CMAKE_PKG_CONFIG_NAME}") +message("Description: ${CMAKE_PKG_CONFIG_DESCRIPTION}") +message("Version: ${CMAKE_PKG_CONFIG_VERSION}") + +message("Conflicts: ${CMAKE_PKG_CONFIG_CONFLICTS}") +message("Provides: ${CMAKE_PKG_CONFIG_PROVIDES}") + +message("Requires: ${CMAKE_PKG_CONFIG_REQUIRES}") +message("Requires.private: ${CMAKE_PKG_CONFIG_REQUIRES_PRIVATE}") + +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") +message("Includes: ${CMAKE_PKG_CONFIG_INCLUDES}") +message("CompileOptions: ${CMAKE_PKG_CONFIG_COMPILE_OPTIONS}") + +message("Cflags.private: ${CMAKE_PKG_CONFIG_CFLAGS_PRIVATE}") +message("Includes.private: ${CMAKE_PKG_CONFIG_INCLUDES_PRIVATE}") +message("CompileOptions.private: ${CMAKE_PKG_CONFIG_COMPILE_OPTIONS_PRIVATE}") + +message("Libs: ${CMAKE_PKG_CONFIG_LIBS}") +message("LibDirs: ${CMAKE_PKG_CONFIG_LIBDIRS}") +message("LibNames: ${CMAKE_PKG_CONFIG_LIBNAMES}") +message("LinkOptions: ${CMAKE_PKG_CONFIG_LINK_OPTIONS}") + +message("Libs.private: ${CMAKE_PKG_CONFIG_LIBS_PRIVATE}") +message("LibDirs.private: ${CMAKE_PKG_CONFIG_LIBDIRS_PRIVATE}") +message("LibNames.private: ${CMAKE_PKG_CONFIG_LIBNAMES_PRIVATE}") +message("LinkOptions.private: ${CMAKE_PKG_CONFIG_LINK_OPTIONS_PRIVATE}") diff --git a/Tests/RunCMake/cmake_pkg_config/TestMangle-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestMangle-stderr.txt new file mode 100644 index 000000000..75557fc2c --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestMangle-stderr.txt @@ -0,0 +1,8 @@ +Cflags: Beta -I/Gamma +Includes: -I/Gamma +Libs: Epsilon -L/Zeta +LibDirs: -L/Zeta +Cflags: -I/Alpha Beta -I/Gamma +Includes: -I/Alpha;-I/Gamma +Libs: -L/Delta Epsilon -L/Zeta +LibDirs: -L/Delta;-L/Zeta diff --git a/Tests/RunCMake/cmake_pkg_config/TestMangle.cmake b/Tests/RunCMake/cmake_pkg_config/TestMangle.cmake new file mode 100644 index 000000000..b880d31a5 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestMangle.cmake @@ -0,0 +1,22 @@ +set(CMAKE_PKG_CONFIG_SYS_INCLUDE_DIRS /Alpha) +set(CMAKE_PKG_CONFIG_SYS_LIB_DIRS /Delta) + +cmake_pkg_config(EXTRACT relocate) + +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") +message("Includes: ${CMAKE_PKG_CONFIG_INCLUDES}") + +message("Libs: ${CMAKE_PKG_CONFIG_LIBS}") +message("LibDirs: ${CMAKE_PKG_CONFIG_LIBDIRS}") + +cmake_pkg_config( + EXTRACT relocate + ALLOW_SYSTEM_INCLUDES ON + ALLOW_SYSTEM_LIBS ON +) + +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") +message("Includes: ${CMAKE_PKG_CONFIG_INCLUDES}") + +message("Libs: ${CMAKE_PKG_CONFIG_LIBS}") +message("LibDirs: ${CMAKE_PKG_CONFIG_LIBDIRS}") diff --git a/Tests/RunCMake/cmake_pkg_config/TestQuiet.cmake b/Tests/RunCMake/cmake_pkg_config/TestQuiet.cmake new file mode 100644 index 000000000..ac72ab239 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestQuiet.cmake @@ -0,0 +1,29 @@ +cmake_pkg_config( + EXTRACT foo + QUIET + STRICTNESS STRICT +) + +cmake_pkg_config( + EXTRACT no-name + QUIET + STRICTNESS STRICT +) + +cmake_pkg_config( + EXTRACT empty-key + QUIET + STRICTNESS STRICT +) + +cmake_pkg_config( + EXTRACT cflags-bothcase-f + QUIET + STRICTNESS STRICT +) + +cmake_pkg_config( + EXTRACT does-not-exist + QUIET + STRICTNESS STRICT +) diff --git a/Tests/RunCMake/cmake_pkg_config/TestRequired-result.txt b/Tests/RunCMake/cmake_pkg_config/TestRequired-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestRequired-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt new file mode 100644 index 000000000..d7f5158f5 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestRequired-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at TestRequired.cmake:[0-9]+ \(cmake_pkg_config\): + cmake_pkg_config Could not find 'does-not-exist' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_pkg_config/TestRequired.cmake b/Tests/RunCMake/cmake_pkg_config/TestRequired.cmake new file mode 100644 index 000000000..fcc72ce91 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestRequired.cmake @@ -0,0 +1,9 @@ +cmake_pkg_config( + EXTRACT foo + REQUIRED +) + +cmake_pkg_config( + EXTRACT does-not-exist + REQUIRED +) diff --git a/Tests/RunCMake/cmake_pkg_config/TestReroot-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestReroot-stderr.txt new file mode 100644 index 000000000..ab524d44b --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestReroot-stderr.txt @@ -0,0 +1,4 @@ +Cflags: -I/NewRoot/Alpha Beta -I/NewRoot/Gamma +Includes: -I/NewRoot/Alpha;-I/NewRoot/Gamma +Libs: -L/NewRoot/Delta Epsilon -L/NewRoot/Zeta +LibDirs: -L/NewRoot/Delta;-L/NewRoot/Zeta diff --git a/Tests/RunCMake/cmake_pkg_config/TestReroot.cmake b/Tests/RunCMake/cmake_pkg_config/TestReroot.cmake new file mode 100644 index 000000000..0f55558b5 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestReroot.cmake @@ -0,0 +1,10 @@ +cmake_pkg_config( + EXTRACT relocate + PC_SYSROOT_DIR /NewRoot +) + +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") +message("Includes: ${CMAKE_PKG_CONFIG_INCLUDES}") + +message("Libs: ${CMAKE_PKG_CONFIG_LIBS}") +message("LibDirs: ${CMAKE_PKG_CONFIG_LIBDIRS}") diff --git a/Tests/RunCMake/cmake_pkg_config/TestStrictness-BEST_EFFORT-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestStrictness-BEST_EFFORT-stderr.txt new file mode 100644 index 000000000..e18e88e7e --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestStrictness-BEST_EFFORT-stderr.txt @@ -0,0 +1,3 @@ +Cflags: lowercase +CFlags: uppercase +Cflags: lowercase uppercase diff --git a/Tests/RunCMake/cmake_pkg_config/TestStrictness-PERMISSIVE-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestStrictness-PERMISSIVE-stderr.txt new file mode 100644 index 000000000..2f4a69cae --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestStrictness-PERMISSIVE-stderr.txt @@ -0,0 +1,31 @@ +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-name.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-description.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-version.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Parsing failed for file[^ +]*(.)*/PackageRoot/invalid.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +Cflags: lowercase +CFlags: uppercase +Cflags: lowercase uppercase diff --git a/Tests/RunCMake/cmake_pkg_config/TestStrictness-STRICT-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestStrictness-STRICT-stderr.txt new file mode 100644 index 000000000..7329e8d15 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestStrictness-STRICT-stderr.txt @@ -0,0 +1,38 @@ +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-name.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-description.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/no-version.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Parsing failed for file[^ +]*(.)*/PackageRoot/invalid.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +Cflags: lowercase +CFlags: uppercase +CMake Warning at TestStrictness.cmake:[0-9]+ \(cmake_pkg_config\): + Resolution failed for file[^ +]*(.)*/PackageRoot/cflags-bothcase-f.pc' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +Cflags: diff --git a/Tests/RunCMake/cmake_pkg_config/TestStrictness.cmake b/Tests/RunCMake/cmake_pkg_config/TestStrictness.cmake new file mode 100644 index 000000000..d34f85ece --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestStrictness.cmake @@ -0,0 +1,51 @@ +cmake_pkg_config( + EXTRACT foo + STRICTNESS ${STRICTNESS} + REQUIRED +) + +cmake_pkg_config( + EXTRACT empty-key + STRICTNESS ${STRICTNESS} + REQUIRED +) + +cmake_pkg_config( + EXTRACT no-name + STRICTNESS ${STRICTNESS} +) + +cmake_pkg_config( + EXTRACT no-description + STRICTNESS ${STRICTNESS} +) + +cmake_pkg_config( + EXTRACT no-version + STRICTNESS ${STRICTNESS} +) + +cmake_pkg_config( + EXTRACT invalid + STRICTNESS ${STRICTNESS} +) + +cmake_pkg_config( + EXTRACT cflags-lowercase-f + STRICTNESS ${STRICTNESS} +) +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") + +set(CMAKE_PKG_CONFIG_CFLAGS) +cmake_pkg_config( + EXTRACT cflags-uppercase-f + STRICTNESS ${STRICTNESS} +) +message("CFlags: ${CMAKE_PKG_CONFIG_CFLAGS}") + +set(CMAKE_PKG_CONFIG_CFLAGS) +cmake_pkg_config( + EXTRACT cflags-bothcase-f + STRICTNESS ${STRICTNESS} +) +message("Cflags: ${CMAKE_PKG_CONFIG_CFLAGS}") diff --git a/Tests/RunCMake/cmake_pkg_config/TestUninstalled-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestUninstalled-stderr.txt new file mode 100644 index 000000000..25afa6874 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestUninstalled-stderr.txt @@ -0,0 +1,2 @@ +QuxUninstalled +QuxInstalled diff --git a/Tests/RunCMake/cmake_pkg_config/TestUninstalled.cmake b/Tests/RunCMake/cmake_pkg_config/TestUninstalled.cmake new file mode 100644 index 000000000..fafed11a4 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestUninstalled.cmake @@ -0,0 +1,10 @@ +cmake_pkg_config(EXTRACT qux) + +message(${CMAKE_PKG_CONFIG_CFLAGS}) + +cmake_pkg_config( + EXTRACT qux + DISABLE_UNINSTALLED ON +) + +message(${CMAKE_PKG_CONFIG_CFLAGS}) diff --git a/Tests/RunCMake/cmake_pkg_config/TestVersion-stderr.txt b/Tests/RunCMake/cmake_pkg_config/TestVersion-stderr.txt new file mode 100644 index 000000000..4b710d8f2 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestVersion-stderr.txt @@ -0,0 +1,103 @@ +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'a' version 'aa' does not meet version requirement 'aaa' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'a' version 'aa' does not meet version requirement '>bb' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'a' version 'aa' does not meet version requirement '>1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'empty-key' version '' does not meet version requirement '!=' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'empty-key' version '' does not meet version requirement '=0' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'one' version '11' does not meet version requirement '<1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'one' version '11' does not meet version requirement '>111' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'one' version '11' does not meet version requirement '>22' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'one' version '11' does not meet version requirement '1.2.1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'onedot' version '1.1.1' does not meet version requirement '> + 1.2.1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'onedot' version '1.1.1' does not meet exact version requirement + '01.01.01' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'pseudo-empty' version '~0' does not meet version requirement '=~' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'pseudo-empty' version '~0' does not meet version requirement + '!=~0' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'tilde' version '~~1' does not meet version requirement '>~1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) + + +CMake Warning at TestVersion.cmake:[0-9]+ \(cmake_pkg_config\): + Package 'tilde' version '~~1' does not meet version requirement '<~~~1' +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/cmake_pkg_config/TestVersion.cmake b/Tests/RunCMake/cmake_pkg_config/TestVersion.cmake new file mode 100644 index 000000000..18fa587c4 --- /dev/null +++ b/Tests/RunCMake/cmake_pkg_config/TestVersion.cmake @@ -0,0 +1,65 @@ +set(CMAKE_PKG_CONFIG_PC_PATH ${CMAKE_CURRENT_LIST_DIR}/PackageRoot/VersionPackages) + +# Good = Should Succeed +# Bad = Should Warn + +cmake_pkg_config(EXTRACT a =aa) # Good +cmake_pkg_config(EXTRACT a >a) # Good +cmake_pkg_config(EXTRACT a aaa) # Bad +cmake_pkg_config(EXTRACT a bb) # Bad +cmake_pkg_config(EXTRACT a 1) # Bad +cmake_pkg_config(EXTRACT a <1) # Good + +cmake_pkg_config(EXTRACT empty-key =) # Good +cmake_pkg_config(EXTRACT empty-key !=) # Bad +cmake_pkg_config(EXTRACT empty-key =0) # Bad +cmake_pkg_config(EXTRACT empty-key !=0) # Good + +cmake_pkg_config(EXTRACT empty-key EXACT) # Good + +cmake_pkg_config(EXTRACT one =11) # Good +cmake_pkg_config(EXTRACT one >1) # Good +cmake_pkg_config(EXTRACT one <1) # Bad +cmake_pkg_config(EXTRACT one >111) # Bad +cmake_pkg_config(EXTRACT one <111) # Good + +cmake_pkg_config(EXTRACT one !=22) # Good +cmake_pkg_config(EXTRACT one >22) # Bad +cmake_pkg_config(EXTRACT one <22) # Good + +cmake_pkg_config(EXTRACT one >a) # Good +cmake_pkg_config(EXTRACT one 1.2.1) # Bad + +cmake_pkg_config(EXTRACT onedot "< 1.2.1") # Good +cmake_pkg_config(EXTRACT onedot "> 1.2.1") # Bad + +cmake_pkg_config(EXTRACT onedot 1.1.1 EXACT) # Good +cmake_pkg_config(EXTRACT onedot =1.1.1 EXACT) # Good +cmake_pkg_config(EXTRACT onedot =01.01.01 EXACT) # Bad + +cmake_pkg_config(EXTRACT pseudo-empty =~) # Bad +cmake_pkg_config(EXTRACT pseudo-empty !=~) # Good +cmake_pkg_config(EXTRACT pseudo-empty =~0) # Good +cmake_pkg_config(EXTRACT pseudo-empty !=~0) # Bad + +cmake_pkg_config(EXTRACT tilde =~~1) # Good +cmake_pkg_config(EXTRACT tilde <~1) # Good +cmake_pkg_config(EXTRACT tilde >~1) # Bad +cmake_pkg_config(EXTRACT tilde <~~~1) # Bad +cmake_pkg_config(EXTRACT tilde >~~~1) # Good + +cmake_pkg_config(EXTRACT zeroone =1) # Good +cmake_pkg_config(EXTRACT zeroone =001) # Good diff --git a/Tests/RunCMake/configure_file/CMakeLists.txt b/Tests/RunCMake/configure_file/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/configure_file/CMakeLists.txt +++ b/Tests/RunCMake/configure_file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/continue/CMakeLists.txt b/Tests/RunCMake/continue/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/continue/CMakeLists.txt +++ b/Tests/RunCMake/continue/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt index f5d57e9b2..9a6217401 100644 --- a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt +++ b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt @@ -1,6 +1,6 @@ ^CMake Deprecation Warning at [^ ]*/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD/test\.cmake:[0-9]+ \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/ctest_build/CMakeLists.txt.in b/Tests/RunCMake/ctest_build/CMakeLists.txt.in index 0a59940fe..12cc7ccc2 100644 --- a/Tests/RunCMake/ctest_build/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_build/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) @CASE_CMAKELISTS_PREFIX_CODE@ project(CTestBuild@CASE_NAME@ @LANG@) include(CTest) diff --git a/Tests/RunCMake/ctest_build/test.cmake.in b/Tests/RunCMake/ctest_build/test.cmake.in index f92568fcf..98bd8acf2 100644 --- a/Tests/RunCMake/ctest_build/test.cmake.in +++ b/Tests/RunCMake/ctest_build/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in b/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in index da7ec1a60..e004d73bb 100644 --- a/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_cmake_error/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestCoverage@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_cmake_error/test.cmake.in b/Tests/RunCMake/ctest_cmake_error/test.cmake.in index 711a77fd4..a0364fa50 100644 --- a/Tests/RunCMake/ctest_cmake_error/test.cmake.in +++ b/Tests/RunCMake/ctest_cmake_error/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in index 2860b5c46..7cb1736c5 100644 --- a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestConfigure@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_configure/test.cmake.in b/Tests/RunCMake/ctest_configure/test.cmake.in index 5935809d9..fa456f3f9 100644 --- a/Tests/RunCMake/ctest_configure/test.cmake.in +++ b/Tests/RunCMake/ctest_configure/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in index da7ec1a60..e004d73bb 100644 --- a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestCoverage@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_coverage/test.cmake.in b/Tests/RunCMake/ctest_coverage/test.cmake.in index f5a12236b..c7a86b4c4 100644 --- a/Tests/RunCMake/ctest_coverage/test.cmake.in +++ b/Tests/RunCMake/ctest_coverage/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_disabled_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_disabled_test/CMakeLists.txt.in index d34fcac6e..8990fb4b5 100644 --- a/Tests/RunCMake/ctest_disabled_test/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_disabled_test/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(@CASE_NAME@ NONE) include(CTest) diff --git a/Tests/RunCMake/ctest_disabled_test/test.cmake.in b/Tests/RunCMake/ctest_disabled_test/test.cmake.in index ca23c832f..eb6a544cf 100644 --- a/Tests/RunCMake/ctest_disabled_test/test.cmake.in +++ b/Tests/RunCMake/ctest_disabled_test/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in index 408b2f31d..cddda9c63 100644 --- a/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestTest@CASE_NAME@ NONE) diff --git a/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in index 2e0cfe648..2e1702919 100644 --- a/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in +++ b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@") set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build") ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) diff --git a/Tests/RunCMake/ctest_environment/test.cmake.in b/Tests/RunCMake/ctest_environment/test.cmake.in index ca23c832f..eb6a544cf 100644 --- a/Tests/RunCMake/ctest_environment/test.cmake.in +++ b/Tests/RunCMake/ctest_environment/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_fixtures/CMakeLists.txt.in b/Tests/RunCMake/ctest_fixtures/CMakeLists.txt.in index 6b11cffac..538f70b53 100644 --- a/Tests/RunCMake/ctest_fixtures/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_fixtures/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.8.0) +cmake_minimum_required (VERSION 3.10) project(ctest_fixtures LANGUAGES NONE) include(CTest) diff --git a/Tests/RunCMake/ctest_fixtures/test.cmake.in b/Tests/RunCMake/ctest_fixtures/test.cmake.in index 7067117c1..12cf074c7 100644 --- a/Tests/RunCMake/ctest_fixtures/test.cmake.in +++ b/Tests/RunCMake/ctest_fixtures/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8.0) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in b/Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in index 30dd37e8e..8623fd58b 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_labels_for_subprojects/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) @CASE_CMAKELISTS_PREFIX_CODE@ project(CTestLabelsForSubprojects@CASE_NAME@ NONE) include(CTest) diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt index 206ab21ca..e9327a3c6 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt +++ b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLine-stderr.txt @@ -1,2 +1,2 @@ Unable to find executable:.*MyThirdPartyDependency/src(/[^/ -]+)?/thirdparty +]+)?/third_party diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt b/Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt index cc07a07f7..0d50a41c3 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt +++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyExperimentalFeature/CMakeLists.txt @@ -1,5 +1,5 @@ +cmake_minimum_required(VERSION 3.10) project(MyExperimentalFeature) -cmake_minimum_required(VERSION 3.8) include(CTest) diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt b/Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt index c2ee673ab..446b86cde 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt +++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyProductionCode/CMakeLists.txt @@ -1,5 +1,5 @@ +cmake_minimum_required(VERSION 3.10) project(MyProductionCode) -cmake_minimum_required(VERSION 3.8) include(CTest) diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt index d5b9c2f22..8e1558d8d 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt +++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/CMakeLists.txt @@ -1,5 +1,5 @@ +cmake_minimum_required(VERSION 3.10) project(MyThirdPartyDependency) -cmake_minimum_required(VERSION 3.8) include(CTest) diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt index dc6cb2a57..6c86f76fb 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt +++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/CMakeLists.txt @@ -1,10 +1,10 @@ +cmake_minimum_required(VERSION 3.10) project(MyThirdPartyDependency) -cmake_minimum_required(VERSION 3.8) include(CTest) -add_executable(thirdparty thirdparty.c) -add_test(NAME thirdparty COMMAND thirdparty) +add_executable(third_party third_party.c) +add_test(NAME third_party COMMAND third_party) -set_property(TARGET thirdparty PROPERTY LABELS NotASubproject) -set_property(TEST thirdparty PROPERTY LABELS NotASubproject) +set_property(TARGET third_party PROPERTY LABELS NotASubproject) +set_property(TEST third_party PROPERTY LABELS NotASubproject) diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/third_party.c similarity index 100% rename from Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c rename to Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/third_party.c diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in b/Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in index 8cec08d46..d3c63d5e6 100644 --- a/Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in +++ b/Tests/RunCMake/ctest_labels_for_subprojects/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) # Settings: diff --git a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in index 1de338b4f..91ecd2237 100644 --- a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestTestMemcheck@CASE_NAME@ NONE) include(CTest) diff --git a/Tests/RunCMake/ctest_memcheck/test.cmake.in b/Tests/RunCMake/ctest_memcheck/test.cmake.in index af995fc0e..71577fa85 100644 --- a/Tests/RunCMake/ctest_memcheck/test.cmake.in +++ b/Tests/RunCMake/ctest_memcheck/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) # Settings: set(CTEST_SITE "@SITE@") diff --git a/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in index cc4b8ed18..08f7b0e73 100644 --- a/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(@CASE_NAME@ C) include(CTest) diff --git a/Tests/RunCMake/ctest_skipped_test/test.cmake.in b/Tests/RunCMake/ctest_skipped_test/test.cmake.in index ca23c832f..eb6a544cf 100644 --- a/Tests/RunCMake/ctest_skipped_test/test.cmake.in +++ b/Tests/RunCMake/ctest_skipped_test/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_start/CMakeLists.txt.in b/Tests/RunCMake/ctest_start/CMakeLists.txt.in index e497a7d99..3eeeed26f 100644 --- a/Tests/RunCMake/ctest_start/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_start/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestStart@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_start/test.cmake.in b/Tests/RunCMake/ctest_start/test.cmake.in index 82acc1968..ec1265f63 100644 --- a/Tests/RunCMake/ctest_start/test.cmake.in +++ b/Tests/RunCMake/ctest_start/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_submit/CMakeLists.txt.in b/Tests/RunCMake/ctest_submit/CMakeLists.txt.in index 5ee64be81..8a418e037 100644 --- a/Tests/RunCMake/ctest_submit/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_submit/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestSubmit@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_submit/test.cmake.in b/Tests/RunCMake/ctest_submit/test.cmake.in index 6026c35c9..b83abed06 100644 --- a/Tests/RunCMake/ctest_submit/test.cmake.in +++ b/Tests/RunCMake/ctest_submit/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_test/CMakeLists.txt.in index 8eb422d10..2da092853 100644 --- a/Tests/RunCMake/ctest_test/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_test/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestTest@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_test/test.cmake.in b/Tests/RunCMake/ctest_test/test.cmake.in index d28b1e2ce..587f51df3 100644 --- a/Tests/RunCMake/ctest_test/test.cmake.in +++ b/Tests/RunCMake/ctest_test/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_update/CMakeLists.txt.in b/Tests/RunCMake/ctest_update/CMakeLists.txt.in index f816fac07..1f7965f08 100644 --- a/Tests/RunCMake/ctest_update/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_update/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestTest@CASE_NAME@ NONE) include(CTest) @CASE_CMAKELISTS_SUFFIX_CODE@ diff --git a/Tests/RunCMake/ctest_update/test.cmake.in b/Tests/RunCMake/ctest_update/test.cmake.in index faaf9b489..756a3ab55 100644 --- a/Tests/RunCMake/ctest_update/test.cmake.in +++ b/Tests/RunCMake/ctest_update/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) @CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") diff --git a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in index 21e527356..e19505607 100644 --- a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in +++ b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(CTestUpload@CASE_NAME@ NONE) include(CTest) add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version) diff --git a/Tests/RunCMake/ctest_upload/test.cmake.in b/Tests/RunCMake/ctest_upload/test.cmake.in index a546d38c4..14885057c 100644 --- a/Tests/RunCMake/ctest_upload/test.cmake.in +++ b/Tests/RunCMake/ctest_upload/test.cmake.in @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/detect_jobserver.c b/Tests/RunCMake/detect_jobserver.c index 67cc7dbfc..04fb1f254 100644 --- a/Tests/RunCMake/detect_jobserver.c +++ b/Tests/RunCMake/detect_jobserver.c @@ -162,6 +162,7 @@ int main(int argc, char** argv) jobserver = jobserver_auth(message); if (jobserver == NULL) { + fclose(fp); fprintf(stderr, "%s\n", message); return 1; } @@ -171,6 +172,7 @@ int main(int argc, char** argv) #else result = posix(jobserver, message); #endif + fclose(fp); free(jobserver); message[MAX_MESSAGE_LENGTH] = '\0'; diff --git a/Tests/RunCMake/execute_process/CMakeLists.txt b/Tests/RunCMake/execute_process/CMakeLists.txt index a640c569e..bf2ef1506 100644 --- a/Tests/RunCMake/execute_process/CMakeLists.txt +++ b/Tests/RunCMake/execute_process/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/execute_process/Encoding-common.cmake b/Tests/RunCMake/execute_process/Encoding-common.cmake new file mode 100644 index 000000000..fda1c2d18 --- /dev/null +++ b/Tests/RunCMake/execute_process/Encoding-common.cmake @@ -0,0 +1,16 @@ +if(ENCODING) + set(maybe_ENCODING ENCODING ${ENCODING}) +else() + set(maybe_ENCODING "") + cmake_policy(GET CMP0176 cmp0176) + if(cmp0176 STREQUAL "NEW") + set(ENCODING UTF-8) # execute_process's default ENCODING + else() + set(ENCODING AUTO) # execute_process's default ENCODING + endif() +endif() +execute_process( + COMMAND ${TEST_ENCODING_EXE} ${ENCODING} ${CMAKE_CURRENT_LIST_DIR}/Encoding${ENCODING}-stderr.txt + OUTPUT_VARIABLE out + ${maybe_ENCODING} + ) diff --git a/Tests/RunCMake/execute_process/Encoding-windows.cmake b/Tests/RunCMake/execute_process/Encoding-windows.cmake new file mode 100644 index 000000000..d51589c84 --- /dev/null +++ b/Tests/RunCMake/execute_process/Encoding-windows.cmake @@ -0,0 +1,11 @@ +if(CMP0176 STREQUAL "NEW") + cmake_policy(SET CMP0176 NEW) +endif() + +# Set the console code page. +execute_process(COMMAND cmd /c chcp ${CODEPAGE}) + +include(${CMAKE_CURRENT_LIST_DIR}/Encoding-common.cmake) + +# Save our internal UTF-8 representation of the output. +file(WRITE "out.txt" "${out}") diff --git a/Tests/RunCMake/execute_process/Encoding.cmake b/Tests/RunCMake/execute_process/Encoding.cmake index 3dc7c397b..61d2c2240 100644 --- a/Tests/RunCMake/execute_process/Encoding.cmake +++ b/Tests/RunCMake/execute_process/Encoding.cmake @@ -1,6 +1,17 @@ -execute_process( - COMMAND ${TEST_ENCODING_EXE} ${TEST_ENCODING} ${CMAKE_CURRENT_LIST_DIR}/EncodingUTF8-stderr.txt - OUTPUT_VARIABLE out - ENCODING ${TEST_ENCODING} - ) +if(CMAKE_HOST_WIN32 AND CODEPAGE) + cmake_policy(GET CMP0176 CMP0176) + + # Run cmake in a new Window to isolate its console code page. + execute_process(COMMAND cmd /c start /min /wait "" + ${CMAKE_COMMAND} -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} + -DENCODING=${ENCODING} + -DCODEPAGE=${CODEPAGE} + -DCMP0176=${CMP0176} + -P ${CMAKE_CURRENT_LIST_DIR}/Encoding-windows.cmake) + + # Load our internal UTF-8 representation of the output. + file(READ "out.txt" out) +else() + include(${CMAKE_CURRENT_LIST_DIR}/Encoding-common.cmake) +endif() message("${out}") diff --git a/Tests/RunCMake/execute_process/EncodingAUTO-stderr.txt b/Tests/RunCMake/execute_process/EncodingAUTO-stderr.txt new file mode 100644 index 000000000..b4cf27ab7 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingAUTO-stderr.txt @@ -0,0 +1,2 @@ +Chinese +注意 diff --git a/Tests/RunCMake/execute_process/EncodingAUTO.cmake b/Tests/RunCMake/execute_process/EncodingAUTO.cmake new file mode 100644 index 000000000..eaf9eefb8 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingAUTO.cmake @@ -0,0 +1,3 @@ +set(ENCODING AUTO) +set(CODEPAGE 54936) +include(${CMAKE_CURRENT_LIST_DIR}/Encoding.cmake) diff --git a/Tests/RunCMake/execute_process/EncodingCMP0176-NEW-stderr.txt b/Tests/RunCMake/execute_process/EncodingCMP0176-NEW-stderr.txt new file mode 100644 index 000000000..9fd0dcdb3 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingCMP0176-NEW-stderr.txt @@ -0,0 +1,2 @@ +Chinese Hindi Greek English Russian +注意 यूनिकोड είναι very здорово! diff --git a/Tests/RunCMake/execute_process/EncodingCMP0176-NEW.cmake b/Tests/RunCMake/execute_process/EncodingCMP0176-NEW.cmake new file mode 100644 index 000000000..b23ff59e3 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingCMP0176-NEW.cmake @@ -0,0 +1,3 @@ +cmake_policy(SET CMP0176 NEW) +# No explicit ENCODING option; fall back to default. +include(${CMAKE_CURRENT_LIST_DIR}/Encoding.cmake) diff --git a/Tests/RunCMake/execute_process/EncodingCMP0176-OLD-stderr.txt b/Tests/RunCMake/execute_process/EncodingCMP0176-OLD-stderr.txt new file mode 100644 index 000000000..b4cf27ab7 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingCMP0176-OLD-stderr.txt @@ -0,0 +1,2 @@ +Chinese +注意 diff --git a/Tests/RunCMake/execute_process/EncodingCMP0176-OLD.cmake b/Tests/RunCMake/execute_process/EncodingCMP0176-OLD.cmake new file mode 100644 index 000000000..805717657 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingCMP0176-OLD.cmake @@ -0,0 +1,4 @@ +cmake_policy(SET CMP0176 OLD) +# No explicit ENCODING option; fall back to default. +set(CODEPAGE 54936) +include(${CMAKE_CURRENT_LIST_DIR}/Encoding.cmake) diff --git a/Tests/RunCMake/execute_process/EncodingUTF-8-stderr.txt b/Tests/RunCMake/execute_process/EncodingUTF-8-stderr.txt index 0ac68de83..9fd0dcdb3 100644 --- a/Tests/RunCMake/execute_process/EncodingUTF-8-stderr.txt +++ b/Tests/RunCMake/execute_process/EncodingUTF-8-stderr.txt @@ -1 +1,2 @@ -यूनिकोड είναι very здорово! +Chinese Hindi Greek English Russian +注意 यूनिकोड είναι very здорово! diff --git a/Tests/RunCMake/execute_process/EncodingUTF-8.cmake b/Tests/RunCMake/execute_process/EncodingUTF-8.cmake new file mode 100644 index 000000000..e9fc06e25 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingUTF-8.cmake @@ -0,0 +1,2 @@ +set(ENCODING UTF-8) +include(${CMAKE_CURRENT_LIST_DIR}/Encoding.cmake) diff --git a/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt b/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt index 0ac68de83..9fd0dcdb3 100644 --- a/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt +++ b/Tests/RunCMake/execute_process/EncodingUTF8-stderr.txt @@ -1 +1,2 @@ -यूनिकोड είναι very здорово! +Chinese Hindi Greek English Russian +注意 यूनिकोड είναι very здорово! diff --git a/Tests/RunCMake/execute_process/EncodingUTF8.cmake b/Tests/RunCMake/execute_process/EncodingUTF8.cmake new file mode 100644 index 000000000..78441ed8d --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingUTF8.cmake @@ -0,0 +1,2 @@ +set(ENCODING UTF8) +include(${CMAKE_CURRENT_LIST_DIR}/Encoding.cmake) diff --git a/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt b/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt new file mode 100644 index 000000000..43c250f9e --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingUnknown-stderr.txt @@ -0,0 +1,5 @@ +^CMake Warning \(dev\) at EncodingUnknown\.cmake:[0-9]+ \(execute_process\): + ENCODING option given unknown value "unknown"\. Ignoring\. +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/execute_process/EncodingUnknown.cmake b/Tests/RunCMake/execute_process/EncodingUnknown.cmake new file mode 100644 index 000000000..2a8f65fb5 --- /dev/null +++ b/Tests/RunCMake/execute_process/EncodingUnknown.cmake @@ -0,0 +1 @@ +execute_process(ENCODING unknown COMMAND ${CMAKE_COMMAND} -E true) diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index 7ee75af9f..dad6322ee 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake @@ -8,9 +8,13 @@ run_cmake_command(MergeOutputFile ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/Mer run_cmake_command(MergeOutputVars ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/MergeOutputVars.cmake) run_cmake(EncodingMissing) +run_cmake(EncodingUnknown) if(TEST_ENCODING_EXE) - run_cmake_command(EncodingUTF8 ${CMAKE_COMMAND} -DTEST_ENCODING=UTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} -P ${RunCMake_SOURCE_DIR}/Encoding.cmake) - run_cmake_command(EncodingUTF-8 ${CMAKE_COMMAND} -DTEST_ENCODING=UTF-8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE} -P ${RunCMake_SOURCE_DIR}/Encoding.cmake) + run_cmake_script(EncodingCMP0176-NEW -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) + run_cmake_script(EncodingCMP0176-OLD -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) + run_cmake_script(EncodingAUTO -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) + run_cmake_script(EncodingUTF-8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) + run_cmake_script(EncodingUTF8 -DTEST_ENCODING_EXE=${TEST_ENCODING_EXE}) endif() if(EXIT_CODE_EXE) diff --git a/Tests/RunCMake/export/CMakeLists.txt b/Tests/RunCMake/export/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/export/CMakeLists.txt +++ b/Tests/RunCMake/export/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file-CHMOD/CMakeLists.txt b/Tests/RunCMake/file-CHMOD/CMakeLists.txt index 289710955..bf2ef1506 100644 --- a/Tests/RunCMake/file-CHMOD/CMakeLists.txt +++ b/Tests/RunCMake/file-CHMOD/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake index 5e0310beb..3fe209009 100644 --- a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake @@ -11,6 +11,7 @@ run_cmake(httpheader-not-set) run_cmake(netrc-bad) run_cmake(tls-cainfo-not-set) run_cmake(tls-verify-not-set) +run_cmake(TLS_VERSION-invalid) run_cmake(TLS_VERSION-missing) run_cmake(pass-not-set) run_cmake(no-save-hash) @@ -26,10 +27,12 @@ if(NOT CMake_TEST_NO_NETWORK) run_cmake(bad-hostname) endif() -run_cmake_with_options(TLS_VERSION-bad) if(CMake_TEST_TLS_VERIFY_URL_BAD) run_cmake_with_options(TLS_VERIFY-bad -Durl=${CMake_TEST_TLS_VERIFY_URL_BAD}) endif() +if(CMake_TEST_TLS_VERSION_URL_BAD) + run_cmake_with_options(TLS_VERSION-bad -Durl=${CMake_TEST_TLS_VERSION_URL_BAD}) +endif() if(CMake_TEST_TLS_VERIFY_URL) run_cmake_with_options(TLS_VERIFY-good -Durl=${CMake_TEST_TLS_VERIFY_URL}) diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt index fbff3b979..d32292fcd 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad-stdout.txt @@ -1,4 +1,4 @@ --- def-0: 0;"No error" +-- def-1: (60;"SSL peer certificate or SSH remote key was not OK|35;"SSL connect error)\. If this is due to https certificate verification failure, one may set environment variable CMAKE_TLS_VERIFY=0 to suppress it\." -- env-0: 0;"No error" -- env-1: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") -- var-0: 0;"No error" diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake index 7d50ece8d..44fcfae9d 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake @@ -7,10 +7,10 @@ function(download case) endif() endfunction() -# The default is OFF. +# The default is ON. unset(ENV{CMAKE_TLS_VERIFY}) unset(CMAKE_TLS_VERIFY) -download(def-0) +download(def-1) # The environment variable overrides the default. set(ENV{CMAKE_TLS_VERIFY} 0) diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-darwin.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-darwin.txt new file mode 100644 index 000000000..3632a61c0 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-darwin.txt @@ -0,0 +1,7 @@ +-- def-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- env-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- env-1\.1: 0;"No error" +-- var-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- var-1\.1: 0;"No error" +-- opt-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- opt-1\.1: 0;"No error" diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-windows.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-windows.txt new file mode 100644 index 000000000..3632a61c0 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout-windows.txt @@ -0,0 +1,7 @@ +-- def-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- env-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- env-1\.1: 0;"No error" +-- var-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- var-1\.1: 0;"No error" +-- opt-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- opt-1\.1: 0;"No error" diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout.txt new file mode 100644 index 000000000..ce313edf0 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stdout.txt @@ -0,0 +1,4 @@ +-- def-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- env-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- var-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") +-- opt-1\.2: (60;"SSL peer certificate or SSH remote key was not OK"|35;"SSL connect error") diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake index 51ae4a21c..51cb8f2fe 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad.cmake @@ -1,10 +1,53 @@ -# The environment variable provides a default. -set(ENV{CMAKE_TLS_VERSION} bad-env) -file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) +function(download case) + # URL with semantics like https://tls-v1-1.badssl.com:1011 is provided by caller + file(DOWNLOAD ${url} ${ARGN} STATUS status LOG log) + message(STATUS "${case}: ${status}") + if(case MATCHES "1\\.2$" AND NOT status MATCHES "^(35|60);") + message("${log}") + endif() +endfunction() + +set(CMAKE_TLS_VERIFY 1) + +if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE) + # The OS-native TLS implementations support TLS 1.1. + set(TEST_TLSv1_1 1) +else() + # OpenSSL 3.1+ does not support TLS 1.1 or older without setting + # the security level to 0, which curl (correctly) does not do. + # https://openssl-library.org/news/openssl-3.1-notes/index.html#major-changes-between-openssl-30-and-openssl-310-14-mar-2023 + set(TEST_TLSv1_1 0) +endif() + +# The default is to require 1.2. +unset(ENV{CMAKE_TLS_VERSION}) +unset(CMAKE_TLS_VERSION) +download(def-1.2) + +# The environment variable overrides the default. +set(ENV{CMAKE_TLS_VERSION} 1.2) +download(env-1.2) +if(TEST_TLSv1_1) + set(ENV{CMAKE_TLS_VERSION} 1.1) + download(env-1.1) +endif() # The cmake variable overrides the environment variable. -set(CMAKE_TLS_VERSION bad-var) -file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) +set(ENV{CMAKE_TLS_VERSION} 1.1) +set(CMAKE_TLS_VERSION 1.2) +download(var-1.2) +if(TEST_TLSv1_1) + set(ENV{CMAKE_TLS_VERSION} 1.2) + set(CMAKE_TLS_VERSION 1.1) + download(var-1.1) +endif() -# The explicit argument overrides the cmake variable. -file(DOWNLOAD "" TLS_VERSION bad-arg TLS_VERIFY 1 STATUS status LOG log) +# The explicit argument overrides the cmake variable and the environment variable. +set(ENV{CMAKE_TLS_VERSION} 1.1) +set(CMAKE_TLS_VERSION 1.1) +download(opt-1.2 TLS_VERSION 1.2) +if(TEST_TLSv1_1) + set(ENV{CMAKE_TLS_VERSION} 1.2) + set(CMAKE_TLS_VERSION 1.2) + download(opt-1.1 TLS_VERSION 1.1) +endif() diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-result.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-stderr.txt similarity index 68% rename from Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt rename to Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-stderr.txt index 421c8cf15..565facb3c 100644 --- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-bad-stderr.txt +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid-stderr.txt @@ -1,14 +1,14 @@ -^CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): +^CMake Error at TLS_VERSION-invalid\.cmake:[0-9]+ \(file\): file DOWNLOAD given unknown TLS/SSL version bad-env Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): +CMake Error at TLS_VERSION-invalid\.cmake:[0-9]+ \(file\): file DOWNLOAD given unknown TLS/SSL version bad-var Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + -CMake Error at TLS_VERSION-bad\.cmake:[0-9]+ \(file\): +CMake Error at TLS_VERSION-invalid\.cmake:[0-9]+ \(file\): file DOWNLOAD given unknown TLS/SSL version bad-arg Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid.cmake new file mode 100644 index 000000000..51ae4a21c --- /dev/null +++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERSION-invalid.cmake @@ -0,0 +1,10 @@ +# The environment variable provides a default. +set(ENV{CMAKE_TLS_VERSION} bad-env) +file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) + +# The cmake variable overrides the environment variable. +set(CMAKE_TLS_VERSION bad-var) +file(DOWNLOAD "" TLS_VERIFY 1 STATUS status LOG log) + +# The explicit argument overrides the cmake variable. +file(DOWNLOAD "" TLS_VERSION bad-arg TLS_VERIFY 1 STATUS status LOG log) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake index f7ede5158..55834070a 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake @@ -72,6 +72,7 @@ elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") run_install_test(linux-unresolved) run_install_test(linux-conflict) run_install_test(linux-notfile) + run_install_test(linux-indirect-dependencies) run_cmake(project) run_cmake(badargs1) run_cmake(badargs2) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-all-check.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-all-check.cmake index d3d1cd6e8..012ed5867 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-all-check.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-all-check.cmake @@ -1,4 +1,5 @@ set(_check + [[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/conflict/libconflict\.so]] [[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/libtest_rpath\.so]] [[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/libtest_runpath\.so]] [[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/rpath/librpath\.so]] @@ -19,7 +20,7 @@ check_contents(deps/udeps1.txt "^${_check}$") check_contents(deps/udeps2.txt "^${_check}$") check_contents(deps/udeps3.txt "^${_check}$") set(_check - "^libconflict\\.so:[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/conflict/libconflict\\.so;[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-build/root-all/lib/conflict2/libconflict\\.so\n$" + "^$" ) check_contents(deps/cdeps1.txt "${_check}") check_contents(deps/cdeps2.txt "${_check}") diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies-all-stdout.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies-all-stdout.txt new file mode 100644 index 000000000..9f5e07da0 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies-all-stdout.txt @@ -0,0 +1 @@ +Resolved dependencies: / diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies.cmake new file mode 100644 index 000000000..1d0a913fc --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-indirect-dependencies.cmake @@ -0,0 +1,83 @@ +enable_language(C) +cmake_policy(SET CMP0095 NEW) + +file(WRITE "${CMAKE_BINARY_DIR}/A.c" "void libA(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/C.c" "void libC(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/BUseAC.c" [[ +extern void libA(void); +extern void libC(void); +void libB(void) +{ + libA(); + libC(); +} +]]) +file(WRITE "${CMAKE_BINARY_DIR}/mainABC.c" [[ +extern void libA(void); +extern void libB(void); +extern void libC(void); + +int main(void) +{ + libA(); + libB(); + libC(); + return 0; +} + +]]) + +set(lib_dirExe "${CMAKE_BINARY_DIR}/Exe") +set(lib_dirA "${CMAKE_BINARY_DIR}/libA") +set(lib_dirB "${CMAKE_BINARY_DIR}/libB") +set(lib_dirC "${CMAKE_BINARY_DIR}/libC") +file(MAKE_DIRECTORY ${lib_dirExe}) +file(MAKE_DIRECTORY ${lib_dirA}) +file(MAKE_DIRECTORY ${lib_dirB}) +file(MAKE_DIRECTORY ${lib_dirC}) + +add_library(A SHARED "${CMAKE_BINARY_DIR}/A.c") +set_property(TARGET A PROPERTY LIBRARY_OUTPUT_DIRECTORY ${lib_dirA}) + +add_library(C SHARED "${CMAKE_BINARY_DIR}/C.c") +set_property(TARGET C PROPERTY LIBRARY_OUTPUT_DIRECTORY ${lib_dirC}) + +# We doesn't need to set A as a dependency of B, because we don't need `RUNPATH` value set for B +add_library(B SHARED "${CMAKE_BINARY_DIR}/BUseAC.c") +target_link_libraries(B PRIVATE A C) +set_property(TARGET B PROPERTY LIBRARY_OUTPUT_DIRECTORY ${lib_dirB}) + +# We MUST have empty `RUNPATH` in A & B +set_target_properties(A B C PROPERTIES + BUILD_WITH_INSTALL_RPATH 1 +) + +# The executable is really workable without `RUNPATH` in B +add_executable(exe "${CMAKE_BINARY_DIR}/mainABC.c") +target_link_libraries(exe A B C) +set_property(TARGET exe PROPERTY RUNTIME_OUTPUT_DIRECTORY ${lib_dirExe}) + +# We MUST have `RUNPATH` in exe, not `RPATH` +# Test will pass if we have `RPATH`, because of the inheritance +target_link_options(exe PRIVATE -Wl,--enable-new-dtags) + +install(CODE [[ + # Work with non-installed binary, because of the RUNPATH values + set(exeFile "$") + + # Check executable is can be successfully finished + execute_process( + COMMAND "${exeFile}" + COMMAND_ERROR_IS_FATAL ANY + ) + + # Check dependencies resolved + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR RESOLVED + PRE_INCLUDE_REGEXES "^lib[ABC]\\.so$" + PRE_EXCLUDE_REGEXES ".*" + EXECUTABLES + "${exeFile}" + ) + message(STATUS "Resolved dependencies: ${RESOLVED}") +]]) diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt new file mode 100644 index 000000000..bf2ef1506 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.10) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt new file mode 100644 index 000000000..63d46fd0a --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-stdout.txt @@ -0,0 +1,3 @@ +^-- Result=Failed to create directory: [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL-build/file/directory0 Error: [^ +]*$ diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake new file mode 100644 index 000000000..0cfccbfae --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-FAIL.cmake @@ -0,0 +1,9 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") + +file(MAKE_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/file/directory0" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory1" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory2" + RESULT resultVal +) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt new file mode 100644 index 000000000..09df4f9e5 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS-stdout.txt @@ -0,0 +1 @@ +^-- Result=0 diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake new file mode 100644 index 000000000..e0781ce17 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-many-dirs-SUCCESS.cmake @@ -0,0 +1,7 @@ +file(MAKE_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/file/directory0" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory1" + "${CMAKE_CURRENT_BINARY_DIR}/file/directory2" + RESULT resultVal +) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt new file mode 100644 index 000000000..5d16178ac --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-stdout.txt @@ -0,0 +1,3 @@ +^-- Result=Failed to create directory: [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL-build/file/directory Error: [^ +]*$ diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake new file mode 100644 index 000000000..0287d6791 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-FAIL.cmake @@ -0,0 +1,3 @@ +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/file" "") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory" RESULT resultVal) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt new file mode 100644 index 000000000..09df4f9e5 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS-stdout.txt @@ -0,0 +1 @@ +^-- Result=0 diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake new file mode 100644 index 000000000..3005b830e --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-Result-one-dir-SUCCESS.cmake @@ -0,0 +1,2 @@ +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/file/directory" RESULT resultVal) +message(STATUS "Result=${resultVal}") diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt new file mode 100644 index 000000000..2bc275c54 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-stderr.txt @@ -0,0 +1,9 @@ +^CMake Error at [^ +]*/MAKE_DIRECTORY-one-dir-FAIL.cmake:[0-9]+ \(file\): + file failed to create directory: + + [^ +]*/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL-build/file/directory + + because: [^ +]+$ diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake similarity index 100% rename from Tests/RunCMake/file/MAKE_DIRECTORY-fail.cmake rename to Tests/RunCMake/file-MAKE_DIRECTORY/MAKE_DIRECTORY-one-dir-FAIL.cmake diff --git a/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake b/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake new file mode 100644 index 000000000..1eacd9065 --- /dev/null +++ b/Tests/RunCMake/file-MAKE_DIRECTORY/RunCMakeTest.cmake @@ -0,0 +1,7 @@ +include(RunCMake) + +run_cmake_script(MAKE_DIRECTORY-one-dir-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-one-dir-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-one-dir-SUCCESS) +run_cmake_script(MAKE_DIRECTORY-Result-many-dirs-FAIL) +run_cmake_script(MAKE_DIRECTORY-Result-many-dirs-SUCCESS) diff --git a/Tests/RunCMake/file-RPATH/Common.cmake b/Tests/RunCMake/file-RPATH/Common.cmake index 7034aadb3..d6d3eeb26 100644 --- a/Tests/RunCMake/file-RPATH/Common.cmake +++ b/Tests/RunCMake/file-RPATH/Common.cmake @@ -1,12 +1,16 @@ # Prepare binaries on which to operate. set(in "${CMAKE_CURRENT_LIST_DIR}/${format}") set(out "${CMAKE_CURRENT_BINARY_DIR}") -foreach(f ${names}) +foreach(f ${dynamic}) file(COPY ${in}/${f} DESTINATION ${out} NO_SOURCE_PERMISSIONS) - list(APPEND files "${out}/${f}") + list(APPEND dynamic_files "${out}/${f}") +endforeach() +foreach(f ${static}) + file(COPY ${in}/${f} DESTINATION ${out} NO_SOURCE_PERMISSIONS) + list(APPEND static_files "${out}/${f}") endforeach() -foreach(f ${files}) +foreach(f ${dynamic_files}) # Check for the initial RPATH. file(RPATH_CHECK FILE "${f}" RPATH "/sample/rpath") if(NOT EXISTS "${f}") @@ -65,11 +69,11 @@ endforeach() # TODO Implement RPATH_SET in XCOFF. if(format STREQUAL "ELF") - foreach(f ${names}) + foreach(f ${dynamic}) file(COPY ${in}/${f} DESTINATION ${out} NO_SOURCE_PERMISSIONS) endforeach() - foreach(f ${files}) + foreach(f ${dynamic_files}) # Set the RPATH. file(RPATH_SET FILE "${f}" NEW_RPATH "/new/rpath") @@ -99,3 +103,8 @@ if(format STREQUAL "ELF") endif() endforeach() endif() + +# Verify that modifying rpaths on a static library is a no-op +foreach(f ${static_files}) + file(RPATH_CHANGE FILE "${f}" OLD_RPATH "/rpath/foo" NEW_RPATH "/rpath/bar") +endforeach() diff --git a/Tests/RunCMake/file-RPATH/ELF.cmake b/Tests/RunCMake/file-RPATH/ELF.cmake index 558b2e264..b8919eff1 100644 --- a/Tests/RunCMake/file-RPATH/ELF.cmake +++ b/Tests/RunCMake/file-RPATH/ELF.cmake @@ -1,9 +1,12 @@ -set(names +set(dynamic elf32lsb.bin elf32msb.bin elf64lsb.bin elf64msb.bin ) +set(static + elf64lsb-static.bin + ) set(format ELF) include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) diff --git a/Tests/RunCMake/file-RPATH/ELF/elf64lsb-static.bin b/Tests/RunCMake/file-RPATH/ELF/elf64lsb-static.bin new file mode 100755 index 0000000000000000000000000000000000000000..b48c4f3b49f12c58da8ec2c76b9c689e81ec383f GIT binary patch literal 4320 zcmb<-^>JfjWMqH=CI&kOFi*e%ECeAL7!(9xDi|0X7%Ui=fFv`J1gT{~NC8<6P#%Qd z0Oi4G2B2@t)^Fuh1L2Xx#(5J?Qg1dm-Hi2(or01)^9 literal 0 HcmV?d00001 diff --git a/Tests/RunCMake/file-RPATH/XCOFF.cmake b/Tests/RunCMake/file-RPATH/XCOFF.cmake index b5709208a..f7464c2da 100644 --- a/Tests/RunCMake/file-RPATH/XCOFF.cmake +++ b/Tests/RunCMake/file-RPATH/XCOFF.cmake @@ -1,4 +1,4 @@ -set(names +set(dynamic xcoff32.bin xcoff64.bin ) diff --git a/Tests/RunCMake/file/CMakeLists.txt b/Tests/RunCMake/file/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/file/CMakeLists.txt +++ b/Tests/RunCMake/file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt b/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt deleted file mode 100644 index 95fccdf9c..000000000 --- a/Tests/RunCMake/file/MAKE_DIRECTORY-fail-stderr.txt +++ /dev/null @@ -1,9 +0,0 @@ -^CMake Error at [^ -]*/MAKE_DIRECTORY-fail.cmake:[0-9]+ \(file\): - file failed to create directory: - - [^ -]*/Tests/RunCMake/file/MAKE_DIRECTORY-fail-build/file/directory - - because: [^ -]+$ diff --git a/Tests/RunCMake/file/REAL_PATH-existing.cmake b/Tests/RunCMake/file/REAL_PATH-existing.cmake new file mode 100644 index 000000000..5f6773956 --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-existing.cmake @@ -0,0 +1,2 @@ +file(REAL_PATH "dir" real_path_dir) +file(REAL_PATH "dir/empty.txt" real_path_file) diff --git a/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt b/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt new file mode 100644 index 000000000..4ac75210c --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-non-existing-stderr.txt @@ -0,0 +1,9 @@ +^CMake Warning \(dev\) at REAL_PATH-non-existing.cmake:[0-9]+ \(file\): + Given path: + + dir/bad-magic-file\.txt + + does not refer to an existing path on disk. +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/file/REAL_PATH-non-existing.cmake b/Tests/RunCMake/file/REAL_PATH-non-existing.cmake new file mode 100644 index 000000000..c6357a8af --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-non-existing.cmake @@ -0,0 +1,2 @@ + +file(REAL_PATH "dir/bad-magic-file.txt" real_path) diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index be8ee7c67..38ec2acd3 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -62,8 +62,6 @@ run_cmake_script(COPY_FILE-arg-unknown) run_cmake_script(COPY_FILE-input-missing) run_cmake_script(COPY_FILE-output-missing) -run_cmake_script(MAKE_DIRECTORY-fail) - run_cmake_script(RENAME-file-replace) run_cmake_script(RENAME-file-to-file) run_cmake_script(RENAME-file-to-dir-capture) @@ -101,6 +99,8 @@ if(NOT WIN32 endif() endif() +run_cmake(REAL_PATH-non-existing) +run_cmake(REAL_PATH-existing) run_cmake(REAL_PATH-unexpected-arg) run_cmake(REAL_PATH-no-base-dir) run_cmake(REAL_PATH) diff --git a/Tests/RunCMake/find_dependency/CMakeLists.txt b/Tests/RunCMake/find_dependency/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_dependency/CMakeLists.txt +++ b/Tests/RunCMake/find_dependency/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_file/CMakeLists.txt b/Tests/RunCMake/find_file/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_file/CMakeLists.txt +++ b/Tests/RunCMake/find_file/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_library/CMakeLists.txt b/Tests/RunCMake/find_library/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_library/CMakeLists.txt +++ b/Tests/RunCMake/find_library/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_package/CMakeLists.txt b/Tests/RunCMake/find_package/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_package/CMakeLists.txt +++ b/Tests/RunCMake/find_package/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_package/DebugRoot-stderr.txt b/Tests/RunCMake/find_package/DebugRoot-stderr.txt new file mode 100644 index 000000000..00a70c11c --- /dev/null +++ b/Tests/RunCMake/find_package/DebugRoot-stderr.txt @@ -0,0 +1,52 @@ +^CMake Debug Log at DebugRoot\.cmake:[0-9]+ \(find_package\): + The internally managed CMAKE_FIND_PACKAGE_REDIRECTS_DIR\. + + [^ +]*/Tests/RunCMake/find_package/DebugRoot-build/CMakeFiles/pkgRedirects + + Paths specified by the find_package HINTS option\. + + none + + Paths specified by the find_package PATHS option\. + + /DebugRoot + + Prepending the following roots to each prefix: + + CMAKE_FIND_ROOT_PATH + + [^ +]*/Tests/RunCMake/find_package/DebugRoot/NoExist + [^ +]*/Tests/RunCMake/find_package/DebugRoot/Exist + + CMAKE_SYSROOT_COMPILE + + none + + CMAKE_SYSROOT_LINK + + none + + CMAKE_SYSROOT + + none + + find_package considered the following locations for DebugRoot's Config + module: + + [^ +]*/Tests/RunCMake/find_package/DebugRoot/NoExist/DebugRoot/DebugRootConfig\.cmake + [^ +]*/Tests/RunCMake/find_package/DebugRoot/NoExist/DebugRoot/debugroot-config\.cmake + [^ +]*/Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig\.cmake + + The file was found at + + [^ +]*/Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig.cmake + +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/find_package/DebugRoot.cmake b/Tests/RunCMake/find_package/DebugRoot.cmake new file mode 100644 index 000000000..2fb701709 --- /dev/null +++ b/Tests/RunCMake/find_package/DebugRoot.cmake @@ -0,0 +1,15 @@ +set(CMAKE_FIND_ROOT_PATH ${CMAKE_CURRENT_LIST_DIR}/DebugRoot/NoExist ${CMAKE_CURRENT_LIST_DIR}/DebugRoot/Exist) +set(CMAKE_FIND_DEBUG_MODE ON) +find_package(DebugRoot + NO_DEFAULT_PATH + NO_PACKAGE_ROOT_PATH + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_PACKAGE_REGISTRY + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_INSTALL_PREFIX + NO_CMAKE_SYSTEM_PACKAGE_REGISTRY + PATHS /DebugRoot + ) +set(CMAKE_FIND_DEBUG_MODE OFF) diff --git a/Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig.cmake b/Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig.cmake new file mode 100644 index 000000000..683b7a62a --- /dev/null +++ b/Tests/RunCMake/find_package/DebugRoot/Exist/DebugRoot/DebugRootConfig.cmake @@ -0,0 +1 @@ +set(DebugRoot_FOUND TRUE) diff --git a/Tests/RunCMake/find_package/DebugRoot/NoExist/DebugRoot/.empty b/Tests/RunCMake/find_package/DebugRoot/NoExist/DebugRoot/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index 8cda4c121..1995ac40b 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake @@ -74,6 +74,7 @@ run_cmake(IgnorePrefixPath) run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) run_cmake(REGISTRY_VIEW-propagated) +run_cmake(DebugRoot) if(CMAKE_HOST_WIN32 AND MINGW) run_cmake(MSYSTEM_PREFIX) diff --git a/Tests/RunCMake/find_path/CMakeLists.txt b/Tests/RunCMake/find_path/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_path/CMakeLists.txt +++ b/Tests/RunCMake/find_path/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/find_program/CMakeLists.txt b/Tests/RunCMake/find_program/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/find_program/CMakeLists.txt +++ b/Tests/RunCMake/find_program/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/get_filename_component/CMakeLists.txt b/Tests/RunCMake/get_filename_component/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/get_filename_component/CMakeLists.txt +++ b/Tests/RunCMake/get_filename_component/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/get_property/CMakeLists.txt b/Tests/RunCMake/get_property/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/get_property/CMakeLists.txt +++ b/Tests/RunCMake/get_property/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/if/CMakeLists.txt b/Tests/RunCMake/if/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/if/CMakeLists.txt +++ b/Tests/RunCMake/if/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include/CMP0024-WARN-stderr.txt b/Tests/RunCMake/include/CMP0024-WARN-stderr.txt index 8472f41e2..6cf4a7b69 100644 --- a/Tests/RunCMake/include/CMP0024-WARN-stderr.txt +++ b/Tests/RunCMake/include/CMP0024-WARN-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0024-WARN\.cmake:[0-9]+ \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/include/CMakeLists.txt b/Tests/RunCMake/include/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/include/CMakeLists.txt +++ b/Tests/RunCMake/include/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include_directories/CMakeLists.txt b/Tests/RunCMake/include_directories/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/include_directories/CMakeLists.txt +++ b/Tests/RunCMake/include_directories/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include_external_msproject/CMakeLists.txt b/Tests/RunCMake/include_external_msproject/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/include_external_msproject/CMakeLists.txt +++ b/Tests/RunCMake/include_external_msproject/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/include_guard/CMakeLists.txt b/Tests/RunCMake/include_guard/CMakeLists.txt index d3137f614..bf2ef1506 100644 --- a/Tests/RunCMake/include_guard/CMakeLists.txt +++ b/Tests/RunCMake/include_guard/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt index 6b4c4b0eb..8eeef6548 100644 --- a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt +++ b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMP0062-OLD\.cmake:[0-9]+ \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake b/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake new file mode 100644 index 000000000..3524b6dc6 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake @@ -0,0 +1,36 @@ +set(installBase ${RunCMake_TEST_BINARY_DIR}/root-all) + +foreach(i RANGE 1 5) + set(subdir shouldNotRemain${i}) + if(IS_DIRECTORY ${installBase}/${subdir}) + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nUnexpectedly created install path that should have disappeared with " + "normalization:\n" + " ${installBase}/${subdir}" + ) + endif() +endforeach() + +file(GLOB perConfigFiles ${installBase}/lib/cmake/pkg/pkg-config-*.cmake) +foreach(file IN LISTS perConfigFiles ITEMS ${installBase}/lib/cmake/pkg/pkg-config.cmake) + file(STRINGS ${file} matches REGEX shouldNotRemain) + if(NOT matches STREQUAL "") + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nNon-normalized path found in ${file}:" + ) + foreach(match IN LISTS matches) + string(APPEND RunCMake_TEST_FAILURE_MESSAGE "\n ${match}") + endforeach() + endif() +endforeach() + +if(NOT EXISTS "${installBase}/dirs/dir/empty.txt") + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nNon-normalized DIRECTORY destination not handled correctly. " + "Expected to find the following file, but it was missing:\n" + " ${installBase}/dirs/dir/empty.txt" + ) +endif() diff --git a/Tests/RunCMake/install/CMP0177-NEW-verify.cmake b/Tests/RunCMake/install/CMP0177-NEW-verify.cmake new file mode 100644 index 000000000..7b8b2dcfa --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-NEW.cmake b/Tests/RunCMake/install/CMP0177-NEW.cmake new file mode 100644 index 000000000..698d9ceca --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0177 NEW) +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt b/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt b/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt new file mode 100644 index 000000000..366660bac --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt @@ -0,0 +1,13 @@ +CMake Error at [^ +]*CMP0177-OLD-build/root-all/lib/cmake/pkg/pkg-config\.cmake:[0-9]+ \(message\): + The imported target "foo1" references the file ++ ".*/shouldNotRemain1/\.\./lib/(libfoo1\.a|foo1\.l(ib)?)" ++ but this file does not exist\. Possible reasons include: ++ \* The file was deleted, renamed, or moved to another location\. ++ \* An install or uninstall procedure did not complete successfully\. ++ \* The installation package was faulty and contained ++ ".*/Tests/RunCMake/install/CMP0177-OLD-build/root-all/lib/cmake/pkg/pkg-config\.cmake" ++ but not all the files it references\. ++Call Stack \(most recent call first\): + CMP0177-OLD-verify\.cmake:2 \(find_package\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify.cmake b/Tests/RunCMake/install/CMP0177-OLD-verify.cmake new file mode 100644 index 000000000..7b8b2dcfa --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-OLD.cmake b/Tests/RunCMake/install/CMP0177-OLD.cmake new file mode 100644 index 000000000..638998344 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0177 OLD) +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177-WARN-stderr.txt b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt new file mode 100644 index 000000000..a7d8b2e54 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt @@ -0,0 +1,35 @@ +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt b/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt b/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt new file mode 100644 index 000000000..0eca81566 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt @@ -0,0 +1,13 @@ +CMake Error at [^ +]*CMP0177-WARN-build/root-all/lib/cmake/pkg/pkg-config\.cmake:[0-9]+ \(message\): + The imported target "foo1" references the file ++ ".*/shouldNotRemain1/\.\./lib/(libfoo1\.a|foo1\.l(ib)?)" ++ but this file does not exist\. Possible reasons include: ++ \* The file was deleted, renamed, or moved to another location\. ++ \* An install or uninstall procedure did not complete successfully\. ++ \* The installation package was faulty and contained ++ ".*/Tests/RunCMake/install/CMP0177-WARN-build/root-all/lib/cmake/pkg/pkg-config\.cmake" ++ but not all the files it references\. ++Call Stack \(most recent call first\): + CMP0177-WARN-verify\.cmake:2 \(find_package\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify.cmake b/Tests/RunCMake/install/CMP0177-WARN-verify.cmake new file mode 100644 index 000000000..7b8b2dcfa --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-WARN.cmake b/Tests/RunCMake/install/CMP0177-WARN.cmake new file mode 100644 index 000000000..5bad118b9 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177.cmake b/Tests/RunCMake/install/CMP0177.cmake new file mode 100644 index 000000000..176367604 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177.cmake @@ -0,0 +1,29 @@ +enable_language(C) + +add_library(foo1 STATIC obj1.c) +add_library(foo2 STATIC obj2.c) + +set_target_properties(foo2 PROPERTIES HIDDEN_DOUBLE_DOT "..") + +# All the shouldNotRemainX path components below should be normalized out when +# CMP0177 is set to NEW, and retained for OLD and WARN. + +install(TARGETS foo1 + EXPORT pkg + ARCHIVE DESTINATION shouldNotRemain1/../lib +) +install(TARGETS foo2 + EXPORT pkg + ARCHIVE DESTINATION shouldNotRemain2/$/lib +) +install(EXPORT pkg + DESTINATION shouldNotRemain3/deeper/../.././lib/cmake/pkg + FILE pkg-config.cmake +) +install(FILES obj1.c + DESTINATION shouldNotRemain4/anotherSubdir/../../files +) +install(DIRECTORY dir + # Trailing slash here is significant + DESTINATION shouldNotRemain5/../dirs/more/../ +) diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/install/CMakeLists.txt +++ b/Tests/RunCMake/install/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake b/Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake index 03fa3c832..17fa19140 100644 --- a/Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake +++ b/Tests/RunCMake/install/DIRECTORY-TYPE-all-check.cmake @@ -14,6 +14,9 @@ set(_check_files [[lib]] [[lib/dir]] [[lib/dir/empty\.txt]] + [[libexec]] + [[libexec/dir]] + [[libexec/dir/empty\.txt]] [[sbin]] [[sbin/dir]] [[sbin/dir/empty\.txt]] diff --git a/Tests/RunCMake/install/DIRECTORY-TYPE.cmake b/Tests/RunCMake/install/DIRECTORY-TYPE.cmake index 53e95f8ee..cb0947471 100644 --- a/Tests/RunCMake/install/DIRECTORY-TYPE.cmake +++ b/Tests/RunCMake/install/DIRECTORY-TYPE.cmake @@ -11,3 +11,4 @@ install(DIRECTORY dir TYPE INFO) install(DIRECTORY dir TYPE LOCALE) install(DIRECTORY dir TYPE MAN) install(DIRECTORY dir TYPE DOC) +install(DIRECTORY dir TYPE LIBEXEC) diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt deleted file mode 100644 index 592f79c68..000000000 --- a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt +++ /dev/null @@ -1,5 +0,0 @@ -.+ -set_target_properties\(pkg1::foo PROPERTIES -.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg1/inc" -\) -.+ diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt deleted file mode 100644 index ebfc43e34..000000000 --- a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt +++ /dev/null @@ -1,5 +0,0 @@ -.+ -set_target_properties\(pkg2::foo PROPERTIES -.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg2/inc" -\) -.+ diff --git a/Tests/RunCMake/install/FILES-TYPE-all-check.cmake b/Tests/RunCMake/install/FILES-TYPE-all-check.cmake index c4ec66149..a0324cfb6 100644 --- a/Tests/RunCMake/install/FILES-TYPE-all-check.cmake +++ b/Tests/RunCMake/install/FILES-TYPE-all-check.cmake @@ -9,6 +9,8 @@ set(_check_files [[include/main\.c]] [[lib]] [[lib/main\.c]] + [[libexec]] + [[libexec/main\.c]] [[sbin]] [[sbin/main\.c]] [[share]] diff --git a/Tests/RunCMake/install/FILES-TYPE.cmake b/Tests/RunCMake/install/FILES-TYPE.cmake index 2e2bfc7fa..ed75adfbd 100644 --- a/Tests/RunCMake/install/FILES-TYPE.cmake +++ b/Tests/RunCMake/install/FILES-TYPE.cmake @@ -11,3 +11,4 @@ install(FILES main.c TYPE INFO) install(FILES main.c TYPE LOCALE) install(FILES main.c TYPE MAN) install(FILES main.c TYPE DOC) +install(FILES main.c TYPE LIBEXEC) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 295922c0d..39c3373dd 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -7,7 +7,9 @@ function(run_install_test case) set(RunCMake_TEST_NO_CLEAN 1) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + set(RunCMake_TEST_RAW_ARGS -DCMAKE_BUILD_TYPE:STRING=Debug) run_cmake(${case}) + unset(RunCMake_TEST_RAW_ARGS) set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug) unset(RunCMake_TEST_OUTPUT_MERGE) @@ -92,6 +94,12 @@ run_cmake(CMP0062-WARN) run_cmake(CMP0087-OLD) run_cmake(CMP0087-NEW) run_cmake(CMP0087-WARN) +foreach(policy IN ITEMS NEW OLD WARN) + run_install_test(CMP0177-${policy}) + run_cmake_with_options(CMP0177-${policy}-verify + -DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/CMP0177-${policy}-build/root-all + ) +endforeach() run_cmake(TARGETS-ImportedGlobal) run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all) run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc) diff --git a/Tests/RunCMake/list/CMakeLists.txt b/Tests/RunCMake/list/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/list/CMakeLists.txt +++ b/Tests/RunCMake/list/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt index 5dd76f778..745329040 100644 --- a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt +++ b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at GET-CMP0007-WARN\.cmake:1 \(cmake_policy\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/math/CMakeLists.txt b/Tests/RunCMake/math/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/math/CMakeLists.txt +++ b/Tests/RunCMake/math/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/message/CMakeLists.txt b/Tests/RunCMake/message/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/message/CMakeLists.txt +++ b/Tests/RunCMake/message/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/no_install_prefix/CMakeLists.txt b/Tests/RunCMake/no_install_prefix/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/no_install_prefix/CMakeLists.txt +++ b/Tests/RunCMake/no_install_prefix/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/project/CMP0048-NEW-stderr.txt b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt index ad75f4386..dde1afca4 100644 --- a/Tests/RunCMake/project/CMP0048-NEW-stderr.txt +++ b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt index 376249b3f..1fb207fac 100644 --- a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt +++ b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt @@ -1,5 +1,5 @@ ^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/project/CMP0180-NEW-stdout.txt b/Tests/RunCMake/project/CMP0180-NEW-stdout.txt new file mode 100644 index 000000000..34e5d4919 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-NEW-stdout.txt @@ -0,0 +1,16 @@ +(-- )?From subdir1: + CMP0180-NEW_SOURCE_DIR = [^ +]+/project/subdir1 + CMP0180-NEW_BINARY_DIR = [^ +]+/project/CMP0180-NEW-build/subdir1 + CMP0180-NEW_IS_TOP_LEVEL = OFF +(-- )?From subdir2: + CMP0180-NEW_SOURCE_DIR = [^ +]+/project + CMP0180-NEW_BINARY_DIR = [^ +]+/project/CMP0180-NEW-build + CMP0180-NEW_IS_TOP_LEVEL = ON +(-- )? sub2proj_SOURCE_DIR = [^ +]+/project/subdir2 + sub2proj_BINARY_DIR = [^ +]+/project/CMP0180-NEW-build/subdir2 diff --git a/Tests/RunCMake/project/CMP0180-NEW.cmake b/Tests/RunCMake/project/CMP0180-NEW.cmake new file mode 100644 index 000000000..71f8b845a --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-NEW.cmake @@ -0,0 +1,2 @@ +# CMP0180 is handled in CMakeLists.txt +include(CMP0180.cmake) diff --git a/Tests/RunCMake/project/CMP0180-OLD-stdout.txt b/Tests/RunCMake/project/CMP0180-OLD-stdout.txt new file mode 100644 index 000000000..4ef55a429 --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-OLD-stdout.txt @@ -0,0 +1,16 @@ +(-- )?From subdir1: + CMP0180-OLD_SOURCE_DIR = [^ +]+/project/subdir1 + CMP0180-OLD_BINARY_DIR = [^ +]+/project/CMP0180-OLD-build/subdir1 + CMP0180-OLD_IS_TOP_LEVEL = OFF +(-- )?From subdir2: + CMP0180-OLD_SOURCE_DIR = [^ +]+/project/subdir1 + CMP0180-OLD_BINARY_DIR = [^ +]+/project/CMP0180-OLD-build/subdir1 + CMP0180-OLD_IS_TOP_LEVEL = OFF +(-- )? sub2proj_SOURCE_DIR = [^ +]+/project/subdir2 + sub2proj_BINARY_DIR = [^ +]+/project/CMP0180-OLD-build/subdir2 diff --git a/Tests/RunCMake/project/CMP0180-OLD.cmake b/Tests/RunCMake/project/CMP0180-OLD.cmake new file mode 100644 index 000000000..71f8b845a --- /dev/null +++ b/Tests/RunCMake/project/CMP0180-OLD.cmake @@ -0,0 +1,2 @@ +# CMP0180 is handled in CMakeLists.txt +include(CMP0180.cmake) diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir.cmake b/Tests/RunCMake/project/CMP0180.cmake similarity index 100% rename from Tests/RunCMake/project/SameProjectVarsSubdir.cmake rename to Tests/RunCMake/project/CMP0180.cmake diff --git a/Tests/RunCMake/project/CMakeLists.txt b/Tests/RunCMake/project/CMakeLists.txt index a0ee6d7a0..28146c472 100644 --- a/Tests/RunCMake/project/CMakeLists.txt +++ b/Tests/RunCMake/project/CMakeLists.txt @@ -1,9 +1,15 @@ if("x${RunCMake_TEST}" STREQUAL "xNoMinimumRequired") # No cmake_minimum_required(VERSION) elseif(RunCMake_TEST MATCHES "^CMP0048") - cmake_minimum_required(VERSION 2.8.12) + cmake_minimum_required(VERSION 2.8.12) # old enough to not set CMP0048 else() - cmake_minimum_required(VERSION 3.5) + cmake_minimum_required(VERSION 3.10) + # CMP0180 needs to be set before the project() call for these tests + if("x${RunCMake_TEST}" STREQUAL "xCMP0180-NEW") + cmake_policy(SET CMP0180 NEW) + elseif("x${RunCMake_TEST}" STREQUAL "xCMP0180-OLD") + cmake_policy(SET CMP0180 OLD) + endif() endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index ed4b8ba5d..554cdff67 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -63,9 +63,13 @@ run_cmake(CMP0096-OLD) run_cmake(CMP0096-NEW) # We deliberately run these twice to verify behavior of the second CMake run -run_cmake(SameProjectVarsSubdir) +run_cmake(CMP0180-OLD) set(RunCMake_TEST_NO_CLEAN 1) -run_cmake(SameProjectVarsSubdir) +run_cmake(CMP0180-OLD) +set(RunCMake_TEST_NO_CLEAN 0) +run_cmake(CMP0180-NEW) +set(RunCMake_TEST_NO_CLEAN 1) +run_cmake(CMP0180-NEW) set(RunCMake_TEST_NO_CLEAN 0) run_cmake(NoMinimumRequired) diff --git a/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt b/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt deleted file mode 100644 index 73dadfda3..000000000 --- a/Tests/RunCMake/project/SameProjectVarsSubdir-stdout.txt +++ /dev/null @@ -1,9 +0,0 @@ -(-- )? SameProjectVarsSubdir_SOURCE_DIR = [^ -]+/subdir1 - SameProjectVarsSubdir_BINARY_DIR = [^ -]+/subdir1 - SameProjectVarsSubdir_IS_TOP_LEVEL = OFF -(-- )? sub2proj_SOURCE_DIR = [^ -]+/subdir2 - sub2proj_BINARY_DIR = [^ -]+/subdir2 diff --git a/Tests/RunCMake/project/subdir1/CMakeLists.txt b/Tests/RunCMake/project/subdir1/CMakeLists.txt index d6be2299a..d2d37297f 100644 --- a/Tests/RunCMake/project/subdir1/CMakeLists.txt +++ b/Tests/RunCMake/project/subdir1/CMakeLists.txt @@ -1 +1,8 @@ project(${RunCMake_TEST} LANGUAGES NONE) + +message(STATUS + "From subdir1:\n" + " ${RunCMake_TEST}_SOURCE_DIR = ${${RunCMake_TEST}_SOURCE_DIR}\n" + " ${RunCMake_TEST}_BINARY_DIR = ${${RunCMake_TEST}_BINARY_DIR}\n" + " ${RunCMake_TEST}_IS_TOP_LEVEL = ${${RunCMake_TEST}_IS_TOP_LEVEL}" +) diff --git a/Tests/RunCMake/project/subdir2/CMakeLists.txt b/Tests/RunCMake/project/subdir2/CMakeLists.txt index c28e0c97b..cab547f10 100644 --- a/Tests/RunCMake/project/subdir2/CMakeLists.txt +++ b/Tests/RunCMake/project/subdir2/CMakeLists.txt @@ -1,4 +1,5 @@ message(STATUS + "From subdir2:\n" " ${RunCMake_TEST}_SOURCE_DIR = ${${RunCMake_TEST}_SOURCE_DIR}\n" " ${RunCMake_TEST}_BINARY_DIR = ${${RunCMake_TEST}_BINARY_DIR}\n" " ${RunCMake_TEST}_IS_TOP_LEVEL = ${${RunCMake_TEST}_IS_TOP_LEVEL}" diff --git a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt index c72534fe1..cb5d87449 100644 --- a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt +++ b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt @@ -12,7 +12,7 @@ This warning is for project developers. Use -Wno-dev to suppress it\. + CMake Deprecation Warning at CMakeLists\.txt:1 \(cmake_minimum_required\): - Compatibility with CMake < 3\.5 will be removed from a future version of + Compatibility with CMake < 3\.10 will be removed from a future version of CMake\. Update the VERSION argument value or use a \.\.\. suffix to tell diff --git a/Tests/RunCMake/property_init/CompileSources.cmake b/Tests/RunCMake/property_init/CompileSources.cmake index 22b8f3fb1..eb0a77f2c 100644 --- a/Tests/RunCMake/property_init/CompileSources.cmake +++ b/Tests/RunCMake/property_init/CompileSources.cmake @@ -177,6 +177,7 @@ set(properties # Metadata "EXPORT_COMPILE_COMMANDS" "OFF" "" + "EXPORT_BUILD_DATABASE" "OFF" "" ) if (CMAKE_HOST_APPLE) # compile-guarded in CMake diff --git a/Tests/RunCMake/pseudo_llvm-rc.c b/Tests/RunCMake/pseudo_llvm-rc.c index 65f0a9e12..e495e8d84 100644 --- a/Tests/RunCMake/pseudo_llvm-rc.c +++ b/Tests/RunCMake/pseudo_llvm-rc.c @@ -9,6 +9,8 @@ int main(int argc, char* argv[]) { FILE* source; FILE* target; + char buffer[500]; + size_t n; int i; for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "-bad") == 0) { @@ -22,13 +24,13 @@ int main(int argc, char* argv[]) return 1; } target = fopen(argv[argc - 2], "wb"); - if (target != NULL) { - char buffer[500]; - size_t n = fread(buffer, 1, sizeof(buffer), source); - fwrite(buffer, 1, n, target); + if (target == NULL) { fclose(source); - fclose(target); - return 0; + return 1; } - return 1; + n = fread(buffer, 1, sizeof(buffer), source); + fwrite(buffer, 1, n, target); + fclose(source); + fclose(target); + return 0; } diff --git a/Tests/RunCMake/separate_arguments/CMakeLists.txt b/Tests/RunCMake/separate_arguments/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/separate_arguments/CMakeLists.txt +++ b/Tests/RunCMake/separate_arguments/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/set/CMakeLists.txt b/Tests/RunCMake/set/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/set/CMakeLists.txt +++ b/Tests/RunCMake/set/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/set_property/CMakeLists.txt b/Tests/RunCMake/set_property/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/set_property/CMakeLists.txt +++ b/Tests/RunCMake/set_property/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/string/CMakeLists.txt b/Tests/RunCMake/string/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/string/CMakeLists.txt +++ b/Tests/RunCMake/string/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_compile_features/CMakeLists.txt b/Tests/RunCMake/target_compile_features/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/target_compile_features/CMakeLists.txt +++ b/Tests/RunCMake/target_compile_features/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/CMakeLists.txt b/Tests/RunCMake/target_link_libraries-ALIAS/CMakeLists.txt index 9cf020fb0..30c0273a6 100644 --- a/Tests/RunCMake/target_link_libraries-ALIAS/CMakeLists.txt +++ b/Tests/RunCMake/target_link_libraries-ALIAS/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/CMakeLists.txt b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/CMakeLists.txt index 9cf020fb0..30c0273a6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/CMakeLists.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/CMakeLists.txt b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/CMakeLists.txt index 9cf020fb0..30c0273a6 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/CMakeLists.txt +++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_link_libraries/CMakeLists.txt b/Tests/RunCMake/target_link_libraries/CMakeLists.txt index 8eb57486a..30c0273a6 100644 --- a/Tests/RunCMake/target_link_libraries/CMakeLists.txt +++ b/Tests/RunCMake/target_link_libraries/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER-check.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER-check.cmake index d0ef8de85..99f5aa3fc 100644 --- a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER-check.cmake +++ b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER-check.cmake @@ -1,2 +1,3 @@ +set(reference_file "LINKER.txt") include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake") diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED-check.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED-check.cmake new file mode 100644 index 000000000..522fe86e6 --- /dev/null +++ b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED-check.cmake @@ -0,0 +1,4 @@ + +set(reference_file "LINKER_NESTED.txt") +set(linker_prefix_expected YES) +include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake") diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED_SHELL-check.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED_SHELL-check.cmake new file mode 100644 index 000000000..917455ce7 --- /dev/null +++ b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_NESTED_SHELL-check.cmake @@ -0,0 +1,4 @@ + +set(reference_file "LINKER_NESTED_SHELL.txt") +set(linker_prefix_expected YES) +include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake") diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_SHELL-check.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_SHELL-check.cmake index d0ef8de85..99f5aa3fc 100644 --- a/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_SHELL-check.cmake +++ b/Tests/RunCMake/target_link_options/LINKER_expansion-LINKER_SHELL-check.cmake @@ -1,2 +1,3 @@ +set(reference_file "LINKER.txt") include ("${CMAKE_CURRENT_LIST_DIR}/LINKER_expansion-validation.cmake") diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion-validation.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion-validation.cmake index 1af8f13a3..27adde674 100644 --- a/Tests/RunCMake/target_link_options/LINKER_expansion-validation.cmake +++ b/Tests/RunCMake/target_link_options/LINKER_expansion-validation.cmake @@ -1,14 +1,14 @@ -if (actual_stdout MATCHES "LINKER:") +if (actual_stdout MATCHES "LINKER:" AND NOT linker_prefix_expected) set (RunCMake_TEST_FAILED "LINKER: prefix was not expanded.") return() endif() -if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/LINKER.txt") - set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/LINKER.txt: Reference file not found.") +if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${reference_file}") + set (RunCMake_TEST_FAILED "${RunCMake_TEST_BINARY_DIR}/${reference_file}: Reference file not found.") return() endif() -file(READ "${RunCMake_TEST_BINARY_DIR}/LINKER.txt" linker_flag) +file(READ "${RunCMake_TEST_BINARY_DIR}/${reference_file}" linker_flag) if (NOT actual_stdout MATCHES "${linker_flag}") set (RunCMake_TEST_FAILED "LINKER: was not expanded correctly.") diff --git a/Tests/RunCMake/target_link_options/LINKER_expansion.cmake b/Tests/RunCMake/target_link_options/LINKER_expansion.cmake index f86d19fa9..221921897 100644 --- a/Tests/RunCMake/target_link_options/LINKER_expansion.cmake +++ b/Tests/RunCMake/target_link_options/LINKER_expansion.cmake @@ -14,26 +14,30 @@ add_executable(dump dump.c) string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}") string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}") +function (add_test_library target_name) + add_library(${target_name} SHARED LinkOptionsLib.c) -# Use LINKER alone -add_library(linker SHARED LinkOptionsLib.c) -target_link_options(linker PRIVATE "LINKER:-foo,bar") + # use LAUNCH facility to dump linker command + set_property(TARGET ${target_name} PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"") -# use LAUNCH facility to dump linker command -set_property(TARGET linker PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"") - -add_dependencies (linker dump) + add_dependencies(${target_name} dump) +endfunction() +# Use LINKER alone +add_test_library(linker) +target_link_options(linker PRIVATE "LINKER:-foo,bar") # Use LINKER with SHELL -add_library(linker_shell SHARED LinkOptionsLib.c) +add_test_library(linker_shell) target_link_options(linker_shell PRIVATE "LINKER:SHELL:-foo bar") -# use LAUNCH facility to dump linker command -set_property(TARGET linker_shell PROPERTY RULE_LAUNCH_LINK "\"${DUMP_EXE}\"") - -add_dependencies (linker_shell dump) +# Nested LINKER: prefixes should be preserved as written, only the outermost LINKER: prefix removed +add_test_library(linker_nested) +target_link_options(linker_nested PRIVATE "LINKER:LINKER:-foo,-Xlinker=bar,-Xlinker,--baz,-Wl,/qux") +# Same with LINKER:SHELL: +add_test_library(linker_nested_shell SHARED LinkOptionsLib.c) +target_link_options(linker_nested_shell PRIVATE "LINKER:SHELL:LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl,/qux") # generate reference for LINKER flag if (CMAKE_C_LINKER_WRAPPER_FLAG) @@ -46,11 +50,23 @@ if (CMAKE_C_LINKER_WRAPPER_FLAG) endif() list (JOIN linker_flag " " linker_flag) if (CMAKE_C_LINKER_WRAPPER_FLAG_SEP) - string (APPEND linker_flag "${linker_space}" "-foo${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}bar") + set(linker_sep "${CMAKE_C_LINKER_WRAPPER_FLAG_SEP}") + + set(linker_flag_nested "${linker_flag}${linker_space}LINKER:-foo${linker_sep}-Xlinker=bar${linker_sep}-Xlinker${linker_sep}--baz${linker_sep}-Wl${linker_sep}/qux") + set(linker_flag_nested_shell "${linker_flag}${linker_space}LINKER:-foo${linker_sep}-Xlinker=bar${linker_sep}-Xlinker${linker_sep}--baz${linker_sep}-Wl,/qux") + string (APPEND linker_flag "${linker_space}" "-foo${linker_sep}bar") else() - set (linker_flag "${linker_flag}${linker_space}-foo ${linker_flag}${linker_space}bar") + set(linker_prefix "${linker_flag}${linker_space}") + + set(linker_flag_nested "${linker_prefix}LINKER:-foo ${linker_prefix}-Xlinker=bar ${linker_prefix}-Xlinker ${linker_prefix}--baz ${linker_prefix}-Wl ${linker_prefix}/qux") + set(linker_flag_nested_shell "${linker_prefix}LINKER:-foo ${linker_prefix}-Xlinker=bar ${linker_prefix}-Xlinker ${linker_prefix}--baz ${linker_prefix}-Wl,/qux") + set (linker_flag "${linker_prefix}-foo ${linker_prefix}bar") endif() else() + set(linker_flag_nested "LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl /qux") + set(linker_flag_nested_shell "LINKER:-foo -Xlinker=bar -Xlinker --baz -Wl,/qux") set(linker_flag "-foo bar") endif() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER.txt" "${linker_flag}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_NESTED.txt" "${linker_flag_nested}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_NESTED_SHELL.txt" "${linker_flag_nested_shell}") diff --git a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake index ff0c5a825..5562c9e95 100644 --- a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake @@ -76,6 +76,8 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)") run_cmake_target(LINKER_expansion LINKER linker) run_cmake_target(LINKER_expansion LINKER_SHELL linker_shell) + run_cmake_target(LINKER_expansion LINKER_NESTED linker_nested) + run_cmake_target(LINKER_expansion LINKER_NESTED_SHELL linker_nested_shell) endif() run_cmake(empty_keyword_args) diff --git a/Tests/RunCMake/test_include_dirs/CMakeLists.txt b/Tests/RunCMake/test_include_dirs/CMakeLists.txt index dc9248697..bf2ef1506 100644 --- a/Tests/RunCMake/test_include_dirs/CMakeLists.txt +++ b/Tests/RunCMake/test_include_dirs/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/try_compile/BadLinkLibraries-stderr.txt b/Tests/RunCMake/try_compile/BadLinkLibraries-stderr.txt index 4e41a19dc..857f0248b 100644 --- a/Tests/RunCMake/try_compile/BadLinkLibraries-stderr.txt +++ b/Tests/RunCMake/try_compile/BadLinkLibraries-stderr.txt @@ -2,4 +2,4 @@ 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\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/BadSources1-stderr.txt b/Tests/RunCMake/try_compile/BadSources1-stderr.txt index ddcba4f35..865f65136 100644 --- a/Tests/RunCMake/try_compile/BadSources1-stderr.txt +++ b/Tests/RunCMake/try_compile/BadSources1-stderr.txt @@ -9,4 +9,4 @@ CMake Error at BadSources1.cmake:[0-9]+ \(try_compile\): See project\(\) command to enable other languages. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/BadSources2-stderr.txt b/Tests/RunCMake/try_compile/BadSources2-stderr.txt index 953dd9ccc..1589eaa7f 100644 --- a/Tests/RunCMake/try_compile/BadSources2-stderr.txt +++ b/Tests/RunCMake/try_compile/BadSources2-stderr.txt @@ -9,4 +9,4 @@ CMake Error at BadSources2.cmake:[0-9]+ \(try_compile\): See project\(\) command to enable other languages. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/CMP0066-stderr.txt b/Tests/RunCMake/try_compile/CMP0066-stderr.txt index 0b92dcfc7..0b621f0f7 100644 --- a/Tests/RunCMake/try_compile/CMP0066-stderr.txt +++ b/Tests/RunCMake/try_compile/CMP0066-stderr.txt @@ -1,3 +1,10 @@ +^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\): + Compatibility with CMake < 3\.10 will be removed from a future version of + CMake\. + + Update the VERSION argument value or use a \.\.\. suffix to tell + CMake that the project does not need compatibility with older versions\. ++ before try_compile with CMP0066 WARN-default after try_compile with CMP0066 WARN-default * diff --git a/Tests/RunCMake/try_compile/CMakeLists.txt b/Tests/RunCMake/try_compile/CMakeLists.txt index 93ee9dfd5..ab5008dc7 100644 --- a/Tests/RunCMake/try_compile/CMakeLists.txt +++ b/Tests/RunCMake/try_compile/CMakeLists.txt @@ -1,3 +1,8 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) +if(RunCMake_TEST STREQUAL "CMP0066") + cmake_policy(VERSION 3.6) # old enough to not set CMP0066 +elseif(RunCMake_TEST STREQUAL "CMP0067") + cmake_policy(VERSION 3.7) # old enough to not set CMP0067 +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt index 8c4930287..25d6c6ba4 100644 --- a/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt +++ b/Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoArgs-stderr.txt b/Tests/RunCMake/try_compile/NoArgs-stderr.txt index 422858006..036bccb89 100644 --- a/Tests/RunCMake/try_compile/NoArgs-stderr.txt +++ b/Tests/RunCMake/try_compile/NoArgs-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoCStandard-stderr.txt b/Tests/RunCMake/try_compile/NoCStandard-stderr.txt index e0bb9ed14..6093510c2 100644 --- a/Tests/RunCMake/try_compile/NoCStandard-stderr.txt +++ b/Tests/RunCMake/try_compile/NoCStandard-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoCStandard.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoCopyFile-stderr.txt b/Tests/RunCMake/try_compile/NoCopyFile-stderr.txt index 55ba687c0..0cb522887 100644 --- a/Tests/RunCMake/try_compile/NoCopyFile-stderr.txt +++ b/Tests/RunCMake/try_compile/NoCopyFile-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoCopyFile.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt b/Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt index 008d4e9a4..1271b1285 100644 --- a/Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt +++ b/Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoCopyFile2.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt b/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt index 7ca185d9e..793a9310e 100644 --- a/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt +++ b/Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoCopyFileError.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoLogDescription-stderr.txt b/Tests/RunCMake/try_compile/NoLogDescription-stderr.txt index 9005bcd87..806b2b41d 100644 --- a/Tests/RunCMake/try_compile/NoLogDescription-stderr.txt +++ b/Tests/RunCMake/try_compile/NoLogDescription-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoLogDescription.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt b/Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt index ec29f0bee..0cdd4f17b 100644 --- a/Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt +++ b/Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoOutputVariable.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt b/Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt index dd345593c..7382ee62f 100644 --- a/Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt +++ b/Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoOutputVariable2.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NoSources-stderr.txt b/Tests/RunCMake/try_compile/NoSources-stderr.txt index 55603dca1..df1d33566 100644 --- a/Tests/RunCMake/try_compile/NoSources-stderr.txt +++ b/Tests/RunCMake/try_compile/NoSources-stderr.txt @@ -4,4 +4,4 @@ CMake Error at NoSources.cmake:[0-9]+ \(try_compile\): missing required value Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NonSourceCompileDefinitions-stderr.txt b/Tests/RunCMake/try_compile/NonSourceCompileDefinitions-stderr.txt index dcd5c7a76..21dcdaf42 100644 --- a/Tests/RunCMake/try_compile/NonSourceCompileDefinitions-stderr.txt +++ b/Tests/RunCMake/try_compile/NonSourceCompileDefinitions-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/NonSourceCopyFile-stderr.txt b/Tests/RunCMake/try_compile/NonSourceCopyFile-stderr.txt index 1b7dfeb24..e61360e60 100644 --- a/Tests/RunCMake/try_compile/NonSourceCopyFile-stderr.txt +++ b/Tests/RunCMake/try_compile/NonSourceCopyFile-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt index 47dd60f8d..d18e6ec45 100644 --- a/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt +++ b/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt @@ -1,4 +1,4 @@ CMake Error at OldProjectSrcDirEmpty.cmake:[0-9]+ \(try_compile\): No specified. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/OneArg-stderr.txt b/Tests/RunCMake/try_compile/OneArg-stderr.txt index a2e983efb..13727d751 100644 --- a/Tests/RunCMake/try_compile/OneArg-stderr.txt +++ b/Tests/RunCMake/try_compile/OneArg-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt b/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt index 45241c930..5c239b64e 100644 --- a/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt +++ b/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt @@ -4,4 +4,4 @@ CMake Warning \(dev\) at ProjectCopyFile.cmake:[0-9]+ \(try_compile\): "COPY_FILE" "result" Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt index dc6f3543d..d7af931a5 100644 --- a/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt +++ b/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt @@ -1,4 +1,4 @@ CMake Error at ProjectSrcDirEmpty.cmake:[0-9]+ \(try_compile\): No specified. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt b/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt index af6edd528..3422267fb 100644 --- a/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt +++ b/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt @@ -1,4 +1,4 @@ CMake Error at ProjectSrcDirMissing.cmake:[0-9]+ \(try_compile\): No specified. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt index 53a6d8df6..9a3470d4f 100644 --- a/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt +++ b/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt @@ -1,4 +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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt index 041f3f144..a819a9731 100644 --- a/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt +++ b/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt @@ -1,4 +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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt b/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt index 8b2248a7a..6de3b1ebe 100644 --- a/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt +++ b/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt @@ -1,4 +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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt b/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt index 5f2ff618a..2e497e809 100644 --- a/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt +++ b/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt @@ -1,4 +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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/TargetTypeInvalid-stderr.txt b/Tests/RunCMake/try_compile/TargetTypeInvalid-stderr.txt index e9123e32b..65f74d7d7 100644 --- a/Tests/RunCMake/try_compile/TargetTypeInvalid-stderr.txt +++ b/Tests/RunCMake/try_compile/TargetTypeInvalid-stderr.txt @@ -2,4 +2,4 @@ Invalid value 'INVALID' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only 'EXECUTABLE' and 'STATIC_LIBRARY' are allowed. Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/TwoArgs-stderr.txt b/Tests/RunCMake/try_compile/TwoArgs-stderr.txt index b68e78e37..6df1ac1e4 100644 --- a/Tests/RunCMake/try_compile/TwoArgs-stderr.txt +++ b/Tests/RunCMake/try_compile/TwoArgs-stderr.txt @@ -1,4 +1,4 @@ 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\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/try_compile/proj/CMakeLists.txt b/Tests/RunCMake/try_compile/proj/CMakeLists.txt index 09feca417..d257b5ea0 100644 --- a/Tests/RunCMake/try_compile/proj/CMakeLists.txt +++ b/Tests/RunCMake/try_compile/proj/CMakeLists.txt @@ -1,2 +1,2 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestProject NONE) diff --git a/Tests/RunCMake/try_run/CMakeLists.txt b/Tests/RunCMake/try_run/CMakeLists.txt index e6c41a5cb..d1b0d2c3e 100644 --- a/Tests/RunCMake/try_run/CMakeLists.txt +++ b/Tests/RunCMake/try_run/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} C) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/variable_watch/CMakeLists.txt b/Tests/RunCMake/variable_watch/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/variable_watch/CMakeLists.txt +++ b/Tests/RunCMake/variable_watch/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/while/CMakeLists.txt b/Tests/RunCMake/while/CMakeLists.txt index 93ee9dfd5..bf2ef1506 100644 --- a/Tests/RunCMake/while/CMakeLists.txt +++ b/Tests/RunCMake/while/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/SetLang/CMakeLists.txt b/Tests/SetLang/CMakeLists.txt index 14e9d6e60..edfb61c23 100644 --- a/Tests/SetLang/CMakeLists.txt +++ b/Tests/SetLang/CMakeLists.txt @@ -1,5 +1,5 @@ # test forcing a source file language to c++ from c -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/SharedLibraryArchive/CMakeLists.txt b/Tests/SharedLibraryArchive/CMakeLists.txt new file mode 100644 index 000000000..f6bef2434 --- /dev/null +++ b/Tests/SharedLibraryArchive/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.30) +project(SharedLibraryArchive C) + +set(CMAKE_AIX_SHARED_LIBRARY_ARCHIVE 1) + +add_library(sla SHARED sla.c) +get_property(aix_sla TARGET sla PROPERTY AIX_SHARED_LIBRARY_ARCHIVE) +if(NOT aix_sla) + message(FATAL_ERROR "AIX_SHARED_LIBRARY_ARCHIVE not initialized on SHARED library") +endif() +add_custom_command(TARGET sla POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -Dsla=$ -Dname=sla -Dsoversion= -P${CMAKE_CURRENT_SOURCE_DIR}/sla-check.cmake + ) + +add_executable(UseSLA use_sla.c) +get_property(aix_sla TARGET UseSLA PROPERTY AIX_SHARED_LIBRARY_ARCHIVE) +if(aix_sla) + message(FATAL_ERROR "AIX_SHARED_LIBRARY_ARCHIVE initialized on EXECUTABLE") +endif() +target_link_libraries(UseSLA PRIVATE sla) + +# Test for versioned names. Everything else should be tested and set above. +add_library(sla_versioned SHARED sla.c) +get_property(aix_sla_versioned TARGET sla_versioned PROPERTY AIX_SHARED_LIBRARY_ARCHIVE) +if(NOT aix_sla_versioned) + message(FATAL_ERROR "AIX_SHARED_LIBRARY_ARCHIVE not initialized on SHARED library") +endif() +set_target_properties(sla_versioned PROPERTIES OUTPUT_NAME "sla_versioned" VERSION 3 SOVERSION 2) +add_custom_command(TARGET sla_versioned POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -Dsla=$ -Dname=sla_versioned -Dsoversion=2 -P${CMAKE_CURRENT_SOURCE_DIR}/sla-check.cmake + ) + +add_executable(UseSLA_versioned use_sla.c) +get_property(aix_sla_versioned TARGET UseSLA_versioned PROPERTY AIX_SHARED_LIBRARY_ARCHIVE) +if(aix_sla_versioned) + message(FATAL_ERROR "AIX_SHARED_LIBRARY_ARCHIVE initialized on EXECUTABLE") +endif() +target_link_libraries(UseSLA_versioned PRIVATE sla_versioned) diff --git a/Tests/SharedLibraryArchive/sla-check.cmake b/Tests/SharedLibraryArchive/sla-check.cmake new file mode 100644 index 000000000..90a0de436 --- /dev/null +++ b/Tests/SharedLibraryArchive/sla-check.cmake @@ -0,0 +1,14 @@ +if(CMAKE_SYSTEM_NAME STREQUAL "AIX") + set(sla_regex "/lib${name}\\.a$") + if(NOT sla MATCHES "${sla_regex}") + message(FATAL_ERROR "sla library does not look like an archive:\n ${sla}") + endif() + execute_process(COMMAND ar t ${sla} OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE sla_members) + if(soversion) + set(soversion_regex "\\.${soversion}") + endif() + set(sla_members_regex "^lib${name}\\.so${soversion_regex}$") + if(NOT sla_members MATCHES "${sla_members_regex}") + message(FATAL_ERROR "sla library archive has members:\n ${sla_members}\nthat do not match:\n ${sla_members_regex}") + endif() +endif() diff --git a/Tests/SharedLibraryArchive/sla.c b/Tests/SharedLibraryArchive/sla.c new file mode 100644 index 000000000..5bc452fcc --- /dev/null +++ b/Tests/SharedLibraryArchive/sla.c @@ -0,0 +1,7 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + int sla(void) +{ + return 0; +} diff --git a/Tests/SharedLibraryArchive/use_sla.c b/Tests/SharedLibraryArchive/use_sla.c new file mode 100644 index 000000000..0b821357e --- /dev/null +++ b/Tests/SharedLibraryArchive/use_sla.c @@ -0,0 +1,9 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + int sla(void); + +int main(void) +{ + return sla(); +} diff --git a/Tests/SourceFileIncludeDirProperty/CMakeLists.txt b/Tests/SourceFileIncludeDirProperty/CMakeLists.txt index 786d5b683..8a0b29759 100644 --- a/Tests/SourceFileIncludeDirProperty/CMakeLists.txt +++ b/Tests/SourceFileIncludeDirProperty/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(SourceFileIncludeDirProperty C) diff --git a/Tests/SourceFileProperty/CMakeLists.txt b/Tests/SourceFileProperty/CMakeLists.txt index 5e55f7b3d..d980e04b9 100644 --- a/Tests/SourceFileProperty/CMakeLists.txt +++ b/Tests/SourceFileProperty/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(SourceFileProperty C) if (EXISTS icasetest.c) diff --git a/Tests/SourceGroups/CMakeLists.txt b/Tests/SourceGroups/CMakeLists.txt index 550fe9ed1..901996d2a 100644 --- a/Tests/SourceGroups/CMakeLists.txt +++ b/Tests/SourceGroups/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.8) +cmake_minimum_required(VERSION 3.10) project(SourceGroups) # this is not really a test which can fail diff --git a/Tests/SourcesProperty/CMakeLists.txt b/Tests/SourcesProperty/CMakeLists.txt index d1c35d801..a5f164e9b 100644 --- a/Tests/SourcesProperty/CMakeLists.txt +++ b/Tests/SourcesProperty/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(SourcesProperty) diff --git a/Tests/StagingPrefix/CMakeLists.txt b/Tests/StagingPrefix/CMakeLists.txt index 9ed5c1243..b2901d55a 100644 --- a/Tests/StagingPrefix/CMakeLists.txt +++ b/Tests/StagingPrefix/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(StagingPrefix) # Wipe out the install tree diff --git a/Tests/StagingPrefix/Consumer/CMakeLists.txt b/Tests/StagingPrefix/Consumer/CMakeLists.txt index f7195b0ff..f2b344e18 100644 --- a/Tests/StagingPrefix/Consumer/CMakeLists.txt +++ b/Tests/StagingPrefix/Consumer/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Consumer) diff --git a/Tests/StagingPrefix/Producer/CMakeLists.txt b/Tests/StagingPrefix/Producer/CMakeLists.txt index c02d5de27..673447261 100644 --- a/Tests/StagingPrefix/Producer/CMakeLists.txt +++ b/Tests/StagingPrefix/Producer/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(Producer) add_library(foo SHARED foo.cpp) diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index 6c0de9d7d..f0dd6a6b9 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(StringFileTest) include_directories(${StringFileTest_BINARY_DIR}) diff --git a/Tests/SubDir/CMakeLists.txt b/Tests/SubDir/CMakeLists.txt index 32aa93ffc..db9b3fede 100644 --- a/Tests/SubDir/CMakeLists.txt +++ b/Tests/SubDir/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(SUBDIR) subdirs(Executable EXCLUDE_FROM_ALL Examples) diff --git a/Tests/SubDir/Examples/CMakeLists.txt b/Tests/SubDir/Examples/CMakeLists.txt index 8cd7e24e1..2447af39f 100644 --- a/Tests/SubDir/Examples/CMakeLists.txt +++ b/Tests/SubDir/Examples/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(Examples) subdirs(example1 example2) diff --git a/Tests/SubDir/Examples/example1/CMakeLists.txt b/Tests/SubDir/Examples/example1/CMakeLists.txt index 8ec1c0269..df4ef83c0 100644 --- a/Tests/SubDir/Examples/example1/CMakeLists.txt +++ b/Tests/SubDir/Examples/example1/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(example1) add_executable(example1 example1.cxx) diff --git a/Tests/SubDirSpaces/CMakeLists.txt b/Tests/SubDirSpaces/CMakeLists.txt index 26a7da98d..050ce93d6 100644 --- a/Tests/SubDirSpaces/CMakeLists.txt +++ b/Tests/SubDirSpaces/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(SUBDIR) # Some systems do not seem to support rpath with spaces. diff --git a/Tests/SubDirSpaces/Some Examples/CMakeLists.txt b/Tests/SubDirSpaces/Some Examples/CMakeLists.txt index 8cd7e24e1..79c44895d 100644 --- a/Tests/SubDirSpaces/Some Examples/CMakeLists.txt +++ b/Tests/SubDirSpaces/Some Examples/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.10) project(Examples) subdirs(example1 example2) diff --git a/Tests/SubDirSpaces/Some Examples/example1/CMakeLists.txt b/Tests/SubDirSpaces/Some Examples/example1/CMakeLists.txt index 8ec1c0269..54db8a968 100644 --- a/Tests/SubDirSpaces/Some Examples/example1/CMakeLists.txt +++ b/Tests/SubDirSpaces/Some Examples/example1/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.10) project(example1) add_executable(example1 example1.cxx) diff --git a/Tests/SubProject/CMakeLists.txt b/Tests/SubProject/CMakeLists.txt index b2bada911..a79ae7439 100644 --- a/Tests/SubProject/CMakeLists.txt +++ b/Tests/SubProject/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(SubProject) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/gen.cxx) # require generation add_custom_command( diff --git a/Tests/SwiftMixLib/CMakeLists.txt b/Tests/SwiftMixLib/CMakeLists.txt index d23c6bad0..c2cfb8f4f 100644 --- a/Tests/SwiftMixLib/CMakeLists.txt +++ b/Tests/SwiftMixLib/CMakeLists.txt @@ -10,3 +10,15 @@ target_link_libraries(Swifty PUBLIC SwiftMixedLib) add_executable(c_main main.c) target_link_libraries(c_main PUBLIC SwiftMixedLib) + +if(WIN32) + # TODO: On macOS and Linux, Swift has mechanism for determining what libraries + # an object, or objects from a static archive, need to link against, + # which is how the Swift driver is able to determine that `c_main` + # needs to link swiftCore. Windows does not have this mechanism. + # Eventually CMake should learn how to do this, explicitly forwarding + # the required library to the link command when linking a static + # archive containing Swift sources into something else, even if the + # linker language is Swift. + target_link_libraries(c_main PRIVATE swiftCore) +endif() diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt index e933ea13a..ba36c10b4 100644 --- a/Tests/SwiftOnly/CMakeLists.txt +++ b/Tests/SwiftOnly/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0126) cmake_policy(SET CMP0126 NEW) endif() diff --git a/Tests/SystemInformation/CMakeLists.txt b/Tests/SystemInformation/CMakeLists.txt index 9a2c4ebcc..c020ef955 100644 --- a/Tests/SystemInformation/CMakeLists.txt +++ b/Tests/SystemInformation/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(SystemInformation) include_directories("This does not exist") diff --git a/Tests/TargetName/CMakeLists.txt b/Tests/TargetName/CMakeLists.txt index 98640013b..bed88892f 100644 --- a/Tests/TargetName/CMakeLists.txt +++ b/Tests/TargetName/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TargetName) add_subdirectory(executables) diff --git a/Tests/TestDriver/CMakeLists.txt b/Tests/TestDriver/CMakeLists.txt index cd7937286..20b265d89 100644 --- a/Tests/TestDriver/CMakeLists.txt +++ b/Tests/TestDriver/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestDriverTest) set(Extra_SRCS testExtraStuff.cxx testExtraStuff2.cxx ) diff --git a/Tests/Testing/CMakeLists.txt b/Tests/Testing/CMakeLists.txt index 44afd4e8c..45ee8587f 100644 --- a/Tests/Testing/CMakeLists.txt +++ b/Tests/Testing/CMakeLists.txt @@ -1,7 +1,7 @@ # # Testing # -cmake_minimum_required (VERSION 2.7) +cmake_minimum_required(VERSION 3.10) project (Testing) include (CTest) @@ -30,7 +30,10 @@ endif () # Extra coverage # build_command(BUILD_COMMAND_VAR ${CMAKE_MAKE_PROGRAM}) +block(SCOPE_FOR POLICIES) +cmake_policy(VERSION 2.8.12) # old enough to not set CMP0036 build_name(BUILD_NAME_VAR) +endblock() site_name(SITE_NAME_VAR) # diff --git a/Tests/TestsWorkingDirectory/CMakeLists.txt b/Tests/TestsWorkingDirectory/CMakeLists.txt index c7b58a932..b17bd52a8 100644 --- a/Tests/TestsWorkingDirectory/CMakeLists.txt +++ b/Tests/TestsWorkingDirectory/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TestsWorkingDirectoryProj) add_executable(WorkingDirectory main.c) diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt index ab2e00742..daf8c8ec5 100644 --- a/Tests/TryCompile/CMakeLists.txt +++ b/Tests/TryCompile/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) if(POLICY CMP0129) cmake_policy(SET CMP0129 NEW) endif() diff --git a/Tests/TryCompile/Inner/CMakeLists.txt b/Tests/TryCompile/Inner/CMakeLists.txt index 262662dbe..95c4be354 100644 --- a/Tests/TryCompile/Inner/CMakeLists.txt +++ b/Tests/TryCompile/Inner/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(TryCompileInner C) try_compile(SHOULD_PASS diff --git a/Tests/Unset/CMakeLists.txt b/Tests/Unset/CMakeLists.txt index a40367bbe..0803c8a8b 100644 --- a/Tests/Unset/CMakeLists.txt +++ b/Tests/Unset/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project(Unset C) # Local variable diff --git a/Tests/UseSWIG/CMakeLists.txt b/Tests/UseSWIG/CMakeLists.txt index 09710259a..d36c3ff2f 100644 --- a/Tests/UseSWIG/CMakeLists.txt +++ b/Tests/UseSWIG/CMakeLists.txt @@ -87,7 +87,7 @@ add_test(NAME UseSWIG.BasicPerl5 COMMAND --test-command ${CMAKE_CTEST_COMMAND} -V -C $ ) if(SWIG_FOUND AND NOT SWIG_VERSION VERSION_LESS "4.0.2" - AND CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])") + AND CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio") add_test(NAME UseSWIG.Depfile.BasicPython COMMAND ${CMAKE_CTEST_COMMAND} -C $ --build-and-test diff --git a/Tests/UseSWIG/LegacyPerl/CMakeLists.txt b/Tests/UseSWIG/LegacyPerl/CMakeLists.txt index be0b4657e..a8e3f9c43 100644 --- a/Tests/UseSWIG/LegacyPerl/CMakeLists.txt +++ b/Tests/UseSWIG/LegacyPerl/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(TestLegacyPerl CXX) diff --git a/Tests/UseSWIG/LegacyPython/CMakeLists.txt b/Tests/UseSWIG/LegacyPython/CMakeLists.txt index 03facb19a..200af2eb1 100644 --- a/Tests/UseSWIG/LegacyPython/CMakeLists.txt +++ b/Tests/UseSWIG/LegacyPython/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(TestLegacyPython CXX) diff --git a/Tests/VSAndroid/CMakeLists.txt b/Tests/VSAndroid/CMakeLists.txt index 774ffc072..1f5e74c9b 100644 --- a/Tests/VSAndroid/CMakeLists.txt +++ b/Tests/VSAndroid/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.10) project(VSAndroid C CXX) set(CMAKE_ANDROID_ARCH armv7-a) diff --git a/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt b/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt index 243fdf589..8aa47a71b 100644 --- a/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt +++ b/Tests/VSExcludeFromDefaultBuild/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.10) project(VSExcludeFromDefaultBuild) # CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD will enable the INSTALL target to be part of the default build diff --git a/Tests/VSExternalInclude/CMakeLists.txt b/Tests/VSExternalInclude/CMakeLists.txt index a44988e24..06bb1ff0b 100644 --- a/Tests/VSExternalInclude/CMakeLists.txt +++ b/Tests/VSExternalInclude/CMakeLists.txt @@ -1,12 +1,6 @@ -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(VSExternalInclude) -if(${CMAKE_GENERATOR} MATCHES "Visual Studio 1[0124567]") - set(PROJECT_EXT vcxproj) -else() - set(PROJECT_EXT vcproj) -endif() - # make sure directories exists set(LIB1_BINARY_DIR ${VSExternalInclude_BINARY_DIR}/Lib1) make_directory("${LIB1_BINARY_DIR}") @@ -37,9 +31,9 @@ execute_process( message("CMAKE Ran with the following output:\n\"${OUT}\"") -include_external_msproject(lib1 ${VSExternalInclude_BINARY_DIR}/Lib1/LIB1.${PROJECT_EXT}) +include_external_msproject(lib1 ${VSExternalInclude_BINARY_DIR}/Lib1/LIB1.vcxproj) # lib2 depends on lib1 -include_external_msproject(lib2 ${VSExternalInclude_BINARY_DIR}/Lib2/LIB2.${PROJECT_EXT} lib1) +include_external_msproject(lib2 ${VSExternalInclude_BINARY_DIR}/Lib2/LIB2.vcxproj lib1) include_directories(${VSExternalInclude_SOURCE_DIR}/Lib2 ${VSExternalInclude_SOURCE_DIR}/Lib1) @@ -55,9 +49,7 @@ add_dependencies(VSExternalInclude lib2) # and the sln file can no longer be the only source # of that depend. So, for VS 10 make the executable # depend on lib1 and lib2 -if(${CMAKE_GENERATOR} MATCHES "Visual Studio 1[0124567]") - add_dependencies(VSExternalInclude lib1) -endif() +add_dependencies(VSExternalInclude lib1) # Interaction testing between the FOLDER target property and # INCLUDE_EXTERNAL_MSPROJECT targets: diff --git a/Tests/VSGNUFortran/CMakeLists.txt b/Tests/VSGNUFortran/CMakeLists.txt index 2b4f4fa9c..4ec082f09 100644 --- a/Tests/VSGNUFortran/CMakeLists.txt +++ b/Tests/VSGNUFortran/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.10) project(VSGNUFortran) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") diff --git a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt index 8918d9832..6c7265f05 100644 --- a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt +++ b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(FortranHello Fortran C) # add a function to test for -lsunquad on sunpro sun systems. @@ -25,6 +25,10 @@ target_link_libraries(sunq sunquad) set(${result} "${RESULT}" PARENT_SCOPE) endfunction() +if(CMAKE_Fortran_COMPILER_ID STREQUAL "LFortran") + add_compile_options(--implicit-interface) +endif() + # check for the fortran c interface mangling include(FortranCInterface) FortranCInterface_HEADER(HelloWorldFCMangle.h diff --git a/Tests/VSMARMASM/CMakeLists.txt b/Tests/VSMARMASM/CMakeLists.txt index 85740de18..749846139 100644 --- a/Tests/VSMARMASM/CMakeLists.txt +++ b/Tests/VSMARMASM/CMakeLists.txt @@ -4,3 +4,4 @@ add_executable(VSMARMASM main.c foo.asm) target_compile_options(VSMARMASM PRIVATE "$<$:SHELL:-predefine \"zero SETA 0\">" ) +add_library(empty STATIC empty.asm) diff --git a/Tests/VSMARMASM/empty.asm b/Tests/VSMARMASM/empty.asm new file mode 100644 index 000000000..a6a9baf65 --- /dev/null +++ b/Tests/VSMARMASM/empty.asm @@ -0,0 +1 @@ +end diff --git a/Tests/VSMASM/CMakeLists.txt b/Tests/VSMASM/CMakeLists.txt index 49bd24a96..bf9a4b85f 100644 --- a/Tests/VSMASM/CMakeLists.txt +++ b/Tests/VSMASM/CMakeLists.txt @@ -10,3 +10,4 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) add_executable(VSMASM main.c foo.asm) target_compile_definitions(VSMASM PUBLIC DEF_FOO) target_compile_options(VSMASM PUBLIC -DDEF_BAR) +add_library(empty STATIC empty.asm) diff --git a/Tests/VSMASM/empty.asm b/Tests/VSMASM/empty.asm new file mode 100644 index 000000000..a6a9baf65 --- /dev/null +++ b/Tests/VSMASM/empty.asm @@ -0,0 +1 @@ +end diff --git a/Tests/VSMidl/CMakeLists.txt b/Tests/VSMidl/CMakeLists.txt index f24640f9e..d8c03fa39 100644 --- a/Tests/VSMidl/CMakeLists.txt +++ b/Tests/VSMidl/CMakeLists.txt @@ -12,7 +12,7 @@ endif() message(STATUS "CMAKE_BUILDNAME='${CMAKE_BUILDNAME}'") message(STATUS "THIS_TESTNAME='${THIS_TESTNAME}'") -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(${THIS_TESTNAME}) include(ExternalProject) diff --git a/Tests/VSMidl/src/CMakeLists.txt b/Tests/VSMidl/src/CMakeLists.txt index d8fd93435..8648ab2b9 100644 --- a/Tests/VSMidl/src/CMakeLists.txt +++ b/Tests/VSMidl/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(VSMidl) include_directories("${CMAKE_CURRENT_BINARY_DIR}/\$(IntDir)") diff --git a/Tests/VSNASM/CMakeLists.txt b/Tests/VSNASM/CMakeLists.txt index 6f824258b..c6b554be2 100644 --- a/Tests/VSNASM/CMakeLists.txt +++ b/Tests/VSNASM/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(VSNASM C ASM_NASM) if(CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/Tests/VSProjectInSubdir/CMakeLists.txt b/Tests/VSProjectInSubdir/CMakeLists.txt index c164a9798..2f5d89b9f 100644 --- a/Tests/VSProjectInSubdir/CMakeLists.txt +++ b/Tests/VSProjectInSubdir/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 2.8.10) +cmake_minimum_required(VERSION 3.10) project(VSProjectInSubdir NONE) add_subdirectory(subdir) diff --git a/Tests/VSResource/CMakeLists.txt b/Tests/VSResource/CMakeLists.txt index fb47c7e13..0e106a47f 100644 --- a/Tests/VSResource/CMakeLists.txt +++ b/Tests/VSResource/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.4) +cmake_minimum_required(VERSION 3.10) project(VSResource) string(REPLACE "/INCREMENTAL:YES" "" diff --git a/Tests/VSResourceNinjaForceRSP/CMakeLists.txt b/Tests/VSResourceNinjaForceRSP/CMakeLists.txt index 29ba0cee5..d9d0bd011 100644 --- a/Tests/VSResourceNinjaForceRSP/CMakeLists.txt +++ b/Tests/VSResourceNinjaForceRSP/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.4) +cmake_minimum_required(VERSION 3.10) project(VSResourceNinjaForceRSP) set(CMAKE_NINJA_FORCE_RESPONSE_FILE TRUE) diff --git a/Tests/VSWinStorePhone/CMakeLists.txt b/Tests/VSWinStorePhone/CMakeLists.txt index 2cb80faab..3c2fe22fe 100644 --- a/Tests/VSWinStorePhone/CMakeLists.txt +++ b/Tests/VSWinStorePhone/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(VSWinStorePhone) if(MSVC_VERSION GREATER 1899) set(COMPILER_VERSION "14") diff --git a/Tests/VSWindowsFormsResx/CMakeLists.txt b/Tests/VSWindowsFormsResx/CMakeLists.txt index b9b216351..6514a341f 100644 --- a/Tests/VSWindowsFormsResx/CMakeLists.txt +++ b/Tests/VSWindowsFormsResx/CMakeLists.txt @@ -4,7 +4,7 @@ # Code modifications and example by John Farrier, john.farrier@helleboreconsulting.com # -cmake_minimum_required(VERSION 2.8.10) +cmake_minimum_required(VERSION 3.10) # Project Name project(VSWindowsFormsResx CXX) diff --git a/Tests/VSXaml/CMakeLists.txt b/Tests/VSXaml/CMakeLists.txt index f384c8213..9f34a65f9 100644 --- a/Tests/VSXaml/CMakeLists.txt +++ b/Tests/VSXaml/CMakeLists.txt @@ -1,4 +1,5 @@ -cmake_minimum_required(VERSION 3.2) +# FIXME(#26248): This test is not executed anymore! +cmake_minimum_required(VERSION 3.10) project(VSXaml) set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/Tests/Visibility/CMakeLists.txt b/Tests/Visibility/CMakeLists.txt index 9498ca65c..66f781cd5 100644 --- a/Tests/Visibility/CMakeLists.txt +++ b/Tests/Visibility/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) cmake_policy(SET CMP0063 NEW) project(Visibility) diff --git a/Tests/Wrapping/CMakeLists.txt b/Tests/Wrapping/CMakeLists.txt index 7d9a4c67f..69614f4a6 100644 --- a/Tests/Wrapping/CMakeLists.txt +++ b/Tests/Wrapping/CMakeLists.txt @@ -1,7 +1,7 @@ # # Wrapping # -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (Wrapping) # Disable cleaning of custom command outputs to preserve the hacks @@ -104,5 +104,7 @@ configure_file( ${Wrapping_SOURCE_DIR}/dummy ${Wrapping_BINARY_DIR}/gl.h COPYONLY) +block(SCOPE_FOR POLICIES) +cmake_policy(VERSION 2.8.12) # old enough to not set CMP0030 use_mangled_mesa (${Wrapping_BINARY_DIR} ${Wrapping_BINARY_DIR}/mangled_mesa) - +endblock() diff --git a/Tests/X11/CMakeLists.txt b/Tests/X11/CMakeLists.txt index b190de216..80707d5d5 100644 --- a/Tests/X11/CMakeLists.txt +++ b/Tests/X11/CMakeLists.txt @@ -1,5 +1,5 @@ # a simple C only test case -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.10) project (UseX11 CXX C) include (${CMAKE_ROOT}/Modules/FindX11.cmake) diff --git a/Tests/XCTest/CMakeLists.txt b/Tests/XCTest/CMakeLists.txt index a07065123..e02810807 100644 --- a/Tests/XCTest/CMakeLists.txt +++ b/Tests/XCTest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.10) project(XCTest C) enable_testing() diff --git a/Tests/iOSNavApp/CMakeLists.txt b/Tests/iOSNavApp/CMakeLists.txt index 1fc33e01a..22f9d5da0 100644 --- a/Tests/iOSNavApp/CMakeLists.txt +++ b/Tests/iOSNavApp/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.5) +cmake_minimum_required(VERSION 3.10) project(NavApp3) set(CMAKE_OSX_SYSROOT iphoneos4.3) diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index 8bf591b91..111bd6f15 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeDeveloperReference_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.29 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Scripts/update-cppdap.bash b/Utilities/Scripts/update-cppdap.bash index b5df7a5d9..ffe10d9e0 100755 --- a/Utilities/Scripts/update-cppdap.bash +++ b/Utilities/Scripts/update-cppdap.bash @@ -8,7 +8,7 @@ readonly name="cppdap" readonly ownership="cppdap Upstream " readonly subtree="Utilities/cmcppdap" readonly repo="https://github.com/google/cppdap.git" -readonly tag="cc2f2058846bb29e18fdadf455d5f5af71b2554f" # 2023-08-17 +readonly tag="c69444ed76f7468b232ac4f989cb8f2bdc100185" # 2024-08-02 readonly shortlog=false readonly paths=" LICENSE diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 3f550462e..fb2a12a03 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream " readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-8_8_0" +readonly tag="curl-8_10_1" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/Scripts/update-expat.bash b/Utilities/Scripts/update-expat.bash index a061adca7..e34b23046 100755 --- a/Utilities/Scripts/update-expat.bash +++ b/Utilities/Scripts/update-expat.bash @@ -8,7 +8,7 @@ readonly name="expat" readonly ownership="Expat Upstream " readonly subtree="Utilities/cmexpat" readonly repo="https://github.com/libexpat/libexpat.git" -readonly tag="R_2_4_6" +readonly tag="R_2_6_2" readonly shortlog=false readonly paths=" expat/lib/asciitab.h diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash index 724303eed..19c2d3211 100755 --- a/Utilities/Scripts/update-libarchive.bash +++ b/Utilities/Scripts/update-libarchive.bash @@ -8,7 +8,7 @@ readonly name="LibArchive" readonly ownership="LibArchive Upstream " readonly subtree="Utilities/cmlibarchive" readonly repo="https://github.com/libarchive/libarchive.git" -readonly tag="v3.7.2" +readonly tag="v3.7.5" readonly shortlog=false readonly paths=" CMakeLists.txt diff --git a/Utilities/Scripts/update-llpkgc.bash b/Utilities/Scripts/update-llpkgc.bash new file mode 100755 index 000000000..b1dd01703 --- /dev/null +++ b/Utilities/Scripts/update-llpkgc.bash @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Note: llpkgc *generates* a parser, thus this script requires npm be available + +set -e +set -x +shopt -s dotglob + +readonly name="llpkgc" +readonly ownership="llpkgc upstream " +readonly subtree="Utilities/cmllpkgc" +readonly repo="https://gitlab.kitware.com/utils/llpkgc.git" +readonly tag="7958a1de42b9eec04676d547f6fcf5daa425fbcc" +readonly shortlog=false +readonly paths=" + bin + src + *.json +" + +extract_source() { + git_archive + npm install + npm run build + mv build/llpkgc/* "${extractdir}/${name}-reduced" + + pushd "${extractdir}/${name}-reduced" + rm CMakeLists.txt *.json + rm -rf src bin + echo "* -whitespace" > .gitattributes + popd +} + +. "${BASH_SOURCE%/*}/update-third-party.bash" diff --git a/Utilities/Scripts/update-third-party.bash b/Utilities/Scripts/update-third-party.bash index bfe6828de..cf76b17c9 100644 --- a/Utilities/Scripts/update-third-party.bash +++ b/Utilities/Scripts/update-third-party.bash @@ -25,7 +25,7 @@ # ownership # A git author name/email for the commits. # subtree -# The location of the thirdparty package within the main source +# The location of the third-party package within the main source # tree. # repo # The git repository to use as upstream. diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index 746c87220..ed7b63196 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeHelp_STANDALONE 1) - cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR) + cmake_minimum_required(VERSION 3.13...3.29 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index 09a7d5ac0..dca679441 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in @@ -103,3 +103,5 @@ linkcheck_allowed_redirects = { r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313', r'https://www\.sphinx-doc\.org': r'https://www\.sphinx-doc\.org/en/master/', } + +linkcheck_report_timeouts_as_broken = False diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake index 311d58e47..fa3d8b67d 100644 --- a/Utilities/cmThirdPartyChecks.cmake +++ b/Utilities/cmThirdPartyChecks.cmake @@ -55,6 +55,7 @@ if(WIN32) set(HAVE_EILSEQ 1) set(HAVE_ERR_H 0) set(HAVE_ERRNO_H 1) + set(HAVE_EVENTFD 0) set(HAVE_EXT2FS_EXT2_FS_H 0) set(HAVE_FCHDIR 0) set(HAVE_FCHFLAGS 0) @@ -211,8 +212,10 @@ if(WIN32) set(HAVE_STRUCT_VFSCONF 0) set(HAVE_STRUCT_XVFSCONF 0) set(HAVE_SYMLINK 0) + set(HAVE_SYSCONF 0) set(HAVE_SYS_ACL_H 0) set(HAVE_SYSCALL_GETRANDOM 0) + set(HAVE_SYS_EVENTFD_H 0) set(HAVE_SYS_EXTATTR_H 0) set(HAVE_SYS_FILIO_H 0) set(HAVE_SYS_IOCTL_H 0) diff --git a/Utilities/cmcppdap/include/dap/network.h b/Utilities/cmcppdap/include/dap/network.h index dd3e00624..03b940925 100644 --- a/Utilities/cmcppdap/include/dap/network.h +++ b/Utilities/cmcppdap/include/dap/network.h @@ -45,13 +45,21 @@ class Server { // create() constructs and returns a new Server. static std::unique_ptr create(); - // start() begins listening for connections on the given port. + // start() begins listening for connections on localhost and the given port. // callback will be called for each connection. // onError will be called for any connection errors. virtual bool start(int port, const OnConnect& callback, const OnError& onError = ignoreErrors) = 0; + // start() begins listening for connections on the given specific address and port. + // callback will be called for each connection. + // onError will be called for any connection errors. + virtual bool start(const char* address, + int port, + const OnConnect& callback, + const OnError& onError = ignoreErrors) = 0; + // stop() stops listening for connections. // stop() is implicitly called on destruction. virtual void stop() = 0; diff --git a/Utilities/cmcppdap/include/dap/protocol.h b/Utilities/cmcppdap/include/dap/protocol.h index e4c479e51..96a18c970 100644 --- a/Utilities/cmcppdap/include/dap/protocol.h +++ b/Utilities/cmcppdap/include/dap/protocol.h @@ -15,7 +15,7 @@ // Generated with protocol_gen.go -- do not edit this file. // go run scripts/protocol_gen/protocol_gen.go // -// DAP version 1.59.0 +// DAP version 1.65.0 #ifndef dap_protocol_h #define dap_protocol_h @@ -141,6 +141,18 @@ struct Breakpoint { // The offset from the instruction reference. // This can be negative. optional offset; + // A machine-readable explanation of why a breakpoint may not be verified. If + // a breakpoint is verified or a specific reason is not known, the adapter + // should omit this property. Possible values include: + // + // - `pending`: Indicates a breakpoint might be verified in the future, but + // the adapter cannot verify it in the current state. + // - `failed`: Indicates a breakpoint was not able to be verified, and the + // adapter does not believe it can be verified without intervention. + // + // Must be one of the following enumeration values: + // 'pending', 'failed' + optional reason; // The source where the breakpoint is located. optional source; // If true, the breakpoint could be set (but not necessarily at the desired @@ -214,7 +226,7 @@ struct BreakpointLocationsRequest : public Request { // line is specified, the request returns all possible locations in that line. integer line; // The source location of the breakpoints; either `source.path` or - // `source.reference` must be specified. + // `source.sourceReference` must be specified. Source source; }; @@ -229,18 +241,19 @@ DAP_DECLARE_STRUCT_TYPEINFO(CancelResponse); // The `cancel` request is used by the client in two situations: // - to indicate that it is no longer interested in the result produced by a // specific request issued earlier -// - to cancel a progress sequence. Clients should only call this request if the -// corresponding capability `supportsCancelRequest` is true. This request has a -// hint characteristic: a debug adapter can only be expected to make a 'best -// effort' in honoring this request but there are no guarantees. The `cancel` -// request may return an error if it could not cancel an operation but a client -// should refrain from presenting this error to end users. The request that got -// cancelled still needs to send a response back. This can either be a normal -// result (`success` attribute true) or an error response (`success` attribute -// false and the `message` set to `cancelled`). Returning partial results from a -// cancelled request is possible but please note that a client has no generic -// way for detecting that a response is partial or not. The progress that got -// cancelled still needs to send a `progressEnd` event back. +// - to cancel a progress sequence. +// Clients should only call this request if the corresponding capability +// `supportsCancelRequest` is true. This request has a hint characteristic: a +// debug adapter can only be expected to make a 'best effort' in honoring this +// request but there are no guarantees. The `cancel` request may return an error +// if it could not cancel an operation but a client should refrain from +// presenting this error to end users. The request that got cancelled still +// needs to send a response back. This can either be a normal result (`success` +// attribute true) or an error response (`success` attribute false and the +// `message` set to `cancelled`). Returning partial results from a cancelled +// request is possible but please note that a client has no generic way for +// detecting that a response is partial or not. The progress that got cancelled +// still needs to send a `progressEnd` event back. // A client should not assume that progress just got cancelled after sending // the `cancel` request. struct CancelRequest : public Request { @@ -280,6 +293,28 @@ struct ColumnDescriptor { DAP_DECLARE_STRUCT_TYPEINFO(ColumnDescriptor); +// Describes one or more type of breakpoint a `BreakpointMode` applies to. This +// is a non-exhaustive enumeration and may expand as future breakpoint types are +// added. +using BreakpointModeApplicability = string; + +// A `BreakpointMode` is provided as a option when setting breakpoints on +// sources or instructions. +struct BreakpointMode { + // Describes one or more type of breakpoint this mode applies to. + array appliesTo; + // A help text providing additional information about the breakpoint mode. + // This string is typically shown as a hover and can be translated. + optional description; + // The name of the breakpoint mode. This is shown in the UI. + string label; + // The internal ID of the mode. This value is passed to the `setBreakpoints` + // request. + string mode; +}; + +DAP_DECLARE_STRUCT_TYPEINFO(BreakpointMode); + // An `ExceptionBreakpointsFilter` is shown in the UI as an filter option for // configuring how exceptions are dealt with. struct ExceptionBreakpointsFilter { @@ -308,6 +343,13 @@ DAP_DECLARE_STRUCT_TYPEINFO(ExceptionBreakpointsFilter); struct Capabilities { // The set of additional module information exposed by the debug adapter. optional> additionalModuleColumns; + // Modes of breakpoints supported by the debug adapter, such as 'hardware' or + // 'software'. If present, the client may allow the user to select a mode and + // include it in its `setBreakpoints` request. + // + // Clients may present the first applicable mode in this array as the + // 'default' mode in gestures that set breakpoints. + optional> breakpointModes; // The set of characters that should trigger completion in a REPL. If not // specified, the UI should assume the `.` character. optional> completionTriggerCharacters; @@ -579,7 +621,11 @@ struct DataBreakpointInfoResponse : public Response { optional canPersist; // An identifier for the data on which a data breakpoint can be registered // with the `setDataBreakpoints` request or null if no data breakpoint is - // available. + // available. If a `variablesReference` or `frameId` is passed, the `dataId` + // is valid in the current suspended state, otherwise it's valid indefinitely. + // See 'Lifetime of Object References' in the Overview section for details. + // Breakpoints set using the `dataId` in the `setDataBreakpoints` request may + // outlive the lifetime of the associated `dataId`. variant dataId; // UI string that describes on what data the breakpoint is set on or why a // data breakpoint is not available. @@ -597,6 +643,9 @@ struct DataBreakpointInfoRequest : public Request { // If not specified, the expression is evaluated in the global scope. When // `variablesReference` is specified, this property has no effect. optional frameId; + // The mode of the desired breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; // The name of the variable's child to obtain data breakpoint information for. // If `variablesReference` isn't specified, this can be an expression. string name; @@ -634,6 +683,15 @@ struct DisassembledInstruction { // but can be omitted afterwards if this instruction maps to the same source // file as the previous instruction. optional location; + // A hint for how to present the instruction in the UI. + // + // A value of `invalid` may be used to indicate this instruction is 'filler' + // and cannot be reached by the program. For example, unreadable memory + // addresses may be presented is 'invalid.' + // + // Must be one of the following enumeration values: + // 'normal', 'invalid' + optional presentationHint; // Name of the symbol that corresponds with the location of this instruction, // if any. optional symbol; @@ -785,9 +843,8 @@ struct EvaluateResponse : public Response { optional indexedVariables; // A memory reference to a location appropriate for this result. // For pointer type eval results, this is generally a reference to the memory - // address contained in the pointer. This attribute should be returned by a - // debug adapter if corresponding capability `supportsMemoryReferences` is - // true. + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. optional memoryReference; // The number of named child variables. // The client can use this information to present the variables in a paged UI @@ -979,6 +1036,13 @@ DAP_DECLARE_STRUCT_TYPEINFO(GotoTargetsRequest); struct InitializeResponse : public Response { // The set of additional module information exposed by the debug adapter. optional> additionalModuleColumns; + // Modes of breakpoints supported by the debug adapter, such as 'hardware' or + // 'software'. If present, the client may allow the user to select a mode and + // include it in its `setBreakpoints` request. + // + // Clients may present the first applicable mode in this array as the + // 'default' mode in gestures that set breakpoints. + optional> breakpointModes; // The set of characters that should trigger completion in a REPL. If not // specified, the UI should assume the `.` character. optional> completionTriggerCharacters; @@ -1774,6 +1838,9 @@ struct SourceBreakpoint { // `hitCondition` or `condition` is specified, then the message should only be // logged if those conditions are met. optional logMessage; + // The mode of this breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; }; DAP_DECLARE_STRUCT_TYPEINFO(SourceBreakpoint); @@ -1846,13 +1913,13 @@ DAP_DECLARE_STRUCT_TYPEINFO(SetDataBreakpointsRequest); // the returned array must start with `filters` information first, followed by // `filterOptions` information. The `verified` property of a `Breakpoint` object // signals whether the exception breakpoint or filter could be successfully -// created and whether the condition or hit count expressions are valid. In case -// of an error the `message` property explains the problem. The `id` property -// can be used to introduce a unique ID for the exception breakpoint or filter -// so that it can be updated subsequently by sending breakpoint events. For -// backward compatibility both the `breakpoints` array and the enclosing `body` -// are optional. If these elements are missing a client is not able to show -// problems for individual exception breakpoints or filters. +// created and whether the condition is valid. In case of an error the `message` +// property explains the problem. The `id` property can be used to introduce a +// unique ID for the exception breakpoint or filter so that it can be updated +// subsequently by sending breakpoint events. For backward compatibility both +// the `breakpoints` array and the enclosing `body` are optional. If these +// elements are missing a client is not able to show problems for individual +// exception breakpoints or filters. struct SetExceptionBreakpointsResponse : public Response { // Information about the exception breakpoints or filters. // The breakpoints returned are in the same order as the elements of the @@ -1901,15 +1968,20 @@ struct ExceptionFilterOptions { // ID of an exception filter returned by the `exceptionBreakpointFilters` // capability. string filterId; + // The mode of this exception breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; }; DAP_DECLARE_STRUCT_TYPEINFO(ExceptionFilterOptions); -// The request configures the debugger's response to thrown exceptions. -// If an exception is configured to break, a `stopped` event is fired (with -// reason `exception`). Clients should only call this request if the -// corresponding capability `exceptionBreakpointFilters` returns one or more -// filters. +// The request configures the debugger's response to thrown exceptions. Each of +// the `filters`, `filterOptions`, and `exceptionOptions` in the request are +// independent configurations to a debug adapter indicating a kind of exception +// to catch. An exception thrown in a program should result in a `stopped` event +// from the debug adapter (with reason `exception`) if any of the configured +// filters match. Clients should only call this request if the corresponding +// capability `exceptionBreakpointFilters` returns one or more filters. struct SetExceptionBreakpointsRequest : public Request { using Response = SetExceptionBreakpointsResponse; // Configuration options for selected exceptions. @@ -1937,6 +2009,11 @@ struct SetExpressionResponse : public Response { // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional indexedVariables; + // A memory reference to a location appropriate for this result. + // For pointer type eval results, this is generally a reference to the memory + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. + optional memoryReference; // The number of named child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to @@ -2047,7 +2124,10 @@ struct InstructionBreakpoint { // `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or // `Breakpoint`. string instructionReference; - // The offset from the instruction reference. + // The mode of this breakpoint. If defined, this must be one of the + // `breakpointModes` the debug adapter advertised in its `Capabilities`. + optional mode; + // The offset from the instruction reference in bytes. // This can be negative. optional offset; }; @@ -2075,6 +2155,11 @@ struct SetVariableResponse : public Response { // and fetch them in chunks. The value should be less than or equal to // 2147483647 (2^31-1). optional indexedVariables; + // A memory reference to a location appropriate for this result. + // For pointer type eval results, this is generally a reference to the memory + // address contained in the pointer. This attribute may be returned by a debug + // adapter if corresponding capability `supportsMemoryReferences` is true. + optional memoryReference; // The number of named child variables. // The client can use this information to present the variables in a paged UI // and fetch them in chunks. The value should be less than or equal to @@ -2568,9 +2653,12 @@ struct Variable { // The client can use this information to present the children in a paged UI // and fetch them in chunks. optional indexedVariables; - // The memory reference for the variable if the variable represents executable - // code, such as a function pointer. This attribute is only required if the - // corresponding capability `supportsMemoryReferences` is true. + // A memory reference associated with this variable. + // For pointer type variables, this is generally a reference to the memory + // address contained in the pointer. For executable data, this reference may + // later be used in a `disassemble` request. This attribute may be returned by + // a debug adapter if corresponding capability `supportsMemoryReferences` is + // true. optional memoryReference; // The variable's name. string name; @@ -2616,7 +2704,8 @@ DAP_DECLARE_STRUCT_TYPEINFO(VariablesResponse); struct VariablesRequest : public Request { using Response = VariablesResponse; // The number of variables to return. If count is missing or 0, all variables - // are returned. + // are returned. The attribute is only honored by a debug adapter if the + // corresponding capability `supportsVariablePaging` is true. optional count; // Filter to limit the child variables to either named or indexed. If omitted, // both types are fetched. @@ -2629,6 +2718,8 @@ struct VariablesRequest : public Request { // capability `supportsValueFormattingOptions` is true. optional format; // The index of the first variable to return; if omitted children start at 0. + // The attribute is only honored by a debug adapter if the corresponding + // capability `supportsVariablePaging` is true. optional start; // The variable for which to retrieve its children. The `variablesReference` // must have been obtained in the current suspended state. See 'Lifetime of diff --git a/Utilities/cmcppdap/include/dap/typeof.h b/Utilities/cmcppdap/include/dap/typeof.h index 803bb8dcc..43c9289c9 100644 --- a/Utilities/cmcppdap/include/dap/typeof.h +++ b/Utilities/cmcppdap/include/dap/typeof.h @@ -164,7 +164,7 @@ M member_type(M T::*); bool TypeOf::deserializeFields(const Deserializer* fd, void* obj) { \ using StructTy = STRUCT; \ (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ - for (auto field : std::initializer_list{__VA_ARGS__}) { \ + for (auto& field : std::initializer_list{__VA_ARGS__}) { \ if (!fd->field(field.name, [&](Deserializer* d) { \ auto ptr = reinterpret_cast(obj) + field.offset; \ return field.type->deserialize(d, ptr); \ @@ -177,7 +177,7 @@ M member_type(M T::*); bool TypeOf::serializeFields(FieldSerializer* fs, const void* obj) {\ using StructTy = STRUCT; \ (void)sizeof(StructTy); /* avoid unused 'using' warning */ \ - for (auto field : std::initializer_list{__VA_ARGS__}) { \ + for (auto& field : std::initializer_list{__VA_ARGS__}) { \ if (!fs->field(field.name, [&](Serializer* s) { \ auto ptr = reinterpret_cast(obj) + field.offset; \ return field.type->serialize(s, ptr); \ diff --git a/Utilities/cmcppdap/src/network.cpp b/Utilities/cmcppdap/src/network.cpp index 613c2343f..7d1da7a1c 100644 --- a/Utilities/cmcppdap/src/network.cpp +++ b/Utilities/cmcppdap/src/network.cpp @@ -32,10 +32,17 @@ class Impl : public dap::net::Server { bool start(int port, const OnConnect& onConnect, const OnError& onError) override { + return start("localhost", port, onConnect, onError); + } + + bool start(const char* address, + int port, + const OnConnect& onConnect, + const OnError& onError) override { std::unique_lock lock(mutex); stopWithLock(); socket = std::unique_ptr( - new dap::Socket("localhost", std::to_string(port).c_str())); + new dap::Socket(address, std::to_string(port).c_str())); if (!socket->isOpen()) { onError("Failed to open socket"); diff --git a/Utilities/cmcppdap/src/protocol_events.cpp b/Utilities/cmcppdap/src/protocol_events.cpp index 9deb85f34..0cd6b9b88 100644 --- a/Utilities/cmcppdap/src/protocol_events.cpp +++ b/Utilities/cmcppdap/src/protocol_events.cpp @@ -15,7 +15,7 @@ // Generated with protocol_gen.go -- do not edit this file. // go run scripts/protocol_gen/protocol_gen.go // -// DAP version 1.59.0 +// DAP version 1.65.0 #include "dap/protocol.h" diff --git a/Utilities/cmcppdap/src/protocol_requests.cpp b/Utilities/cmcppdap/src/protocol_requests.cpp index a3b33ec2d..dd5d3e367 100644 --- a/Utilities/cmcppdap/src/protocol_requests.cpp +++ b/Utilities/cmcppdap/src/protocol_requests.cpp @@ -15,7 +15,7 @@ // Generated with protocol_gen.go -- do not edit this file. // go run scripts/protocol_gen/protocol_gen.go // -// DAP version 1.59.0 +// DAP version 1.65.0 #include "dap/protocol.h" @@ -55,6 +55,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(ContinueRequest, DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpointInfoRequest, "dataBreakpointInfo", DAP_FIELD(frameId, "frameId"), + DAP_FIELD(mode, "mode"), DAP_FIELD(name, "name"), DAP_FIELD(variablesReference, "variablesReference")); diff --git a/Utilities/cmcppdap/src/protocol_response.cpp b/Utilities/cmcppdap/src/protocol_response.cpp index bab8ebbb2..1810c8aa7 100644 --- a/Utilities/cmcppdap/src/protocol_response.cpp +++ b/Utilities/cmcppdap/src/protocol_response.cpp @@ -15,7 +15,7 @@ // Generated with protocol_gen.go -- do not edit this file. // go run scripts/protocol_gen/protocol_gen.go // -// DAP version 1.59.0 +// DAP version 1.65.0 #include "dap/protocol.h" @@ -83,6 +83,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO( InitializeResponse, "", DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), + DAP_FIELD(breakpointModes, "breakpointModes"), DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), @@ -177,6 +178,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExceptionBreakpointsResponse, DAP_IMPLEMENT_STRUCT_TYPEINFO(SetExpressionResponse, "", DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(type, "type"), @@ -195,6 +197,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(SetInstructionBreakpointsResponse, DAP_IMPLEMENT_STRUCT_TYPEINFO(SetVariableResponse, "", DAP_FIELD(indexedVariables, "indexedVariables"), + DAP_FIELD(memoryReference, "memoryReference"), DAP_FIELD(namedVariables, "namedVariables"), DAP_FIELD(type, "type"), DAP_FIELD(value, "value"), diff --git a/Utilities/cmcppdap/src/protocol_types.cpp b/Utilities/cmcppdap/src/protocol_types.cpp index d9a9e36f7..2ae91000b 100644 --- a/Utilities/cmcppdap/src/protocol_types.cpp +++ b/Utilities/cmcppdap/src/protocol_types.cpp @@ -15,7 +15,7 @@ // Generated with protocol_gen.go -- do not edit this file. // go run scripts/protocol_gen/protocol_gen.go // -// DAP version 1.59.0 +// DAP version 1.65.0 #include "dap/protocol.h" @@ -48,6 +48,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(Breakpoint, DAP_FIELD(line, "line"), DAP_FIELD(message, "message"), DAP_FIELD(offset, "offset"), + DAP_FIELD(reason, "reason"), DAP_FIELD(source, "source"), DAP_FIELD(verified, "verified")); @@ -66,6 +67,13 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(ColumnDescriptor, DAP_FIELD(type, "type"), DAP_FIELD(width, "width")); +DAP_IMPLEMENT_STRUCT_TYPEINFO(BreakpointMode, + "", + DAP_FIELD(appliesTo, "appliesTo"), + DAP_FIELD(description, "description"), + DAP_FIELD(label, "label"), + DAP_FIELD(mode, "mode")); + DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionBreakpointsFilter, "", DAP_FIELD(conditionDescription, @@ -81,6 +89,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO( Capabilities, "", DAP_FIELD(additionalModuleColumns, "additionalModuleColumns"), + DAP_FIELD(breakpointModes, "breakpointModes"), DAP_FIELD(completionTriggerCharacters, "completionTriggerCharacters"), DAP_FIELD(exceptionBreakpointFilters, "exceptionBreakpointFilters"), DAP_FIELD(supportSuspendDebuggee, "supportSuspendDebuggee"), @@ -148,6 +157,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(DisassembledInstruction, DAP_FIELD(instructionBytes, "instructionBytes"), DAP_FIELD(line, "line"), DAP_FIELD(location, "location"), + DAP_FIELD(presentationHint, "presentationHint"), DAP_FIELD(symbol, "symbol")); DAP_IMPLEMENT_STRUCT_TYPEINFO(Message, @@ -223,7 +233,8 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(SourceBreakpoint, DAP_FIELD(condition, "condition"), DAP_FIELD(hitCondition, "hitCondition"), DAP_FIELD(line, "line"), - DAP_FIELD(logMessage, "logMessage")); + DAP_FIELD(logMessage, "logMessage"), + DAP_FIELD(mode, "mode")); DAP_IMPLEMENT_STRUCT_TYPEINFO(DataBreakpoint, "", @@ -245,7 +256,8 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionOptions, DAP_IMPLEMENT_STRUCT_TYPEINFO(ExceptionFilterOptions, "", DAP_FIELD(condition, "condition"), - DAP_FIELD(filterId, "filterId")); + DAP_FIELD(filterId, "filterId"), + DAP_FIELD(mode, "mode")); DAP_IMPLEMENT_STRUCT_TYPEINFO(FunctionBreakpoint, "", @@ -259,6 +271,7 @@ DAP_IMPLEMENT_STRUCT_TYPEINFO(InstructionBreakpoint, DAP_FIELD(hitCondition, "hitCondition"), DAP_FIELD(instructionReference, "instructionReference"), + DAP_FIELD(mode, "mode"), DAP_FIELD(offset, "offset")); DAP_IMPLEMENT_STRUCT_TYPEINFO(StackFrame, diff --git a/Utilities/cmcppdap/src/session.cpp b/Utilities/cmcppdap/src/session.cpp index 5bf22c9cc..ffc77751f 100644 --- a/Utilities/cmcppdap/src/session.cpp +++ b/Utilities/cmcppdap/src/session.cpp @@ -138,7 +138,7 @@ class Impl : public dap::Session { return send(s.dump()); } - ~Impl() { + ~Impl() override { inbox.close(); reader.close(); writer.close(); diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake index 8289b4924..00b7b3c10 100644 --- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake +++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake @@ -23,62 +23,58 @@ ########################################################################### include(CheckCSourceCompiles) -option(CURL_HIDDEN_SYMBOLS "Set to ON to hide libcurl internal symbols (=hide all symbols that aren't officially external)." ON) +option(CURL_HIDDEN_SYMBOLS "Hide libcurl internal symbols (=hide all symbols that are not officially external)" ON) mark_as_advanced(CURL_HIDDEN_SYMBOLS) -if(WIN32 AND ENABLE_CURLDEBUG) - # We need to export internal debug functions (e.g. curl_dbg_*), so disable - # symbol hiding for debug builds. +if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) + # We need to export internal debug functions, + # e.g. curl_easy_perform_ev() or curl_dbg_*(), + # so disable symbol hiding for debug builds and for memory tracking. set(CURL_HIDDEN_SYMBOLS OFF) endif() if(CURL_HIDDEN_SYMBOLS) - set(SUPPORTS_SYMBOL_HIDING FALSE) + set(_supports_symbol_hiding FALSE) if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT MSVC) - set(SUPPORTS_SYMBOL_HIDING TRUE) - set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") - set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + set(_supports_symbol_hiding TRUE) + set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") + set(_cflag_symbols_hide "-fvisibility=hidden") elseif(CMAKE_COMPILER_IS_GNUCC) if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) - # note: this is considered buggy prior to 4.0 but the autotools don't care, so let's ignore that fact - set(SUPPORTS_SYMBOL_HIDING TRUE) - set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") - set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + # Note: This is considered buggy prior to 4.0 but the autotools do not care, so let us ignore that fact + set(_supports_symbol_hiding TRUE) + set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") + set(_cflag_symbols_hide "-fvisibility=hidden") endif() elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.0) - set(SUPPORTS_SYMBOL_HIDING TRUE) - set(_SYMBOL_EXTERN "__global") - set(_CFLAG_SYMBOLS_HIDE "-xldscope=hidden") + set(_supports_symbol_hiding TRUE) + set(_symbol_extern "__global") + set(_cflag_symbols_hide "-xldscope=hidden") elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0) - # note: this should probably just check for version 9.1.045 but I'm not 100% sure - # so let's do it the same way autotools do. - set(SUPPORTS_SYMBOL_HIDING TRUE) - set(_SYMBOL_EXTERN "__attribute__ ((__visibility__ (\"default\")))") - set(_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") + # Note: This should probably just check for version 9.1.045 but I am not 100% sure + # so let us do it the same way autotools do. + set(_supports_symbol_hiding TRUE) + set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") + set(_cflag_symbols_hide "-fvisibility=hidden") check_c_source_compiles("#include - int main (void) { printf(\"icc fvisibility bug test\"); return 0; }" _no_bug) + int main(void) { printf(\"icc fvisibility bug test\"); return 0; }" _no_bug) if(NOT _no_bug) - set(SUPPORTS_SYMBOL_HIDING FALSE) - set(_SYMBOL_EXTERN "") - set(_CFLAG_SYMBOLS_HIDE "") + set(_supports_symbol_hiding FALSE) + set(_symbol_extern "") + set(_cflag_symbols_hide "") endif() elseif(MSVC) - set(SUPPORTS_SYMBOL_HIDING TRUE) + set(_supports_symbol_hiding TRUE) endif() - set(HIDES_CURL_PRIVATE_SYMBOLS ${SUPPORTS_SYMBOL_HIDING}) -elseif(MSVC) - if(NOT CMAKE_VERSION VERSION_LESS 3.7) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) #present since 3.4.3 but broken - set(HIDES_CURL_PRIVATE_SYMBOLS FALSE) - else() - message(WARNING "Hiding private symbols regardless CURL_HIDDEN_SYMBOLS being disabled.") - set(HIDES_CURL_PRIVATE_SYMBOLS TRUE) - endif() + set(CURL_HIDES_PRIVATE_SYMBOLS ${_supports_symbol_hiding}) else() - set(HIDES_CURL_PRIVATE_SYMBOLS FALSE) + if(MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + endif() + set(CURL_HIDES_PRIVATE_SYMBOLS FALSE) endif() -set(CURL_CFLAG_SYMBOLS_HIDE ${_CFLAG_SYMBOLS_HIDE}) -set(CURL_EXTERN_SYMBOL ${_SYMBOL_EXTERN}) +set(CURL_CFLAG_SYMBOLS_HIDE ${_cflag_symbols_hide}) +set(CURL_EXTERN_SYMBOL ${_symbol_extern}) diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index 483b9a218..579758654 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -152,7 +152,7 @@ int main(void) { return 0; } #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. - We can't simply define LARGE_OFF_T to be 9223372036854775807, + We cannot simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) @@ -337,7 +337,7 @@ int main(void) #include #include -/* float, because a pointer can't be implicitly cast to float */ +/* Float, because a pointer cannot be implicitly cast to float */ void check(float f) {} int main(void) @@ -380,7 +380,7 @@ int main(void) #ifdef HAVE_BUILTIN_AVAILABLE int main(void) { - if(__builtin_available(macOS 10.12, *)) {} + if(__builtin_available(macOS 10.12, iOS 5.0, *)) {} return 0; } #endif diff --git a/Utilities/cmcurl/CMake/FindBearSSL.cmake b/Utilities/cmcurl/CMake/FindBearSSL.cmake index 56a064eac..dba4f5e60 100644 --- a/Utilities/cmcurl/CMake/FindBearSSL.cmake +++ b/Utilities/cmcurl/CMake/FindBearSSL.cmake @@ -21,12 +21,39 @@ # SPDX-License-Identifier: curl # ########################################################################### -find_path(BEARSSL_INCLUDE_DIRS bearssl.h) +# Find the bearssl library +# +# Input variables: +# +# BEARSSL_INCLUDE_DIR The bearssl include directory +# BEARSSL_INCLUDE_DIRS The bearssl include directory (deprecated) +# BEARSSL_LIBRARY Path to bearssl library +# +# Result variables: +# +# BEARSSL_FOUND System has bearssl +# BEARSSL_INCLUDE_DIRS The bearssl include directories +# BEARSSL_LIBRARIES The bearssl library names + +if(DEFINED BEARSSL_INCLUDE_DIRS AND NOT DEFINED BEARSSL_INCLUDE_DIR) + message(WARNING "BEARSSL_INCLUDE_DIRS is deprecated, use BEARSSL_INCLUDE_DIR instead.") + set(BEARSSL_INCLUDE_DIR "${BEARSSL_INCLUDE_DIRS}") + unset(BEARSSL_INCLUDE_DIRS) +endif() -find_library(BEARSSL_LIBRARY bearssl) +find_path(BEARSSL_INCLUDE_DIR NAMES "bearssl.h") +find_library(BEARSSL_LIBRARY NAMES "bearssl") include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(BEARSSL DEFAULT_MSG - BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) +find_package_handle_standard_args(BearSSL + REQUIRED_VARS + BEARSSL_INCLUDE_DIR + BEARSSL_LIBRARY +) + +if(BEARSSL_FOUND) + set(BEARSSL_INCLUDE_DIRS ${BEARSSL_INCLUDE_DIR}) + set(BEARSSL_LIBRARIES ${BEARSSL_LIBRARY}) +endif() -mark_as_advanced(BEARSSL_INCLUDE_DIRS BEARSSL_LIBRARY) +mark_as_advanced(BEARSSL_INCLUDE_DIR BEARSSL_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake index 11ab7f825..1150d4cc7 100644 --- a/Utilities/cmcurl/CMake/FindBrotli.cmake +++ b/Utilities/cmcurl/CMake/FindBrotli.cmake @@ -21,23 +21,60 @@ # SPDX-License-Identifier: curl # ########################################################################### -include(FindPackageHandleStandardArgs) +# Find the brotli library +# +# Input variables: +# +# BROTLI_INCLUDE_DIR The brotli include directory +# BROTLICOMMON_LIBRARY Path to brotlicommon library +# BROTLIDEC_LIBRARY Path to brotlidec library +# +# Result variables: +# +# BROTLI_FOUND System has brotli +# BROTLI_INCLUDE_DIRS The brotli include directories +# BROTLI_LIBRARIES The brotli library names +# BROTLI_VERSION Version of brotli + +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_BROTLI "libbrotlidec") +endif() -find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") +find_path(BROTLI_INCLUDE_DIR "brotli/decode.h" + HINTS + ${PC_BROTLI_INCLUDEDIR} + ${PC_BROTLI_INCLUDE_DIRS} +) + +find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon" + HINTS + ${PC_BROTLI_LIBDIR} + ${PC_BROTLI_LIBRARY_DIRS} +) +find_library(BROTLIDEC_LIBRARY NAMES "brotlidec" + HINTS + ${PC_BROTLI_LIBDIR} + ${PC_BROTLI_LIBRARY_DIRS} +) -find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon) -find_library(BROTLIDEC_LIBRARY NAMES brotlidec) +if(PC_BROTLI_VERSION) + set(BROTLI_VERSION ${PC_BROTLI_VERSION}) +endif() +include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Brotli - FOUND_VAR - BROTLI_FOUND - REQUIRED_VARS - BROTLIDEC_LIBRARY - BROTLICOMMON_LIBRARY - BROTLI_INCLUDE_DIR - FAIL_MESSAGE - "Could NOT find Brotli" + REQUIRED_VARS + BROTLI_INCLUDE_DIR + BROTLIDEC_LIBRARY + BROTLICOMMON_LIBRARY + VERSION_VAR + BROTLI_VERSION ) -set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) -set(BROTLI_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIDEC_LIBRARY}) +if(BROTLI_FOUND) + set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) + set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) +endif() + +mark_as_advanced(BROTLI_INCLUDE_DIR BROTLIDEC_LIBRARY BROTLICOMMON_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindCARES.cmake b/Utilities/cmcurl/CMake/FindCARES.cmake deleted file mode 100644 index fa7589118..000000000 --- a/Utilities/cmcurl/CMake/FindCARES.cmake +++ /dev/null @@ -1,47 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, , 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 -# -########################################################################### -# - Find c-ares -# Find the c-ares includes and library -# This module defines -# CARES_INCLUDE_DIR, where to find ares.h, etc. -# CARES_LIBRARIES, the libraries needed to use c-ares. -# CARES_FOUND, If false, do not try to use c-ares. -# also defined, but not for general use are -# CARES_LIBRARY, where to find the c-ares library. - -find_path(CARES_INCLUDE_DIR ares.h) - -set(CARES_NAMES ${CARES_NAMES} cares) -find_library(CARES_LIBRARY - NAMES ${CARES_NAMES} - ) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CARES - REQUIRED_VARS CARES_LIBRARY CARES_INCLUDE_DIR) - -mark_as_advanced( - CARES_LIBRARY - CARES_INCLUDE_DIR - ) diff --git a/Utilities/cmcurl/CMake/FindCares.cmake b/Utilities/cmcurl/CMake/FindCares.cmake new file mode 100644 index 000000000..e7b821afc --- /dev/null +++ b/Utilities/cmcurl/CMake/FindCares.cmake @@ -0,0 +1,80 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the c-ares library +# +# Input variables: +# +# CARES_INCLUDE_DIR The c-ares include directory +# CARES_LIBRARY Path to c-ares library +# +# Result variables: +# +# CARES_FOUND System has c-ares +# CARES_INCLUDE_DIRS The c-ares include directories +# CARES_LIBRARIES The c-ares library names +# CARES_VERSION Version of c-ares + +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_CARES "libcares") +endif() + +find_path(CARES_INCLUDE_DIR NAMES "ares.h" + HINTS + ${PC_CARES_INCLUDEDIR} + ${PC_CARES_INCLUDE_DIRS} +) + +find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares" + HINTS + ${PC_CARES_LIBDIR} + ${PC_CARES_LIBRARY_DIRS} +) + +if(PC_CARES_VERSION) + set(CARES_VERSION ${PC_CARES_VERSION}) +elseif(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") + set(_version_regex "#[\t ]*define[\t ]+ARES_VERSION_STR[\t ]+\"([^\"]*)\"") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(CARES_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Cares + REQUIRED_VARS + CARES_INCLUDE_DIR + CARES_LIBRARY + VERSION_VAR + CARES_VERSION +) + +if(CARES_FOUND) + set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) + set(CARES_LIBRARIES ${CARES_LIBRARY}) +endif() + +mark_as_advanced(CARES_INCLUDE_DIR CARES_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake index b244e610e..e84f8947e 100644 --- a/Utilities/cmcurl/CMake/FindGSS.cmake +++ b/Utilities/cmcurl/CMake/FindGSS.cmake @@ -21,275 +21,276 @@ # SPDX-License-Identifier: curl # ########################################################################### -# - Try to find the GSS Kerberos library -# Once done this will define +# Find the GSS Kerberos library # -# GSS_ROOT_DIR - Set this variable to the root installation of GSS +# Input variables: # -# Read-Only variables: -# GSS_FOUND - system has the Heimdal library -# GSS_FLAVOUR - "MIT" or "Heimdal" if anything found. -# GSS_INCLUDE_DIR - the Heimdal include directory -# GSS_LIBRARIES - The libraries needed to use GSS -# GSS_LINK_DIRECTORIES - Directories to add to linker search path -# GSS_LINKER_FLAGS - Additional linker flags -# GSS_COMPILER_FLAGS - Additional compiler flags -# GSS_VERSION - This is set to version advertised by pkg-config or read from manifest. -# In case the library is found but no version info available it'll be set to "unknown" - -set(_MIT_MODNAME mit-krb5-gssapi) -set(_HEIMDAL_MODNAME heimdal-gssapi) +# GSS_ROOT_DIR Set this variable to the root installation of GSS +# +# Result variables: +# +# GSS_FOUND System has the Heimdal library +# GSS_FLAVOUR "MIT" or "Heimdal" if anything found +# GSS_INCLUDE_DIRS The GSS include directories +# GSS_LIBRARIES The GSS library names +# GSS_LIBRARY_DIRS The GSS library directories +# GSS_LDFLAGS Required linker flags +# GSS_CFLAGS Required compiler flags +# GSS_VERSION This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info available it is set to "unknown" + +set(_mit_modname "mit-krb5-gssapi") +set(_heimdal_modname "heimdal-gssapi") include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckTypeSize) -set(_GSS_ROOT_HINTS - "${GSS_ROOT_DIR}" - "$ENV{GSS_ROOT_DIR}" +set(_gss_root_hints + "${GSS_ROOT_DIR}" + "$ENV{GSS_ROOT_DIR}" ) -# try to find library using system pkg-config if user didn't specify root dir +# Try to find library using system pkg-config if user did not specify root dir if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") - if(UNIX) + if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(_GSS_PKG ${_MIT_MODNAME} ${_HEIMDAL_MODNAME}) - list(APPEND _GSS_ROOT_HINTS "${_GSS_PKG_PREFIX}") - elseif(WIN32) - list(APPEND _GSS_ROOT_HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]") + pkg_search_module(_GSS ${_mit_modname} ${_heimdal_modname}) + list(APPEND _gss_root_hints "${_GSS_PREFIX}") + endif() + if(WIN32) + list(APPEND _gss_root_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]") endif() endif() -if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approach. - find_file(_GSS_CONFIGURE_SCRIPT - NAMES - "krb5-config" - HINTS - ${_GSS_ROOT_HINTS} - PATH_SUFFIXES - bin - NO_CMAKE_PATH - NO_CMAKE_ENVIRONMENT_PATH +if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional approach. + find_file(_gss_configure_script + NAMES + "krb5-config" + HINTS + ${_gss_root_hints} + PATH_SUFFIXES + "bin" + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH ) - # if not found in user-supplied directories, maybe system knows better - find_file(_GSS_CONFIGURE_SCRIPT - NAMES - "krb5-config" - PATH_SUFFIXES - bin + # If not found in user-supplied directories, maybe system knows better + find_file(_gss_configure_script + NAMES + "krb5-config" + PATH_SUFFIXES + "bin" ) - if(_GSS_CONFIGURE_SCRIPT) + if(_gss_configure_script) execute_process( - COMMAND ${_GSS_CONFIGURE_SCRIPT} "--cflags" "gssapi" - OUTPUT_VARIABLE _GSS_CFLAGS - RESULT_VARIABLE _GSS_CONFIGURE_FAILED - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - message(STATUS "CFLAGS: ${_GSS_CFLAGS}") - if(NOT _GSS_CONFIGURE_FAILED) # 0 means success - # should also work in an odd case when multiple directories are given + COMMAND ${_gss_configure_script} "--cflags" "gssapi" + OUTPUT_VARIABLE _GSS_CFLAGS + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "FindGSS CFLAGS: ${_GSS_CFLAGS}") + if(NOT _gss_configure_failed) # 0 means success + # Should also work in an odd case when multiple directories are given string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS) string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}") string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}") - foreach(_flag ${_GSS_CFLAGS}) + foreach(_flag IN LISTS _GSS_CFLAGS) if(_flag MATCHES "^-I.*") string(REGEX REPLACE "^-I" "" _val "${_flag}") - list(APPEND _GSS_INCLUDE_DIR "${_val}") + list(APPEND _GSS_INCLUDE_DIRS "${_val}") else() - list(APPEND _GSS_COMPILER_FLAGS "${_flag}") + list(APPEND _GSS_CFLAGS "${_flag}") endif() endforeach() endif() execute_process( - COMMAND ${_GSS_CONFIGURE_SCRIPT} "--libs" "gssapi" - OUTPUT_VARIABLE _GSS_LIB_FLAGS - RESULT_VARIABLE _GSS_CONFIGURE_FAILED - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND ${_gss_configure_script} "--libs" "gssapi" + OUTPUT_VARIABLE _gss_lib_flags + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE ) - message(STATUS "LDFLAGS: ${_GSS_LIB_FLAGS}") + message(STATUS "FindGSS LDFLAGS: ${_gss_lib_flags}") - if(NOT _GSS_CONFIGURE_FAILED) # 0 means success - # this script gives us libraries and link directories. Blah. We have to deal with it. - string(STRIP "${_GSS_LIB_FLAGS}" _GSS_LIB_FLAGS) - string(REGEX REPLACE " +-(L|l)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") - string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}") + if(NOT _gss_configure_failed) # 0 means success + # This script gives us libraries and link directories. Blah. We have to deal with it. + string(STRIP "${_gss_lib_flags}" _gss_lib_flags) + string(REGEX REPLACE " +-(L|l)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") + string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") - foreach(_flag ${_GSS_LIB_FLAGS}) + foreach(_flag IN LISTS _gss_lib_flags) if(_flag MATCHES "^-l.*") string(REGEX REPLACE "^-l" "" _val "${_flag}") list(APPEND _GSS_LIBRARIES "${_val}") elseif(_flag MATCHES "^-L.*") string(REGEX REPLACE "^-L" "" _val "${_flag}") - list(APPEND _GSS_LINK_DIRECTORIES "${_val}") + list(APPEND _GSS_LIBRARY_DIRS "${_val}") else() - list(APPEND _GSS_LINKER_FLAGS "${_flag}") + list(APPEND _GSS_LDFLAGS "${_flag}") endif() endforeach() endif() execute_process( - COMMAND ${_GSS_CONFIGURE_SCRIPT} "--version" - OUTPUT_VARIABLE _GSS_VERSION - RESULT_VARIABLE _GSS_CONFIGURE_FAILED - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND ${_gss_configure_script} "--version" + OUTPUT_VARIABLE _GSS_VERSION + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE ) - # older versions may not have the "--version" parameter. In this case we just don't care. - if(_GSS_CONFIGURE_FAILED) + # Older versions may not have the "--version" parameter. In this case we just do not care. + if(_gss_configure_failed) set(_GSS_VERSION 0) endif() execute_process( - COMMAND ${_GSS_CONFIGURE_SCRIPT} "--vendor" - OUTPUT_VARIABLE _GSS_VENDOR - RESULT_VARIABLE _GSS_CONFIGURE_FAILED - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND ${_gss_configure_script} "--vendor" + OUTPUT_VARIABLE _gss_vendor + RESULT_VARIABLE _gss_configure_failed + OUTPUT_STRIP_TRAILING_WHITESPACE ) - # older versions may not have the "--vendor" parameter. In this case we just don't care. - if(_GSS_CONFIGURE_FAILED) - set(GSS_FLAVOUR "Heimdal") # most probably, shouldn't really matter + # Older versions may not have the "--vendor" parameter. In this case we just do not care. + if(_gss_configure_failed) + set(GSS_FLAVOUR "Heimdal") # most probably, should not really matter else() - if(_GSS_VENDOR MATCHES ".*H|heimdal.*") + if(_gss_vendor MATCHES ".*H|heimdal.*") set(GSS_FLAVOUR "Heimdal") else() set(GSS_FLAVOUR "MIT") endif() endif() - else() # either there is no config script or we are on a platform that doesn't provide one (Windows?) + else() # Either there is no config script or we are on a platform that does not provide one (Windows?) - find_path(_GSS_INCLUDE_DIR - NAMES - "gssapi/gssapi.h" - HINTS - ${_GSS_ROOT_HINTS} - PATH_SUFFIXES - include - inc + find_path(_GSS_INCLUDE_DIRS NAMES "gssapi/gssapi.h" + HINTS + ${_gss_root_hints} + PATH_SUFFIXES + "include" + "inc" ) - if(_GSS_INCLUDE_DIR) #jay, we've found something - set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIR}") - check_include_files( "gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _GSS_HAVE_MIT_HEADERS) + if(_GSS_INCLUDE_DIRS) # jay, we have found something + set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}") + check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) - if(_GSS_HAVE_MIT_HEADERS) + if(_gss_have_mit_headers) set(GSS_FLAVOUR "MIT") else() - # prevent compiling the header - just check if we can include it - list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) - check_include_file( "roken.h" _GSS_HAVE_ROKEN_H) + # Prevent compiling the header - just check if we can include it + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D__ROKEN_H__") + check_include_file("roken.h" _gss_have_roken_h) - check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H) - if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H) + check_include_file("heimdal/roken.h" _gss_have_heimdal_roken_h) + if(_gss_have_roken_h OR _gss_have_heimdal_roken_h) set(GSS_FLAVOUR "Heimdal") endif() - list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS "-D__ROKEN_H__") endif() else() - # I'm not convinced if this is the right way but this is what autotools do at the moment - find_path(_GSS_INCLUDE_DIR - NAMES - "gssapi.h" - HINTS - ${_GSS_ROOT_HINTS} - PATH_SUFFIXES - include - inc + # I am not convinced if this is the right way but this is what autotools do at the moment + find_path(_GSS_INCLUDE_DIRS NAMES "gssapi.h" + HINTS + ${_gss_root_hints} + PATH_SUFFIXES + "include" + "inc" ) - if(_GSS_INCLUDE_DIR) + if(_GSS_INCLUDE_DIRS) set(GSS_FLAVOUR "Heimdal") endif() endif() - # if we have headers, check if we can link libraries + # If we have headers, check if we can link libraries if(GSS_FLAVOUR) - set(_GSS_LIBDIR_SUFFIXES "") - set(_GSS_LIBDIR_HINTS ${_GSS_ROOT_HINTS}) - get_filename_component(_GSS_CALCULATED_POTENTIAL_ROOT "${_GSS_INCLUDE_DIR}" PATH) - list(APPEND _GSS_LIBDIR_HINTS ${_GSS_CALCULATED_POTENTIAL_ROOT}) + set(_gss_libdir_suffixes "") + set(_gss_libdir_hints ${_gss_root_hints}) + get_filename_component(_gss_calculated_potential_root "${_GSS_INCLUDE_DIRS}" DIRECTORY) + list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _GSS_LIBDIR_SUFFIXES "lib/AMD64") + list(APPEND _gss_libdir_suffixes "lib/AMD64") if(GSS_FLAVOUR STREQUAL "MIT") - set(_GSS_LIBNAME "gssapi64") + set(_gss_libname "gssapi64") else() - set(_GSS_LIBNAME "libgssapi") + set(_gss_libname "libgssapi") endif() else() - list(APPEND _GSS_LIBDIR_SUFFIXES "lib/i386") + list(APPEND _gss_libdir_suffixes "lib/i386") if(GSS_FLAVOUR STREQUAL "MIT") - set(_GSS_LIBNAME "gssapi32") + set(_gss_libname "gssapi32") else() - set(_GSS_LIBNAME "libgssapi") + set(_gss_libname "libgssapi") endif() endif() else() - list(APPEND _GSS_LIBDIR_SUFFIXES "lib;lib64") # those suffixes are not checked for HINTS + list(APPEND _gss_libdir_suffixes "lib;lib64") # those suffixes are not checked for HINTS if(GSS_FLAVOUR STREQUAL "MIT") - set(_GSS_LIBNAME "gssapi_krb5") + set(_gss_libname "gssapi_krb5") else() - set(_GSS_LIBNAME "gssapi") + set(_gss_libname "gssapi") endif() endif() - find_library(_GSS_LIBRARIES - NAMES - ${_GSS_LIBNAME} - HINTS - ${_GSS_LIBDIR_HINTS} - PATH_SUFFIXES - ${_GSS_LIBDIR_SUFFIXES} + find_library(_GSS_LIBRARIES NAMES ${_gss_libname} + HINTS + ${_gss_libdir_hints} + PATH_SUFFIXES + ${_gss_libdir_suffixes} ) - endif() endif() else() - if(_GSS_PKG_${_MIT_MODNAME}_VERSION) + if(_GSS_MODULE_NAME STREQUAL _mit_modname OR _GSS_${_mit_modname}_VERSION) # _GSS_MODULE_NAME set since CMake 3.16 set(GSS_FLAVOUR "MIT") - set(_GSS_VERSION _GSS_PKG_${_MIT_MODNAME}_VERSION) + if(NOT _GSS_VERSION) # for old CMake versions? + set(_GSS_VERSION ${_GSS_${_mit_modname}_VERSION}) + endif() else() set(GSS_FLAVOUR "Heimdal") - set(_GSS_VERSION _GSS_PKG_${_MIT_HEIMDAL}_VERSION) + if(NOT _GSS_VERSION) # for old CMake versions? + set(_GSS_VERSION ${_GSS_${_heimdal_modname}_VERSION}) + endif() endif() + message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_GSS_INCLUDE_DIRS} (found version \"${_GSS_VERSION}\")") endif() -set(GSS_INCLUDE_DIR ${_GSS_INCLUDE_DIR}) +set(GSS_INCLUDE_DIRS ${_GSS_INCLUDE_DIRS}) set(GSS_LIBRARIES ${_GSS_LIBRARIES}) -set(GSS_LINK_DIRECTORIES ${_GSS_LINK_DIRECTORIES}) -set(GSS_LINKER_FLAGS ${_GSS_LINKER_FLAGS}) -set(GSS_COMPILER_FLAGS ${_GSS_COMPILER_FLAGS}) +set(GSS_LIBRARY_DIRS ${_GSS_LIBRARY_DIRS}) +set(GSS_LDFLAGS ${_GSS_LDFLAGS}) +set(GSS_CFLAGS ${_GSS_CFLAGS}) set(GSS_VERSION ${_GSS_VERSION}) if(GSS_FLAVOUR) if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal") if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.amd64.manifest") + set(_heimdal_manifest_file "Heimdal.Application.amd64.manifest") else() - set(HEIMDAL_MANIFEST_FILE "Heimdal.Application.x86.manifest") + set(_heimdal_manifest_file "Heimdal.Application.x86.manifest") endif() - if(EXISTS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}") - file(STRINGS "${GSS_INCLUDE_DIR}/${HEIMDAL_MANIFEST_FILE}" heimdal_version_str - REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$") + if(EXISTS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}") + file(STRINGS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}" _heimdal_version_str + REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$") - string(REGEX MATCH "[0-9]\\.[^\"]+" - GSS_VERSION "${heimdal_version_str}") + string(REGEX MATCH "[0-9]\\.[^\"]+" GSS_VERSION "${_heimdal_version_str}") endif() if(NOT GSS_VERSION) set(GSS_VERSION "Heimdal Unknown") endif() elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "MIT") - get_filename_component(_MIT_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) - if(WIN32 AND _MIT_VERSION) - set(GSS_VERSION "${_MIT_VERSION}") + get_filename_component(_mit_version "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME + CACHE) + if(WIN32 AND _mit_version) + set(GSS_VERSION "${_mit_version}") else() set(GSS_VERSION "MIT Unknown") endif() @@ -297,16 +298,24 @@ if(GSS_FLAVOUR) endif() include(FindPackageHandleStandardArgs) - -set(_GSS_REQUIRED_VARS GSS_LIBRARIES GSS_FLAVOUR) - find_package_handle_standard_args(GSS - REQUIRED_VARS - ${_GSS_REQUIRED_VARS} - VERSION_VAR - GSS_VERSION - FAIL_MESSAGE - "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR" + REQUIRED_VARS + GSS_FLAVOUR + GSS_LIBRARIES + VERSION_VAR + GSS_VERSION + FAIL_MESSAGE + "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR" ) -mark_as_advanced(GSS_INCLUDE_DIR GSS_LIBRARIES) +mark_as_advanced( + _GSS_CFLAGS + _GSS_FOUND + _GSS_INCLUDE_DIRS + _GSS_LDFLAGS + _GSS_LIBRARIES + _GSS_LIBRARY_DIRS + _GSS_MODULE_NAME + _GSS_PREFIX + _GSS_VERSION +) diff --git a/Utilities/cmcurl/CMake/FindLibSSH2.cmake b/Utilities/cmcurl/CMake/FindLibSSH2.cmake deleted file mode 100644 index a0c251ae3..000000000 --- a/Utilities/cmcurl/CMake/FindLibSSH2.cmake +++ /dev/null @@ -1,45 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, , 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 libssh2 library -# Once done this will define -# -# LIBSSH2_FOUND - system has the libssh2 library -# LIBSSH2_INCLUDE_DIR - the libssh2 include directory -# LIBSSH2_LIBRARY - the libssh2 library name - -find_path(LIBSSH2_INCLUDE_DIR libssh2.h) - -find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2) - -if(LIBSSH2_INCLUDE_DIR) - file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" libssh2_version_str REGEX "^#define[\t ]+LIBSSH2_VERSION[\t ]+\"(.*)\"") - string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1" LIBSSH2_VERSION "${libssh2_version_str}") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibSSH2 - REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR - VERSION_VAR LIBSSH2_VERSION) - -mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindLibgsasl.cmake b/Utilities/cmcurl/CMake/FindLibgsasl.cmake new file mode 100644 index 000000000..ed930ebb7 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibgsasl.cmake @@ -0,0 +1,78 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libgsasl library +# +# Input variables: +# +# LIBGSASL_INCLUDE_DIR The libgsasl include directory +# LIBGSASL_LIBRARY Path to libgsasl library +# +# Result variables: +# +# LIBGSASL_FOUND System has libgsasl +# LIBGSASL_INCLUDE_DIRS The libgsasl include directories +# LIBGSASL_LIBRARIES The libgsasl library names +# LIBGSASL_LIBRARY_DIRS The libgsasl library directories +# LIBGSASL_CFLAGS Required compiler flags +# LIBGSASL_VERSION Version of libgsasl + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBGSASL_INCLUDE_DIR AND + NOT DEFINED LIBGSASL_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LIBGSASL "libgsasl") +endif() + +if(LIBGSASL_FOUND) + string(REPLACE ";" " " LIBGSASL_CFLAGS "${LIBGSASL_CFLAGS}") + message(STATUS "Found Libgsasl (via pkg-config): ${LIBGSASL_INCLUDE_DIRS} (found version \"${LIBGSASL_VERSION}\")") +else() + find_path(LIBGSASL_INCLUDE_DIR NAMES "gsasl.h") + find_library(LIBGSASL_LIBRARY NAMES "gsasl" "libgsasl") + + if(LIBGSASL_INCLUDE_DIR AND EXISTS "${LIBGSASL_INCLUDE_DIR}/gsasl-version.h") + set(_version_regex "#[\t ]*define[\t ]+GSASL_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBGSASL_INCLUDE_DIR}/gsasl-version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBGSASL_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libgsasl + REQUIRED_VARS + LIBGSASL_INCLUDE_DIR + LIBGSASL_LIBRARY + VERSION_VAR + LIBGSASL_VERSION + ) + + if(LIBGSASL_FOUND) + set(LIBGSASL_INCLUDE_DIRS ${LIBGSASL_INCLUDE_DIR}) + set(LIBGSASL_LIBRARIES ${LIBGSASL_LIBRARY}) + endif() + + mark_as_advanced(LIBGSASL_INCLUDE_DIR LIBGSASL_LIBRARY) +endif() diff --git a/Utilities/cmcurl/CMake/FindLibidn2.cmake b/Utilities/cmcurl/CMake/FindLibidn2.cmake new file mode 100644 index 000000000..47d4a5862 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibidn2.cmake @@ -0,0 +1,78 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libidn2 library +# +# Input variables: +# +# LIBIDN2_INCLUDE_DIR The libidn2 include directory +# LIBIDN2_LIBRARY Path to libidn2 library +# +# Result variables: +# +# LIBIDN2_FOUND System has libidn2 +# LIBIDN2_INCLUDE_DIRS The libidn2 include directories +# LIBIDN2_LIBRARIES The libidn2 library names +# LIBIDN2_LIBRARY_DIRS The libidn2 library directories +# LIBIDN2_CFLAGS Required compiler flags +# LIBIDN2_VERSION Version of libidn2 + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBIDN2_INCLUDE_DIR AND + NOT DEFINED LIBIDN2_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LIBIDN2 "libidn2") +endif() + +if(LIBIDN2_FOUND) + string(REPLACE ";" " " LIBIDN2_CFLAGS "${LIBIDN2_CFLAGS}") + message(STATUS "Found Libidn2 (via pkg-config): ${LIBIDN2_INCLUDE_DIRS} (found version \"${LIBIDN2_VERSION}\")") +else() + find_path(LIBIDN2_INCLUDE_DIR NAMES "idn2.h") + find_library(LIBIDN2_LIBRARY NAMES "idn2" "libidn2") + + if(LIBIDN2_INCLUDE_DIR AND EXISTS "${LIBIDN2_INCLUDE_DIR}/idn2.h") + set(_version_regex "#[\t ]*define[\t ]+IDN2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBIDN2_INCLUDE_DIR}/idn2.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBIDN2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libidn2 + REQUIRED_VARS + LIBIDN2_INCLUDE_DIR + LIBIDN2_LIBRARY + VERSION_VAR + LIBIDN2_VERSION + ) + + if(LIBIDN2_FOUND) + set(LIBIDN2_INCLUDE_DIRS ${LIBIDN2_INCLUDE_DIR}) + set(LIBIDN2_LIBRARIES ${LIBIDN2_LIBRARY}) + endif() + + mark_as_advanced(LIBIDN2_INCLUDE_DIR LIBIDN2_LIBRARY) +endif() diff --git a/Utilities/cmcurl/CMake/FindLibpsl.cmake b/Utilities/cmcurl/CMake/FindLibpsl.cmake new file mode 100644 index 000000000..b2cd64f0d --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibpsl.cmake @@ -0,0 +1,80 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libpsl library +# +# Input variables: +# +# LIBPSL_INCLUDE_DIR The libpsl include directory +# LIBPSL_LIBRARY Path to libpsl library +# +# Result variables: +# +# LIBPSL_FOUND System has libpsl +# LIBPSL_INCLUDE_DIRS The libpsl include directories +# LIBPSL_LIBRARIES The libpsl library names +# LIBPSL_VERSION Version of libpsl + +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_LIBPSL "libpsl") +endif() + +find_path(LIBPSL_INCLUDE_DIR NAMES "libpsl.h" + HINTS + ${PC_LIBPSL_INCLUDEDIR} + ${PC_LIBPSL_INCLUDE_DIRS} +) + +find_library(LIBPSL_LIBRARY NAMES "psl" "libpsl" + HINTS + ${PC_LIBPSL_LIBDIR} + ${PC_LIBPSL_LIBRARY_DIRS} +) + +if(PC_LIBPSL_VERSION) + set(LIBPSL_VERSION ${PC_LIBPSL_VERSION}) +elseif(LIBPSL_INCLUDE_DIR AND EXISTS "${LIBPSL_INCLUDE_DIR}/libpsl.h") + set(_version_regex "#[\t ]*define[\t ]+PSL_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBPSL_INCLUDE_DIR}/libpsl.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBPSL_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libpsl + REQUIRED_VARS + LIBPSL_INCLUDE_DIR + LIBPSL_LIBRARY + VERSION_VAR + LIBPSL_VERSION +) + +if(LIBPSL_FOUND) + set(LIBPSL_INCLUDE_DIRS ${LIBPSL_INCLUDE_DIR}) + set(LIBPSL_LIBRARIES ${LIBPSL_LIBRARY}) +endif() + +mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindLibssh.cmake b/Utilities/cmcurl/CMake/FindLibssh.cmake new file mode 100644 index 000000000..7dc019be7 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibssh.cmake @@ -0,0 +1,92 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libssh library +# +# Input variables: +# +# LIBSSH_INCLUDE_DIR The libssh include directory +# LIBSSH_LIBRARY Path to libssh library +# +# Result variables: +# +# LIBSSH_FOUND System has libssh +# LIBSSH_INCLUDE_DIRS The libssh include directories +# LIBSSH_LIBRARIES The libssh library names +# LIBSSH_LIBRARY_DIRS The libssh library directories +# LIBSSH_CFLAGS Required compiler flags +# LIBSSH_VERSION Version of libssh + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBSSH_INCLUDE_DIR AND + NOT DEFINED LIBSSH_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LIBSSH "libssh") +endif() + +if(LIBSSH_FOUND) + string(REPLACE ";" " " LIBSSH_CFLAGS "${LIBSSH_CFLAGS}") + message(STATUS "Found Libssh (via pkg-config): ${LIBSSH_INCLUDE_DIRS} (found version \"${LIBSSH_VERSION}\")") +else() + find_path(LIBSSH_INCLUDE_DIR NAMES "libssh/libssh.h") + find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + + if(LIBSSH_INCLUDE_DIR AND EXISTS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h") + set(_version_regex1 "#[\t ]*define[\t ]+LIBSSH_VERSION_MAJOR[\t ]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[\t ]+LIBSSH_VERSION_MINOR[\t ]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[\t ]+LIBSSH_VERSION_MICRO[\t ]+([0-9]+).*") + file(STRINGS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(LIBSSH_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libssh + REQUIRED_VARS + LIBSSH_INCLUDE_DIR + LIBSSH_LIBRARY + VERSION_VAR + LIBSSH_VERSION + ) + + if(LIBSSH_FOUND) + set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) + set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY}) + endif() + + mark_as_advanced(LIBSSH_INCLUDE_DIR LIBSSH_LIBRARY) +endif() + +if(LIBSSH_FOUND AND WIN32) + list(APPEND LIBSSH_LIBRARIES "iphlpapi") # for if_nametoindex +endif() diff --git a/Utilities/cmcurl/CMake/FindLibssh2.cmake b/Utilities/cmcurl/CMake/FindLibssh2.cmake new file mode 100644 index 000000000..0007a8a72 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibssh2.cmake @@ -0,0 +1,80 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libssh2 library +# +# Input variables: +# +# LIBSSH2_INCLUDE_DIR The libssh2 include directory +# LIBSSH2_LIBRARY Path to libssh2 library +# +# Result variables: +# +# LIBSSH2_FOUND System has libssh2 +# LIBSSH2_INCLUDE_DIRS The libssh2 include directories +# LIBSSH2_LIBRARIES The libssh2 library names +# LIBSSH2_VERSION Version of libssh2 + +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_LIBSSH2 "libssh2") +endif() + +find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h" + HINTS + ${PC_LIBSSH2_INCLUDEDIR} + ${PC_LIBSSH2_INCLUDE_DIRS} +) + +find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2" + HINTS + ${PC_LIBSSH2_LIBDIR} + ${PC_LIBSSH2_LIBRARY_DIRS} +) + +if(PC_LIBSSH2_VERSION) + set(LIBSSH2_VERSION ${PC_LIBSSH2_VERSION}) +elseif(LIBSSH2_INCLUDE_DIR AND EXISTS "${LIBSSH2_INCLUDE_DIR}/libssh2.h") + set(_version_regex "#[\t ]*define[\t ]+LIBSSH2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${LIBSSH2_INCLUDE_DIR}/libssh2.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(LIBSSH2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libssh2 + REQUIRED_VARS + LIBSSH2_INCLUDE_DIR + LIBSSH2_LIBRARY + VERSION_VAR + LIBSSH2_VERSION +) + +if(LIBSSH2_FOUND) + set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) + set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY}) +endif() + +mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindLibuv.cmake b/Utilities/cmcurl/CMake/FindLibuv.cmake new file mode 100644 index 000000000..d4dfa2450 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindLibuv.cmake @@ -0,0 +1,88 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the libuv library +# +# Input variables: +# +# LIBUV_INCLUDE_DIR The libuv include directory +# LIBUV_LIBRARY Path to libuv library +# +# Result variables: +# +# LIBUV_FOUND System has libuv +# LIBUV_INCLUDE_DIRS The libuv include directories +# LIBUV_LIBRARIES The libuv library names +# LIBUV_LIBRARY_DIRS The libuv library directories +# LIBUV_CFLAGS Required compiler flags +# LIBUV_VERSION Version of libuv + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED LIBUV_INCLUDE_DIR AND + NOT DEFINED LIBUV_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(LIBUV "libuv") +endif() + +if(LIBUV_FOUND) + string(REPLACE ";" " " LIBUV_CFLAGS "${LIBUV_CFLAGS}") + message(STATUS "Found Libuv (via pkg-config): ${LIBUV_INCLUDE_DIRS} (found version \"${LIBUV_VERSION}\")") +else() + find_path(LIBUV_INCLUDE_DIR NAMES "uv.h") + find_library(LIBUV_LIBRARY NAMES "uv" "libuv") + + if(LIBUV_INCLUDE_DIR AND EXISTS "${LIBUV_INCLUDE_DIR}/uv/version.h") + set(_version_regex1 "#[\t ]*define[\t ]+UV_VERSION_MAJOR[\t ]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[\t ]+UV_VERSION_MINOR[\t ]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[\t ]+UV_VERSION_PATCH[\t ]+([0-9]+).*") + file(STRINGS "${LIBUV_INCLUDE_DIR}/uv/version.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${LIBUV_INCLUDE_DIR}/uv/version.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${LIBUV_INCLUDE_DIR}/uv/version.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(LIBUV_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libuv + REQUIRED_VARS + LIBUV_INCLUDE_DIR + LIBUV_LIBRARY + VERSION_VAR + LIBUV_VERSION + ) + + if(LIBUV_FOUND) + set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) + set(LIBUV_LIBRARIES ${LIBUV_LIBRARY}) + endif() + + mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) +endif() diff --git a/Utilities/cmcurl/CMake/FindMSH3.cmake b/Utilities/cmcurl/CMake/FindMSH3.cmake index 7d9c6b654..46cee8871 100644 --- a/Utilities/cmcurl/CMake/FindMSH3.cmake +++ b/Utilities/cmcurl/CMake/FindMSH3.cmake @@ -21,50 +21,53 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Find the msh3 library +# +# Input variables: +# +# MSH3_INCLUDE_DIR The msh3 include directory +# MSH3_LIBRARY Path to msh3 library +# +# Result variables: +# +# MSH3_FOUND System has msh3 +# MSH3_INCLUDE_DIRS The msh3 include directories +# MSH3_LIBRARIES The msh3 library names +# MSH3_VERSION Version of msh3 -#[=======================================================================[.rst: -FindMSH3 ----------- - -Find the msh3 library - -Result Variables -^^^^^^^^^^^^^^^^ - -``MSH3_FOUND`` - System has msh3 -``MSH3_INCLUDE_DIRS`` - The msh3 include directories. -``MSH3_LIBRARIES`` - The libraries needed to use msh3 -#]=======================================================================] -if(UNIX) +if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(PC_MSH3 libmsh3) + pkg_check_modules(PC_MSH3 "libmsh3") endif() -find_path(MSH3_INCLUDE_DIR msh3.h +find_path(MSH3_INCLUDE_DIR NAMES "msh3.h" HINTS ${PC_MSH3_INCLUDEDIR} ${PC_MSH3_INCLUDE_DIRS} ) -find_library(MSH3_LIBRARY NAMES msh3 +find_library(MSH3_LIBRARY NAMES "msh3" HINTS ${PC_MSH3_LIBDIR} ${PC_MSH3_LIBRARY_DIRS} ) +if(PC_MSH3_VERSION) + set(MSH3_VERSION ${PC_MSH3_VERSION}) +endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MSH3 REQUIRED_VARS - MSH3_LIBRARY MSH3_INCLUDE_DIR + MSH3_LIBRARY + VERSION_VAR + MSH3_VERSION ) if(MSH3_FOUND) - set(MSH3_LIBRARIES ${MSH3_LIBRARY}) set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR}) + set(MSH3_LIBRARIES ${MSH3_LIBRARY}) endif() -mark_as_advanced(MSH3_INCLUDE_DIRS MSH3_LIBRARIES) +mark_as_advanced(MSH3_INCLUDE_DIR MSH3_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index 814bd97da..53b86149e 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake @@ -21,16 +21,91 @@ # SPDX-License-Identifier: curl # ########################################################################### -find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h) +# Find the mbedtls library +# +# Input variables: +# +# MBEDTLS_INCLUDE_DIR The mbedtls include directory +# MBEDTLS_INCLUDE_DIRS The mbedtls include directory (deprecated) +# MBEDTLS_LIBRARY Path to mbedtls library +# MBEDX509_LIBRARY Path to mbedx509 library +# MBEDCRYPTO_LIBRARY Path to mbedcrypto library +# +# Result variables: +# +# MBEDTLS_FOUND System has mbedtls +# MBEDTLS_INCLUDE_DIRS The mbedtls include directories +# MBEDTLS_LIBRARIES The mbedtls library names +# MBEDTLS_VERSION Version of mbedtls -find_library(MBEDTLS_LIBRARY mbedtls) -find_library(MBEDX509_LIBRARY mbedx509) -find_library(MBEDCRYPTO_LIBRARY mbedcrypto) +if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) + message(WARNING "MBEDTLS_INCLUDE_DIRS is deprecated, use MBEDTLS_INCLUDE_DIR instead.") + set(MBEDTLS_INCLUDE_DIR "${MBEDTLS_INCLUDE_DIRS}") + unset(MBEDTLS_INCLUDE_DIRS) +endif() -set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_MBEDTLS "mbedtls") +endif() + +find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h" + HINTS + ${PC_MBEDTLS_INCLUDEDIR} + ${PC_MBEDTLS_INCLUDE_DIRS} +) + +find_library(MBEDTLS_LIBRARY NAMES "mbedtls" + HINTS + ${PC_MBEDTLS_LIBDIR} + ${PC_MBEDTLS_LIBRARY_DIRS} +) +find_library(MBEDX509_LIBRARY NAMES "mbedx509" + HINTS + ${PC_MBEDTLS_LIBDIR} + ${PC_MBEDTLS_LIBRARY_DIRS} +) +find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" + HINTS + ${PC_MBEDTLS_LIBDIR} + ${PC_MBEDTLS_LIBRARY_DIRS} +) + +if(PC_MBEDTLS_VERSION) + set(MBEDTLS_VERSION ${PC_MBEDTLS_VERSION}) +elseif(MBEDTLS_INCLUDE_DIR) + if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") # 3.x + set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") + elseif(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") # 2.x + set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") + else() + unset(_version_header) + endif() + if(_version_header) + set(_version_regex "#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"([0-9.]+)\"") + file(STRINGS "${_version_header}" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(MBEDTLS_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + unset(_version_header) + endif() +endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MbedTLS DEFAULT_MSG - MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) +find_package_handle_standard_args(MbedTLS + REQUIRED_VARS + MBEDTLS_INCLUDE_DIR + MBEDTLS_LIBRARY + MBEDX509_LIBRARY + MBEDCRYPTO_LIBRARY + VERSION_VAR + MBEDTLS_VERSION +) + +if(MBEDTLS_FOUND) + set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) +endif() -mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) +mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index d3528cb27..e632d2054 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake @@ -21,21 +21,60 @@ # SPDX-License-Identifier: curl # ########################################################################### -include(FindPackageHandleStandardArgs) +# Find the nghttp2 library +# +# Input variables: +# +# NGHTTP2_INCLUDE_DIR The nghttp2 include directory +# NGHTTP2_LIBRARY Path to nghttp2 library +# +# Result variables: +# +# NGHTTP2_FOUND System has nghttp2 +# NGHTTP2_INCLUDE_DIRS The nghttp2 include directories +# NGHTTP2_LIBRARIES The nghttp2 library names +# NGHTTP2_VERSION Version of nghttp2 -find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h") +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_NGHTTP2 "libnghttp2") +endif() -find_library(NGHTTP2_LIBRARY NAMES nghttp2 nghttp2_static) +find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h" + HINTS + ${PC_NGHTTP2_INCLUDEDIR} + ${PC_NGHTTP2_INCLUDE_DIRS} +) + +find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static" + HINTS + ${PC_NGHTTP2_LIBDIR} + ${PC_NGHTTP2_LIBRARY_DIRS} +) +if(PC_NGHTTP2_VERSION) + set(NGHTTP2_VERSION ${PC_NGHTTP2_VERSION}) +elseif(NGHTTP2_INCLUDE_DIR AND EXISTS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h") + set(_version_regex "#[\t ]*define[\t ]+NGHTTP2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGHTTP2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() + +include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NGHTTP2 - FOUND_VAR - NGHTTP2_FOUND - REQUIRED_VARS - NGHTTP2_LIBRARY - NGHTTP2_INCLUDE_DIR + REQUIRED_VARS + NGHTTP2_INCLUDE_DIR + NGHTTP2_LIBRARY + VERSION_VAR + NGHTTP2_VERSION ) -set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) -set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) +if(NGHTTP2_FOUND) + set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) + set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) +endif() -mark_as_advanced(NGHTTP2_INCLUDE_DIRS NGHTTP2_LIBRARIES) +mark_as_advanced(NGHTTP2_INCLUDE_DIR NGHTTP2_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake index 9b13e6c6f..02a0c87d0 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake @@ -21,38 +21,32 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Find the nghttp3 library +# +# Input variables: +# +# NGHTTP3_INCLUDE_DIR The nghttp3 include directory +# NGHTTP3_LIBRARY Path to nghttp3 library +# +# Result variables: +# +# NGHTTP3_FOUND System has nghttp3 +# NGHTTP3_INCLUDE_DIRS The nghttp3 include directories +# NGHTTP3_LIBRARIES The nghttp3 library names +# NGHTTP3_VERSION Version of nghttp3 -#[=======================================================================[.rst: -FindNGHTTP3 ----------- - -Find the nghttp3 library - -Result Variables -^^^^^^^^^^^^^^^^ - -``NGHTTP3_FOUND`` - System has nghttp3 -``NGHTTP3_INCLUDE_DIRS`` - The nghttp3 include directories. -``NGHTTP3_LIBRARIES`` - The libraries needed to use nghttp3 -``NGHTTP3_VERSION`` - version of nghttp3. -#]=======================================================================] - -if(UNIX) +if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(PC_NGHTTP3 libnghttp3) + pkg_check_modules(PC_NGHTTP3 "libnghttp3") endif() -find_path(NGHTTP3_INCLUDE_DIR nghttp3/nghttp3.h +find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h" HINTS ${PC_NGHTTP3_INCLUDEDIR} ${PC_NGHTTP3_INCLUDE_DIRS} ) -find_library(NGHTTP3_LIBRARY NAMES nghttp3 +find_library(NGHTTP3_LIBRARY NAMES "nghttp3" HINTS ${PC_NGHTTP3_LIBDIR} ${PC_NGHTTP3_LIBRARY_DIRS} @@ -60,19 +54,27 @@ find_library(NGHTTP3_LIBRARY NAMES nghttp3 if(PC_NGHTTP3_VERSION) set(NGHTTP3_VERSION ${PC_NGHTTP3_VERSION}) +elseif(NGHTTP3_INCLUDE_DIR AND EXISTS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h") + set(_version_regex "#[\t ]*define[\t ]+NGHTTP3_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGHTTP3_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NGHTTP3 REQUIRED_VARS - NGHTTP3_LIBRARY NGHTTP3_INCLUDE_DIR - VERSION_VAR NGHTTP3_VERSION + NGHTTP3_LIBRARY + VERSION_VAR + NGHTTP3_VERSION ) if(NGHTTP3_FOUND) - set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) + set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) endif() -mark_as_advanced(NGHTTP3_INCLUDE_DIRS NGHTTP3_LIBRARIES) +mark_as_advanced(NGHTTP3_INCLUDE_DIR NGHTTP3_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake index 7ea466582..194483d54 100644 --- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake @@ -21,46 +21,40 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Find the ngtcp2 library +# +# This module accepts optional COMPONENTS to control the crypto library (these are +# mutually exclusive): +# +# quictls: Use libngtcp2_crypto_quictls (choose this for LibreSSL) +# BoringSSL: Use libngtcp2_crypto_boringssl (choose this for AWS-LC) +# wolfSSL: Use libngtcp2_crypto_wolfssl +# GnuTLS: Use libngtcp2_crypto_gnutls +# +# Input variables: +# +# NGTCP2_INCLUDE_DIR The ngtcp2 include directory +# NGTCP2_LIBRARY Path to ngtcp2 library +# +# Result variables: +# +# NGTCP2_FOUND System has ngtcp2 +# NGTCP2_INCLUDE_DIRS The ngtcp2 include directories +# NGTCP2_LIBRARIES The ngtcp2 library names +# NGTCP2_VERSION Version of ngtcp2 -#[=======================================================================[.rst: -FindNGTCP2 ----------- - -Find the ngtcp2 library - -This module accepts optional COMPONENTS to control the crypto library (these are -mutually exclusive):: - - quictls, LibreSSL: Use libngtcp2_crypto_quictls - BoringSSL, AWS-LC: Use libngtcp2_crypto_boringssl - wolfSSL: Use libngtcp2_crypto_wolfssl - GnuTLS: Use libngtcp2_crypto_gnutls - -Result Variables -^^^^^^^^^^^^^^^^ - -``NGTCP2_FOUND`` - System has ngtcp2 -``NGTCP2_INCLUDE_DIRS`` - The ngtcp2 include directories. -``NGTCP2_LIBRARIES`` - The libraries needed to use ngtcp2 -``NGTCP2_VERSION`` - version of ngtcp2. -#]=======================================================================] - -if(UNIX) +if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(PC_NGTCP2 libngtcp2) + pkg_check_modules(PC_NGTCP2 "libngtcp2") endif() -find_path(NGTCP2_INCLUDE_DIR ngtcp2/ngtcp2.h +find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h" HINTS ${PC_NGTCP2_INCLUDEDIR} ${PC_NGTCP2_INCLUDE_DIRS} ) -find_library(NGTCP2_LIBRARY NAMES ngtcp2 +find_library(NGTCP2_LIBRARY NAMES "ngtcp2" HINTS ${PC_NGTCP2_LIBDIR} ${PC_NGTCP2_LIBRARY_DIRS} @@ -68,33 +62,38 @@ find_library(NGTCP2_LIBRARY NAMES ngtcp2 if(PC_NGTCP2_VERSION) set(NGTCP2_VERSION ${PC_NGTCP2_VERSION}) +elseif(NGTCP2_INCLUDE_DIR AND EXISTS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h") + set(_version_regex "#[\t ]*define[\t ]+NGTCP2_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(NGTCP2_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) endif() if(NGTCP2_FIND_COMPONENTS) - set(NGTCP2_CRYPTO_BACKEND "") - foreach(component IN LISTS NGTCP2_FIND_COMPONENTS) - if(component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)") - if(NGTCP2_CRYPTO_BACKEND) + set(_ngtcp2_crypto_backend "") + foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS) + if(_component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)") + if(_ngtcp2_crypto_backend) message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") endif() - set(NGTCP2_CRYPTO_BACKEND ${component}) + set(_ngtcp2_crypto_backend ${_component}) endif() endforeach() - if(NGTCP2_CRYPTO_BACKEND) - string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library) - if(UNIX) - pkg_search_module(PC_${_crypto_library} lib${_crypto_library}) + if(_ngtcp2_crypto_backend) + string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library) + if(CURL_USE_PKGCONFIG) + pkg_check_modules(PC_${_crypto_library} "lib${_crypto_library}") endif() - find_library(${_crypto_library}_LIBRARY - NAMES - ${_crypto_library} + find_library(${_crypto_library}_LIBRARY NAMES ${_crypto_library} HINTS ${PC_${_crypto_library}_LIBDIR} ${PC_${_crypto_library}_LIBRARY_DIRS} ) if(${_crypto_library}_LIBRARY) - set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE) + set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library}_LIBRARY}) endif() endif() @@ -103,15 +102,16 @@ endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NGTCP2 REQUIRED_VARS - NGTCP2_LIBRARY NGTCP2_INCLUDE_DIR - VERSION_VAR NGTCP2_VERSION + NGTCP2_LIBRARY + VERSION_VAR + NGTCP2_VERSION HANDLE_COMPONENTS ) if(NGTCP2_FOUND) - set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) + set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) endif() -mark_as_advanced(NGTCP2_INCLUDE_DIRS NGTCP2_LIBRARIES) +mark_as_advanced(NGTCP2_INCLUDE_DIR NGTCP2_LIBRARY NGTCP2_CRYPTO_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindNettle.cmake b/Utilities/cmcurl/CMake/FindNettle.cmake new file mode 100644 index 000000000..b5da05bf7 --- /dev/null +++ b/Utilities/cmcurl/CMake/FindNettle.cmake @@ -0,0 +1,83 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the nettle library +# +# Input variables: +# +# NETTLE_INCLUDE_DIR The nettle include directory +# NETTLE_LIBRARY Path to nettle library +# +# Result variables: +# +# NETTLE_FOUND System has nettle +# NETTLE_INCLUDE_DIRS The nettle include directories +# NETTLE_LIBRARIES The nettle library names +# NETTLE_LIBRARY_DIRS The nettle library directories +# NETTLE_CFLAGS Required compiler flags +# NETTLE_VERSION Version of nettle + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED NETTLE_INCLUDE_DIR AND + NOT DEFINED NETTLE_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(NETTLE "nettle") +endif() + +if(NETTLE_FOUND) + string(REPLACE ";" " " NETTLE_CFLAGS "${NETTLE_CFLAGS}") + message(STATUS "Found Nettle (via pkg-config): ${NETTLE_INCLUDE_DIRS} (found version \"${NETTLE_VERSION}\")") +else() + find_path(NETTLE_INCLUDE_DIR NAMES "nettle/sha2.h") + find_library(NETTLE_LIBRARY NAMES "nettle") + + if(NETTLE_INCLUDE_DIR AND EXISTS "${NETTLE_INCLUDE_DIR}/nettle/version.h") + set(_version_regex1 "#[\t ]*define[ \t]+NETTLE_VERSION_MAJOR[ \t]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[ \t]+NETTLE_VERSION_MINOR[ \t]+([0-9]+).*") + file(STRINGS "${NETTLE_INCLUDE_DIR}/nettle/version.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${NETTLE_INCLUDE_DIR}/nettle/version.h" _version_str2 REGEX "${_version_regex2}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + set(NETTLE_VERSION "${_version_str1}.${_version_str2}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_str1) + unset(_version_str2) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Nettle + REQUIRED_VARS + NETTLE_INCLUDE_DIR + NETTLE_LIBRARY + VERSION_VAR + NETTLE_VERSION + ) + + if(NETTLE_FOUND) + set(NETTLE_INCLUDE_DIRS ${NETTLE_INCLUDE_DIR}) + set(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) + endif() + + mark_as_advanced(NETTLE_INCLUDE_DIR NETTLE_LIBRARY) +endif() diff --git a/Utilities/cmcurl/CMake/FindQUICHE.cmake b/Utilities/cmcurl/CMake/FindQuiche.cmake similarity index 66% rename from Utilities/cmcurl/CMake/FindQUICHE.cmake rename to Utilities/cmcurl/CMake/FindQuiche.cmake index 0488463d5..7d1626adc 100644 --- a/Utilities/cmcurl/CMake/FindQUICHE.cmake +++ b/Utilities/cmcurl/CMake/FindQuiche.cmake @@ -21,50 +21,53 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Find the quiche library +# +# Input variables: +# +# QUICHE_INCLUDE_DIR The quiche include directory +# QUICHE_LIBRARY Path to quiche library +# +# Result variables: +# +# QUICHE_FOUND System has quiche +# QUICHE_INCLUDE_DIRS The quiche include directories +# QUICHE_LIBRARIES The quiche library names +# QUICHE_VERSION Version of quiche -#[=======================================================================[.rst: -FindQUICHE ----------- - -Find the quiche library - -Result Variables -^^^^^^^^^^^^^^^^ - -``QUICHE_FOUND`` - System has quiche -``QUICHE_INCLUDE_DIRS`` - The quiche include directories. -``QUICHE_LIBRARIES`` - The libraries needed to use quiche -#]=======================================================================] -if(UNIX) +if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(PC_QUICHE quiche) + pkg_check_modules(PC_QUICHE "quiche") endif() -find_path(QUICHE_INCLUDE_DIR quiche.h +find_path(QUICHE_INCLUDE_DIR NAMES "quiche.h" HINTS ${PC_QUICHE_INCLUDEDIR} ${PC_QUICHE_INCLUDE_DIRS} ) -find_library(QUICHE_LIBRARY NAMES quiche +find_library(QUICHE_LIBRARY NAMES "quiche" HINTS ${PC_QUICHE_LIBDIR} ${PC_QUICHE_LIBRARY_DIRS} ) +if(PC_QUICHE_VERSION) + set(QUICHE_VERSION ${PC_QUICHE_VERSION}) +endif() + include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(QUICHE +find_package_handle_standard_args(Quiche REQUIRED_VARS - QUICHE_LIBRARY QUICHE_INCLUDE_DIR + QUICHE_LIBRARY + VERSION_VAR + QUICHE_VERSION ) if(QUICHE_FOUND) - set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) + set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) endif() -mark_as_advanced(QUICHE_INCLUDE_DIRS QUICHE_LIBRARIES) +mark_as_advanced(QUICHE_INCLUDE_DIR QUICHE_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindLibPSL.cmake b/Utilities/cmcurl/CMake/FindRustls.cmake similarity index 50% rename from Utilities/cmcurl/CMake/FindLibPSL.cmake rename to Utilities/cmcurl/CMake/FindRustls.cmake index e3bd68d1d..ffd6859ff 100644 --- a/Utilities/cmcurl/CMake/FindLibPSL.cmake +++ b/Utilities/cmcurl/CMake/FindRustls.cmake @@ -21,25 +21,53 @@ # SPDX-License-Identifier: curl # ########################################################################### -# - Try to find the libpsl library -# Once done this will define +# Find the rustls library # -# LIBPSL_FOUND - system has the libpsl library -# LIBPSL_INCLUDE_DIR - the libpsl include directory -# LIBPSL_LIBRARY - the libpsl library name +# Input variables: +# +# RUSTLS_INCLUDE_DIR The rustls include directory +# RUSTLS_LIBRARY Path to rustls library +# +# Result variables: +# +# RUSTLS_FOUND System has rustls +# RUSTLS_INCLUDE_DIRS The rustls include directories +# RUSTLS_LIBRARIES The rustls library names +# RUSTLS_VERSION Version of rustls -find_path(LIBPSL_INCLUDE_DIR libpsl.h) +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_RUSTLS "rustls") +endif() -find_library(LIBPSL_LIBRARY NAMES psl libpsl) +find_path(RUSTLS_INCLUDE_DIR NAMES "rustls.h" + HINTS + ${PC_RUSTLS_INCLUDEDIR} + ${PC_RUSTLS_INCLUDE_DIRS} +) -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}") +find_library(RUSTLS_LIBRARY NAMES "rustls" + HINTS + ${PC_RUSTLS_LIBDIR} + ${PC_RUSTLS_LIBRARY_DIRS} +) + +if(PC_RUSTLS_VERSION) + set(RUSTLS_VERSION ${PC_RUSTLS_VERSION}) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibPSL - REQUIRED_VARS LIBPSL_LIBRARY LIBPSL_INCLUDE_DIR - VERSION_VAR LIBPSL_VERSION) +find_package_handle_standard_args(Rustls + REQUIRED_VARS + RUSTLS_INCLUDE_DIR + RUSTLS_LIBRARY + VERSION_VAR + RUSTLS_VERSION +) + +if(RUSTLS_FOUND) + set(RUSTLS_INCLUDE_DIRS ${RUSTLS_INCLUDE_DIR}) + set(RUSTLS_LIBRARIES ${RUSTLS_LIBRARY}) +endif() -mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) +mark_as_advanced(RUSTLS_INCLUDE_DIR RUSTLS_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindWolfSSH.cmake b/Utilities/cmcurl/CMake/FindWolfSSH.cmake new file mode 100644 index 000000000..608e3abfc --- /dev/null +++ b/Utilities/cmcurl/CMake/FindWolfSSH.cmake @@ -0,0 +1,64 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , 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 +# +########################################################################### +# Find the wolfssh library +# +# Input variables: +# +# WOLFSSH_INCLUDE_DIR The wolfssh include directory +# WOLFSSH_LIBRARY Path to wolfssh library +# +# Result variables: +# +# WOLFSSH_FOUND System has wolfssh +# WOLFSSH_INCLUDE_DIRS The wolfssh include directories +# WOLFSSH_LIBRARIES The wolfssh library names +# WOLFSSH_VERSION Version of wolfssh + +find_path(WOLFSSH_INCLUDE_DIR NAMES "wolfssh/ssh.h") +find_library(WOLFSSH_LIBRARY NAMES "wolfssh" "libwolfssh") + +if(WOLFSSH_INCLUDE_DIR AND EXISTS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h") + set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSH_VERSION_STRING[\t ]+\"([^\"]*)\"") + file(STRINGS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(WOLFSSH_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WolfSSH + REQUIRED_VARS + WOLFSSH_INCLUDE_DIR + WOLFSSH_LIBRARY + VERSION_VAR + WOLFSSH_VERSION +) + +if(WOLFSSH_FOUND) + set(WOLFSSH_INCLUDE_DIRS ${WOLFSSH_INCLUDE_DIR}) + set(WOLFSSH_LIBRARIES ${WOLFSSH_LIBRARY}) +endif() + +mark_as_advanced(WOLFSSH_INCLUDE_DIR WOLFSSH_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake index d67c0eb24..905fbfd5d 100644 --- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake +++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake @@ -21,16 +21,78 @@ # SPDX-License-Identifier: curl # ########################################################################### -find_path(WolfSSL_INCLUDE_DIR NAMES wolfssl/ssl.h) -find_library(WolfSSL_LIBRARY NAMES wolfssl) -mark_as_advanced(WolfSSL_INCLUDE_DIR WolfSSL_LIBRARY) +# Find the wolfssl library +# +# Input variables: +# +# WOLFSSL_INCLUDE_DIR The wolfssl include directory +# WolfSSL_INCLUDE_DIR The wolfssl include directory (deprecated) +# WOLFSSL_LIBRARY Path to wolfssl library +# WolfSSL_LIBRARY Path to wolfssl library (deprecated) +# +# Result variables: +# +# WOLFSSL_FOUND System has wolfssl +# WOLFSSL_INCLUDE_DIRS The wolfssl include directories +# WOLFSSL_LIBRARIES The wolfssl library names +# WOLFSSL_VERSION Version of wolfssl + +if(DEFINED WolfSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_INCLUDE_DIR) + message(WARNING "WolfSSL_INCLUDE_DIR is deprecated, use WOLFSSL_INCLUDE_DIR instead.") + set(WOLFSSL_INCLUDE_DIR "${WolfSSL_INCLUDE_DIR}") +endif() +if(DEFINED WolfSSL_LIBRARY AND NOT DEFINED WOLFSSL_LIBRARY) + message(WARNING "WolfSSL_LIBRARY is deprecated, use WOLFSSL_LIBRARY instead.") + set(WOLFSSL_LIBRARY "${WolfSSL_LIBRARY}") +endif() + +if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_WOLFSSL "wolfssl") +endif() + +find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h" + HINTS + ${PC_WOLFSSL_INCLUDEDIR} + ${PC_WOLFSSL_INCLUDE_DIRS} +) + +find_library(WOLFSSL_LIBRARY NAMES "wolfssl" + HINTS + ${PC_WOLFSSL_LIBDIR} + ${PC_WOLFSSL_LIBRARY_DIRS} +) + +if(PC_WOLFSSL_VERSION) + set(WOLFSSL_VERSION ${PC_WOLFSSL_VERSION}) +elseif(WOLFSSL_INCLUDE_DIR AND EXISTS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h") + set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"([^\"]*)\"") + file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(WOLFSSL_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) +endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WolfSSL - REQUIRED_VARS WolfSSL_INCLUDE_DIR WolfSSL_LIBRARY - ) + REQUIRED_VARS + WOLFSSL_INCLUDE_DIR + WOLFSSL_LIBRARY + VERSION_VAR + WOLFSSL_VERSION +) + +if(WOLFSSL_FOUND) + set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) + set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) -if(WolfSSL_FOUND) - set(WolfSSL_INCLUDE_DIRS ${WolfSSL_INCLUDE_DIR}) - set(WolfSSL_LIBRARIES ${WolfSSL_LIBRARY}) + if(NOT WIN32) + find_library(_math_library "m") + if(_math_library) + list(APPEND WOLFSSL_LIBRARIES "m") # for log and pow + endif() + endif() endif() + +mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake index 0ea9e0c87..10558ccc4 100644 --- a/Utilities/cmcurl/CMake/FindZstd.cmake +++ b/Utilities/cmcurl/CMake/FindZstd.cmake @@ -21,58 +21,81 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Find the zstd library +# +# Input variables: +# +# ZSTD_INCLUDE_DIR The zstd include directory +# Zstd_INCLUDE_DIR The zstd include directory (deprecated) +# ZSTD_LIBRARY Path to zstd library +# Zstd_LIBRARY Path to zstd library (deprecated) +# +# Result variables: +# +# ZSTD_FOUND System has zstd +# ZSTD_INCLUDE_DIRS The zstd include directories +# ZSTD_LIBRARIES The zstd library names +# ZSTD_VERSION Version of zstd -#[=======================================================================[.rst: -FindZstd ----------- - -Find the zstd library - -Result Variables -^^^^^^^^^^^^^^^^ - -``Zstd_FOUND`` - System has zstd -``Zstd_INCLUDE_DIRS`` - The zstd include directories. -``Zstd_LIBRARIES`` - The libraries needed to use zstd -#]=======================================================================] +if(DEFINED Zstd_INCLUDE_DIR AND NOT DEFINED ZSTD_INCLUDE_DIR) + message(WARNING "Zstd_INCLUDE_DIR is deprecated, use ZSTD_INCLUDE_DIR instead.") + set(ZSTD_INCLUDE_DIR "${Zstd_INCLUDE_DIR}") +endif() +if(DEFINED Zstd_LIBRARY AND NOT DEFINED ZSTD_LIBRARY) + message(WARNING "Zstd_LIBRARY is deprecated, use ZSTD_LIBRARY instead.") + set(ZSTD_LIBRARY "${Zstd_LIBRARY}") +endif() -if(UNIX) +if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(PC_Zstd libzstd) + pkg_check_modules(PC_ZSTD "libzstd") endif() -find_path(Zstd_INCLUDE_DIR zstd.h +find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h" HINTS - ${PC_Zstd_INCLUDEDIR} - ${PC_Zstd_INCLUDE_DIRS} + ${PC_ZSTD_INCLUDEDIR} + ${PC_ZSTD_INCLUDE_DIRS} ) -find_library(Zstd_LIBRARY NAMES zstd +find_library(ZSTD_LIBRARY NAMES "zstd" HINTS - ${PC_Zstd_LIBDIR} - ${PC_Zstd_LIBRARY_DIRS} + ${PC_ZSTD_LIBDIR} + ${PC_ZSTD_LIBRARY_DIRS} ) -if(Zstd_INCLUDE_DIR) - file(READ "${Zstd_INCLUDE_DIR}/zstd.h" _zstd_header) - string(REGEX MATCH ".*define ZSTD_VERSION_MAJOR *([0-9]+).*define ZSTD_VERSION_MINOR *([0-9]+).*define ZSTD_VERSION_RELEASE *([0-9]+)" _zstd_ver "${_zstd_header}") - set(Zstd_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") +if(PC_ZSTD_VERSION) + set(ZSTD_VERSION ${PC_ZSTD_VERSION}) +elseif(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h") + set(_version_regex1 "#[\t ]*define[ \t]+ZSTD_VERSION_MAJOR[ \t]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[ \t]+ZSTD_VERSION_MINOR[ \t]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[ \t]+ZSTD_VERSION_RELEASE[ \t]+([0-9]+).*") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${ZSTD_INCLUDE_DIR}/zstd.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(ZSTD_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Zstd REQUIRED_VARS - Zstd_LIBRARY - Zstd_INCLUDE_DIR - VERSION_VAR Zstd_VERSION + ZSTD_INCLUDE_DIR + ZSTD_LIBRARY + VERSION_VAR + ZSTD_VERSION ) -if(Zstd_FOUND) - set(Zstd_LIBRARIES ${Zstd_LIBRARY}) - set(Zstd_INCLUDE_DIRS ${Zstd_INCLUDE_DIR}) +if(ZSTD_FOUND) + set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) + set(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) endif() -mark_as_advanced(Zstd_INCLUDE_DIRS Zstd_LIBRARIES) +mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index d5439fcc0..d268667ff 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake @@ -21,60 +21,56 @@ # SPDX-License-Identifier: curl # ########################################################################### -#File defines convenience macros for available feature testing +# File defines convenience macros for available feature testing # Check if header file exists and add it to the list. # This macro is intended to be called multiple times with a sequence of # possibly dependent header files. Some headers depend on others to be # compiled correctly. -macro(check_include_file_concat FILE VARIABLE) - check_include_files("${CURL_INCLUDES};${FILE}" ${VARIABLE}) - if(${VARIABLE}) - set(CURL_INCLUDES ${CURL_INCLUDES} ${FILE}) - set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${VARIABLE}") +macro(check_include_file_concat _file _variable) + check_include_files("${CURL_INCLUDES};${_file}" ${_variable}) + if(${_variable}) + set(CURL_INCLUDES ${CURL_INCLUDES} ${_file}) + set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${_variable}") endif() endmacro() # For other curl specific tests, use this macro. -macro(curl_internal_test CURL_TEST) - if(NOT DEFINED "${CURL_TEST}") - set(MACRO_CHECK_FUNCTION_DEFINITIONS - "-D${CURL_TEST} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}") +# Return result in variable: CURL_TEST_OUTPUT +macro(curl_internal_test _curl_test) + if(NOT DEFINED "${_curl_test}") + set(_macro_check_function_definitions + "-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}") if(CMAKE_REQUIRED_LIBRARIES) - set(CURL_TEST_ADD_LIBRARIES + set(_curl_test_add_libraries "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") endif() - message(STATUS "Performing Test ${CURL_TEST}") - try_compile(${CURL_TEST} + message(STATUS "Performing Test ${_curl_test}") + try_compile(${_curl_test} ${CMAKE_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c - CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} - "${CURL_TEST_ADD_LIBRARIES}" - OUTPUT_VARIABLE OUTPUT) - if(${CURL_TEST}) - set(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}") - message(STATUS "Performing Test ${CURL_TEST} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing Test ${CURL_TEST} passed with the following output:\n" - "${OUTPUT}\n") + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" + CMAKE_FLAGS + "-DCOMPILE_DEFINITIONS:STRING=${_macro_check_function_definitions}" + "${_curl_test_add_libraries}" + OUTPUT_VARIABLE CURL_TEST_OUTPUT) + if(${_curl_test}) + set(${_curl_test} 1 CACHE INTERNAL "Curl test") + message(STATUS "Performing Test ${_curl_test} - Success") else() - message(STATUS "Performing Test ${CURL_TEST} - Failed") - set(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing Test ${CURL_TEST} failed with the following output:\n" - "${OUTPUT}\n") + set(${_curl_test} "" CACHE INTERNAL "Curl test") + message(STATUS "Performing Test ${_curl_test} - Failed") endif() endif() endmacro() -macro(optional_dependency DEPENDENCY) - set(CURL_${DEPENDENCY} AUTO CACHE STRING "Build curl with ${DEPENDENCY} support (AUTO, ON or OFF)") - set_property(CACHE CURL_${DEPENDENCY} PROPERTY STRINGS AUTO ON OFF) +macro(optional_dependency _dependency) + set(CURL_${_dependency} "AUTO" CACHE STRING "Build curl with ${_dependency} support (AUTO, ON or OFF)") + set_property(CACHE CURL_${_dependency} PROPERTY STRINGS "AUTO" "ON" "OFF") - if(CURL_${DEPENDENCY} STREQUAL AUTO) - find_package(${DEPENDENCY}) - elseif(CURL_${DEPENDENCY}) - find_package(${DEPENDENCY} REQUIRED) + if(CURL_${_dependency} STREQUAL "AUTO") + find_package(${_dependency}) + elseif(CURL_${_dependency}) + find_package(${_dependency} REQUIRED) endif() endmacro() diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index 7701c0ee9..8936cea01 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -25,17 +25,17 @@ include(CheckCSourceCompiles) include(CheckCSourceRuns) include(CheckTypeSize) -macro(add_header_include check header) - if(${check}) +macro(add_header_include _check _header) + if(${_check}) set(_source_epilogue "${_source_epilogue} - #include <${header}>") + #include <${_header}>") endif() endmacro() set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) - set(CMAKE_EXTRA_INCLUDE_FILES) + unset(CMAKE_EXTRA_INCLUDE_FILES) if(WIN32) set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") @@ -45,6 +45,7 @@ if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) endif() check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) set(HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE}) + set(CMAKE_EXTRA_INCLUDE_FILES "") endif() if(NOT WIN32) @@ -75,37 +76,32 @@ check_c_source_compiles("${_source_epilogue} unset(CMAKE_TRY_COMPILE_TARGET_TYPE) -if(NOT CMAKE_CROSSCOMPILING AND NOT APPLE) +if(NOT APPLE) set(_source_epilogue "#undef inline") add_header_include(HAVE_SYS_POLL_H "sys/poll.h") add_header_include(HAVE_POLL_H "poll.h") - check_c_source_runs("${_source_epilogue} - #include - #include - int main(void) - { - if(0 != poll(0, 0, 10)) { - return 1; /* fail */ - } - else { - /* detect the 10.12 poll() breakage */ - struct timeval before, after; - int rc; - size_t us; - - gettimeofday(&before, NULL); - rc = poll(NULL, 0, 500); - gettimeofday(&after, NULL); - - us = (after.tv_sec - before.tv_sec) * 1000000 + - (after.tv_usec - before.tv_usec); - - if(us < 400000) { - return 1; + if(NOT CMAKE_CROSSCOMPILING) + check_c_source_runs("${_source_epilogue} + #include + int main(void) + { + if(0 != poll(0, 0, 10)) { + return 1; /* fail */ } - } - return 0; - }" HAVE_POLL_FINE) + return 0; + }" HAVE_POLL_FINE) + elseif(UNIX) + check_c_source_compiles("${_source_epilogue} + #include + int main(void) + { + #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L + (void)poll(0, 0, 0); + #else + #error force compilation error + #endif + }" HAVE_POLL_FINE) + endif() endif() # Detect HAVE_GETADDRINFO_THREADSAFE @@ -137,7 +133,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) #ifdef h_errno return 0; #else - force compilation error + #error force compilation error #endif }" HAVE_H_ERRNO) @@ -158,7 +154,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) #elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 700) return 0; #else - force compilation error + #error force compilation error #endif }" HAVE_H_ERRNO_SBS_ISSUE_7) endif() diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake index d1183fe39..a711efaea 100644 --- a/Utilities/cmcurl/CMake/PickyWarnings.cmake +++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake @@ -28,11 +28,21 @@ unset(WPICKY) if(CURL_WERROR AND ((CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND - NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # check_symbol_exists() incompatible with GCC -pedantic-errors in earlier CMake versions + NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # to avoid check_symbol_exists() conflicting with GCC -pedantic-errors CMAKE_C_COMPILER_ID MATCHES "Clang")) set(WPICKY "${WPICKY} -pedantic-errors") endif() +if(APPLE AND + (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) + set(WPICKY "${WPICKY} -Werror=partial-availability") # clang 3.6 appleclang 6.3 +endif() + +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") + set(WPICKY "${WPICKY} -Werror-implicit-function-declaration") # clang 1.0 gcc 2.95 +endif() + if(PICKY_COMPILER) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -94,13 +104,13 @@ if(PICKY_COMPILER) -Wmissing-noreturn # clang 2.7 gcc 4.1 -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) -Wno-system-headers # clang 1.0 gcc 3.0 - # -Wpadded # clang 2.9 gcc 4.1 # Not used because we cannot change public structs + # -Wpadded # clang 2.9 gcc 4.1 # Not used: We cannot change public structs -Wold-style-definition # clang 2.7 gcc 3.4 -Wredundant-decls # clang 2.7 gcc 4.1 -Wsign-conversion # clang 2.9 gcc 4.3 -Wno-error=sign-conversion # FIXME -Wstrict-prototypes # clang 1.0 gcc 3.3 - # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used because this basically disallows default case + # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used: It basically disallows default case -Wtype-limits # clang 2.7 gcc 4.3 -Wunreachable-code # clang 2.7 gcc 4.1 # -Wunused-macros # clang 2.7 gcc 4.1 # Not practical @@ -150,7 +160,7 @@ if(PICKY_COMPILER) if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.4)) list(APPEND WPICKY_ENABLE - -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 12.4 # we have silencing markup for clang 10.0 and above only + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 12.4 # We do silencing for clang 10.0 and above only ) endif() else() # gcc @@ -170,7 +180,7 @@ if(PICKY_COMPILER) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW) list(APPEND WPICKY_ENABLE - -Wno-pedantic-ms-format # gcc 4.5 (mingw-only) + -Wno-pedantic-ms-format # gcc 4.5 (MinGW-only) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8) @@ -196,7 +206,7 @@ if(PICKY_COMPILER) list(APPEND WPICKY_ENABLE -Walloc-zero # gcc 7.0 -Wduplicated-branches # gcc 7.0 - -Wformat-overflow=2 # gcc 7.0 + -Wno-format-overflow # gcc 7.0 -Wformat-truncation=2 # gcc 7.0 -Wimplicit-fallthrough # clang 4.0 gcc 7.0 -Wrestrict # gcc 7.0 @@ -211,20 +221,20 @@ if(PICKY_COMPILER) # - foreach(_CCOPT IN LISTS WPICKY_ENABLE) - set(WPICKY "${WPICKY} ${_CCOPT}") + foreach(_ccopt IN LISTS WPICKY_ENABLE) + set(WPICKY "${WPICKY} ${_ccopt}") endforeach() - foreach(_CCOPT IN LISTS WPICKY_DETECT) + foreach(_ccopt IN LISTS WPICKY_DETECT) # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new # test result in. - string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname) + string(MAKE_C_IDENTIFIER "OPT${_ccopt}" _optvarname) # GCC only warns about unknown -Wno- options if there are also other diagnostic messages, # so test for the positive form instead - string(REPLACE "-Wno-" "-W" _CCOPT_ON "${_CCOPT}") - check_c_compiler_flag(${_CCOPT_ON} ${_optvarname}) + string(REPLACE "-Wno-" "-W" _ccopt_on "${_ccopt}") + check_c_compiler_flag(${_ccopt_on} ${_optvarname}) if(${_optvarname}) - set(WPICKY "${WPICKY} ${_CCOPT}") + set(WPICKY "${WPICKY} ${_ccopt}") endif() endforeach() endif() diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index 082154ff9..317f21c87 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -41,6 +41,10 @@ if(MINGW) set(HAVE_SYS_PARAM_H 1) set(HAVE_SYS_TIME_H 1) set(HAVE_GETTIMEOFDAY 1) + set(HAVE_STRINGS_H 1) # wrapper to string.h + set(HAVE_UTIME_H 1) # wrapper to sys/utime.h + set(HAVE_DIRENT_H 1) + set(HAVE_OPENDIR 1) else() set(HAVE_LIBGEN_H 0) set(HAVE_STRCASECMP 0) @@ -48,6 +52,10 @@ else() set(HAVE_SYS_PARAM_H 0) set(HAVE_SYS_TIME_H 0) set(HAVE_GETTIMEOFDAY 0) + set(HAVE_STRINGS_H 0) + set(HAVE_UTIME_H 0) + set(HAVE_DIRENT_H 0) + set(HAVE_OPENDIR 0) if(MSVC) set(HAVE_UNISTD_H 0) set(HAVE_LOCALE_H 1) @@ -88,6 +96,7 @@ set(HAVE_GETPWUID_R 0) set(HAVE_STRERROR_R 0) set(HAVE_SIGINTERRUPT 0) set(HAVE_PIPE 0) +set(HAVE_EVENTFD 0) set(HAVE_IF_NAMETOINDEX 0) set(HAVE_GETRLIMIT 0) set(HAVE_SETRLIMIT 0) @@ -120,7 +129,7 @@ set(HAVE_IOCTL_SIOCGIFADDR 0) set(HAVE_POLL_H 0) set(HAVE_POLL_FINE 0) set(HAVE_PWD_H 0) -set(HAVE_STRINGS_H 0) # mingw-w64 has it (wrapper to string.h) +set(HAVE_SYS_EVENTFD_H 0) set(HAVE_SYS_FILIO_H 0) set(HAVE_SYS_WAIT_H 0) set(HAVE_SYS_IOCTL_H 0) @@ -135,12 +144,9 @@ set(HAVE_SYS_UN_H 0) set(HAVE_SYS_UTIME_H 1) set(HAVE_TERMIOS_H 0) set(HAVE_TERMIO_H 0) -set(HAVE_UTIME_H 0) # mingw-w64 has it (wrapper to sys/utime.h) - -set(HAVE_DIRENT_H 0) -set(HAVE_OPENDIR 0) +set(HAVE_LINUX_TCP_H 0) -set(HAVE_FSEEKO 0) +set(HAVE_FSEEKO 0) # mingw-w64 2.0.0 and newer has it set(HAVE__FSEEKI64 1) set(HAVE_SOCKET 1) set(HAVE_SELECT 1) @@ -159,7 +165,6 @@ set(HAVE_GMTIME_R 0) set(HAVE_GETHOSTBYNAME_R 0) set(HAVE_SIGNAL 1) set(HAVE_SIGACTION 0) -set(HAVE_LINUX_TCP_H 0) set(HAVE_GLIBC_STRERROR_R 0) set(HAVE_MACH_ABSOLUTE_TIME 0) set(HAVE_GETIFADDRS 0) @@ -171,7 +176,6 @@ set(HAVE_IOCTLSOCKET_FIONBIO 1) set(HAVE_IOCTL_FIONBIO 0) set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) set(HAVE_POSIX_STRERROR_R 0) -set(HAVE_BUILTIN_AVAILABLE 0) set(HAVE_MSG_NOSIGNAL 0) set(HAVE_STRUCT_TIMEVAL 1) set(HAVE_STRUCT_SOCKADDR_STORAGE 1) diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake index 84a40f4e3..0ecfa3129 100644 --- a/Utilities/cmcurl/CMake/Utilities.cmake +++ b/Utilities/cmcurl/CMake/Utilities.cmake @@ -24,12 +24,12 @@ # File containing various utilities # Returns number of arguments that evaluate to true -function(count_true output_count_var) +function(count_true _output_count_var) set(lst_len 0) foreach(option_var IN LISTS ARGN) if(${option_var}) math(EXPR lst_len "${lst_len} + 1") endif() endforeach() - set(${output_count_var} ${lst_len} PARENT_SCOPE) + set(${_output_count_var} ${lst_len} PARENT_SCOPE) endfunction() diff --git a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in index 3e0742d1e..4df85542d 100644 --- a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in +++ b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in @@ -30,20 +30,20 @@ if(NOT DEFINED CMAKE_INSTALL_PREFIX) endif() message(${CMAKE_INSTALL_PREFIX}) -file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) -string(REGEX REPLACE "\n" ";" files "${files}") -foreach(file ${files}) - message(STATUS "Uninstalling $ENV{DESTDIR}${file}") - if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" _files) +string(REGEX REPLACE "\n" ";" _files "${_files}") +foreach(_file ${_files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${_file}") + if(IS_SYMLINK "$ENV{DESTDIR}${_file}" OR EXISTS "$ENV{DESTDIR}${_file}") exec_program( - "@CMAKE_COMMAND@" ARGS "-E rm -f \"$ENV{DESTDIR}${file}\"" + "@CMAKE_COMMAND@" ARGS "-E rm -f \"$ENV{DESTDIR}${_file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval - ) + ) if(NOT "${rm_retval}" STREQUAL 0) - message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${_file}") endif() else() - message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + message(STATUS "File $ENV{DESTDIR}${_file} does not exist.") endif() endforeach() diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index 9adb96e0a..7dc1f99f0 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -23,6 +23,14 @@ ########################################################################### @PACKAGE_INIT@ +if(NOT DEFINED CURL_USE_PKGCONFIG) + if(UNIX OR (MSVC AND VCPKG_TOOLCHAIN)) # Keep in sync with root CMakeLists.txt + set(CURL_USE_PKGCONFIG ON) + else() + set(CURL_USE_PKGCONFIG OFF) + endif() +endif() + include(CMakeFindDependencyMacro) if(@USE_OPENSSL@) find_dependency(OpenSSL @OPENSSL_VERSION_MAJOR@) @@ -38,3 +46,7 @@ check_required_components("@PROJECT_NAME@") if(NOT TARGET @PROJECT_NAME@::libcurl) add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@) endif() + +# For compatibility with CMake's FindCURL.cmake +set(CURL_LIBRARIES @PROJECT_NAME@::libcurl) +set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index b766aee42..ef6c269e8 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -6,16 +6,20 @@ set(BUILD_SHARED_LIBS OFF) set(BUILD_STATIC_LIBS ON) set(BUILD_STATIC_CURL OFF) set(CURL_USE_BEARSSL OFF) +set(CURL_USE_GSASL OFF) set(CURL_USE_GSSAPI OFF) set(CURL_USE_LIBPSL OFF) set(CURL_USE_LIBSSH2 OFF) set(CURL_USE_LIBSSH OFF) +set(CURL_USE_LIBUV OFF) set(CURL_USE_MBEDTLS OFF) set(CURL_USE_NSS OFF) set(CURL_USE_OPENLDAP OFF) set(CURL_USE_OPENSSL "${CMAKE_USE_OPENSSL}") +set(CURL_USE_PKGCONFIG OFF) set(CURL_USE_SCHANNEL OFF) set(CURL_USE_SECTRANSP OFF) +set(CURL_USE_WOLFSSH OFF) set(CURL_USE_WOLFSSL OFF) set(CURL_BROTLI OFF) set(CURL_DISABLE_ALTSVC ON) @@ -53,6 +57,7 @@ set(CURL_DISABLE_POP3 ON CACHE INTERNAL "Disable curl pop3 protocol?") set(CURL_DISABLE_PROGRESS_METER OFF) set(CURL_DISABLE_PROXY OFF CACHE INTERNAL "Do not disable curl proxy") set(CURL_DISABLE_RTSP ON CACHE INTERNAL "Disable curl rtsp protocol?") +set(CURL_DISABLE_SHA512_256 OFF) set(CURL_DISABLE_SHUFFLE_DNS OFF) set(CURL_DISABLE_SMB OFF) set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?") @@ -88,7 +93,7 @@ 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(PICKY_COMPILER OFF) set(SHARE_LIB_OBJECT OFF) set(USE_ECH OFF) set(USE_HTTPSRR OFF) @@ -186,11 +191,11 @@ endif() # by Tetetest and Sukender (Benoit Neil) # 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: +# 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: @@ -198,12 +203,34 @@ endif() # HAVE_OPENSSL_SRP: `SSL_CTX_set_srp_username` present in OpenSSL/wolfSSL # HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS # HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL -# HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE +# HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in quiche # HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL # # 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. +# to ON or OFF), the symbol detection is skipped. If the variable is +# NOT DEFINED, the symbol detection is performed. + +if(0) # This code not needed for building within CMake. +cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) +message(STATUS "Using CMake version ${CMAKE_VERSION}") + +# Collect command-line arguments for buildinfo.txt. +# Must reside at the top of the script to work as expected. +get_cmake_property(_cache_vars CACHE_VARIABLES) +unset(_cmake_args) +foreach(_cache_var ${_cache_vars}) + get_property(_cache_var_helpstring CACHE ${_cache_var} PROPERTY HELPSTRING) + if(_cache_var_helpstring STREQUAL "No help, variable specified on the command line.") + get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) + if(_cache_var_type STREQUAL "UNINITIALIZED") + set(_cache_var_type) + else() + set(_cache_var_type ":${_cache_var_type}") + endif() + set(_cmake_args "${_cmake_args} -D${_cache_var}${_cache_var_type}=\"${${_cache_var}}\"") + endif() +endforeach() +endif() set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) @@ -213,84 +240,148 @@ include(CheckCCompilerFlag) project(CURL C) -file(STRINGS ${CURL_SOURCE_DIR}/include/curl/curlver.h CURL_VERSION_H_CONTENTS REGEX "#define LIBCURL_VERSION( |_NUM )") -string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" - CURL_VERSION ${CURL_VERSION_H_CONTENTS}) +unset(_target_flags) +if(APPLE) + set(_target_flags "${_target_flags} APPLE") +endif() +if(UNIX) + set(_target_flags "${_target_flags} UNIX") +endif() +if(WIN32) + set(_target_flags "${_target_flags} WIN32") +endif() +if(CYGWIN) + set(_target_flags "${_target_flags} CYGWIN") +endif() +if(MSYS) + set(_target_flags "${_target_flags} MSYS") +endif() +if(CMAKE_COMPILER_IS_GNUCC) + set(_target_flags "${_target_flags} GCC") +endif() +if(MINGW) + set(_target_flags "${_target_flags} MINGW") +endif() +if(MSVC) + set(_target_flags "${_target_flags} MSVC") +endif() +if(VCPKG_TOOLCHAIN) + set(_target_flags "${_target_flags} VCPKG") +endif() +if(CMAKE_CROSSCOMPILING) + set(_target_flags "${_target_flags} CROSS") +endif() +if(0) # This code not needed for building within CMake. +message(STATUS "CMake platform flags:${_target_flags}") +endif() + +if(CMAKE_CROSSCOMPILING) + message(STATUS "Cross-compiling: " + "${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR} -> " + "${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}") +endif() + +function(curl_dumpvars) # Dump all defined variables with their values + message("::group::CMake Variable Dump") + get_cmake_property(_vars VARIABLES) + foreach(_var ${_vars}) + message("${_var} = ${${_var}}") + endforeach() + message("::endgroup::") +endfunction() + +file(STRINGS "${CURL_SOURCE_DIR}/include/curl/curlver.h" _curl_version_h_contents REGEX "#define LIBCURL_VERSION( |_NUM )") +string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" CURL_VERSION ${_curl_version_h_contents}) string(REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) -string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" - CURL_VERSION_NUM ${CURL_VERSION_H_CONTENTS}) +string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" CURL_VERSION_NUM ${_curl_version_h_contents}) string(REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM}) +unset(_curl_version_h_contents) - -# Setup package meta-data -# SET(PACKAGE "curl") if(0) # This code not needed for building within CMake. message(STATUS "curl version=[${CURL_VERSION}]") endif() -# SET(PACKAGE_TARNAME "curl") -# SET(PACKAGE_NAME "curl") -# SET(PACKAGE_VERSION "-") -# SET(PACKAGE_STRING "curl-") -# SET(PACKAGE_BUGREPORT "a suitable curl mailing list => https://curl.se/mail/") -set(OPERATING_SYSTEM "${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) +include_directories("${CURL_SOURCE_DIR}/include") -set(CMAKE_UNITY_BUILD_BATCH_SIZE 0) +if(NOT DEFINED CMAKE_UNITY_BUILD_BATCH_SIZE) + set(CMAKE_UNITY_BUILD_BATCH_SIZE 0) +endif() option(CURL_WERROR "Turn compiler warnings into errors" OFF) option(PICKY_COMPILER "Enable picky compiler options" ON) -option(BUILD_CURL_EXE "Set to ON to build curl executable." ON) +option(BUILD_CURL_EXE "Build curl executable" ON) option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(BUILD_STATIC_LIBS "Build static libraries" OFF) option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF) -option(ENABLE_ARES "Set to ON to enable c-ares support" OFF) -option(CURL_DISABLE_INSTALL "Set to ON to disable installation targets" OFF) +option(ENABLE_ARES "Enable c-ares support" OFF) +option(CURL_DISABLE_INSTALL "Disable installation targets" OFF) if(WIN32) - option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF) - option(ENABLE_UNICODE "Set to ON to use the Unicode version of the Windows API functions" OFF) + option(CURL_STATIC_CRT "Build libcurl with static CRT on Windows (/MT)" OFF) + if(CURL_STATIC_CRT) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") + endif() + + option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF) + if(ENABLE_UNICODE) + add_definitions("-DUNICODE" "-D_UNICODE") + if(MINGW) + add_compile_options("-municode") + endif() + endif() + 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}) - list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}) + add_definitions("-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") endif() endif() - if(ENABLE_UNICODE) - add_definitions(-DUNICODE -D_UNICODE) - if(MINGW) - add_compile_options(-municode) - endif() + + # 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]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE ".*_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") + string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits + string(TOLOWER "${CURL_TEST_OUTPUT}" HAVE_WIN32_WINNT) + message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") endif() + # Avoid storing HAVE_WIN32_WINNT in CMake cache + unset(HAVE_WIN32_WINNT CACHE) endif() -option(CURL_LTO "Turn on compiler Link Time Optimizations" OFF) +option(CURL_LTO "Enable compiler Link Time Optimizations" OFF) if(0) # This code not needed for building within CMake. -cmake_dependent_option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup" - ON "NOT ENABLE_ARES" - OFF) +cmake_dependent_option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" + ON "NOT ENABLE_ARES" + OFF) + +include(PickyWarnings) endif() -option(ENABLE_DEBUG "Set to ON to enable curl debug features" OFF) -option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OFF) +option(ENABLE_DEBUG "Enable curl debug features" OFF) +option(ENABLE_CURLDEBUG "Enable TrackMemory feature" ${ENABLE_DEBUG}) -include(PickyWarnings) +if(MSVC) + set(ENABLE_CURLDEBUG OFF) # FIXME: TrackMemory + MSVC fails test 558 and 1330. Tested with static build, Debug mode. +endif() if(ENABLE_DEBUG) - # DEBUGBUILD will be defined only for Debug builds - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$:DEBUGBUILD>) - set(ENABLE_CURLDEBUG ON) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "DEBUGBUILD") endif() if(ENABLE_CURLDEBUG) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS CURLDEBUG) + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURLDEBUG") endif() if(0) # This code not needed for building within CMake. @@ -312,124 +403,145 @@ elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS) set(BUILD_STATIC_CURL OFF) endif() -# lib flavour selected for curl tool +# Lib flavour selected for curl tool if(BUILD_STATIC_CURL) set(LIB_SELECTED_FOR_EXE ${LIB_STATIC}) else() set(LIB_SELECTED_FOR_EXE ${LIB_SHARED}) endif() -# lib flavour selected for example and test programs. +# Lib flavour selected for example and test programs. if(BUILD_SHARED_LIBS) set(LIB_SELECTED ${LIB_SHARED}) else() set(LIB_SELECTED ${LIB_STATIC}) endif() -# initialize CURL_LIBS +# Override to force-disable or force-enable the use of pkg-config. +if(UNIX OR (MSVC AND VCPKG_TOOLCHAIN)) # Keep in sync with CMake/curl-config.cmake.in + set(_curl_use_pkgconfig_default ON) +else() + set(_curl_use_pkgconfig_default OFF) +endif() +option(CURL_USE_PKGCONFIG "Enable pkg-config to detect dependencies" ${_curl_use_pkgconfig_default}) + +# Initialize CURL_LIBS set(CURL_LIBS "") +set(CURL_LIBDIRS "") +set(LIBCURL_PC_REQUIRES_PRIVATE "") if(ENABLE_ARES) set(USE_ARES 1) - find_package(CARES REQUIRED) - list(APPEND CURL_LIBS ${CARES_LIBRARY}) + find_package(Cares REQUIRED) + list(APPEND CURL_LIBS ${CARES_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libcares") + add_definitions("-DCARES_NO_DEPRECATED") # Ignore c-ares deprecation warnings endif() if(0) # This code not needed for building within CMake. include(CurlSymbolHiding) endif() -option(CURL_ENABLE_EXPORT_TARGET "to enable cmake export target" ON) +option(CURL_ENABLE_EXPORT_TARGET "Enable CMake export target" ON) mark_as_advanced(CURL_ENABLE_EXPORT_TARGET) -option(CURL_DISABLE_ALTSVC "disables alt-svc support" OFF) +option(CURL_DISABLE_ALTSVC "Disable alt-svc support" OFF) mark_as_advanced(CURL_DISABLE_ALTSVC) -option(CURL_DISABLE_SRP "disables TLS-SRP support" OFF) +option(CURL_DISABLE_SRP "Disable TLS-SRP support" OFF) mark_as_advanced(CURL_DISABLE_SRP) -option(CURL_DISABLE_COOKIES "disables cookies support" OFF) +option(CURL_DISABLE_COOKIES "Disable cookies support" OFF) mark_as_advanced(CURL_DISABLE_COOKIES) -option(CURL_DISABLE_BASIC_AUTH "disables Basic authentication" OFF) +option(CURL_DISABLE_BASIC_AUTH "Disable Basic authentication" OFF) mark_as_advanced(CURL_DISABLE_BASIC_AUTH) -option(CURL_DISABLE_BEARER_AUTH "disables Bearer authentication" OFF) +option(CURL_DISABLE_BEARER_AUTH "Disable Bearer authentication" OFF) mark_as_advanced(CURL_DISABLE_BEARER_AUTH) -option(CURL_DISABLE_DIGEST_AUTH "disables Digest authentication" OFF) +option(CURL_DISABLE_DIGEST_AUTH "Disable Digest authentication" OFF) mark_as_advanced(CURL_DISABLE_DIGEST_AUTH) -option(CURL_DISABLE_KERBEROS_AUTH "disables Kerberos authentication" OFF) +option(CURL_DISABLE_KERBEROS_AUTH "Disable Kerberos authentication" OFF) mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH) -option(CURL_DISABLE_NEGOTIATE_AUTH "disables negotiate authentication" OFF) +option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) -option(CURL_DISABLE_AWS "disables AWS-SIG4" OFF) +option(CURL_DISABLE_AWS "Disable AWS-SIG4" OFF) mark_as_advanced(CURL_DISABLE_AWS) -option(CURL_DISABLE_DICT "disables DICT" OFF) +option(CURL_DISABLE_DICT "Disable DICT" OFF) mark_as_advanced(CURL_DISABLE_DICT) -option(CURL_DISABLE_DOH "disables DNS-over-HTTPS" OFF) +option(CURL_DISABLE_DOH "Disable DNS-over-HTTPS" OFF) mark_as_advanced(CURL_DISABLE_DOH) -option(CURL_DISABLE_FILE "disables FILE" OFF) +option(CURL_DISABLE_FILE "Disable FILE" OFF) mark_as_advanced(CURL_DISABLE_FILE) if(0) # This code not needed for building within CMake. -cmake_dependent_option(CURL_DISABLE_FORM_API "disables form api" OFF - "NOT CURL_DISABLE_MIME" ON) +cmake_dependent_option(CURL_DISABLE_FORM_API "Disable form-api" + OFF "NOT CURL_DISABLE_MIME" + ON) mark_as_advanced(CURL_DISABLE_FORM_API) endif() -option(CURL_DISABLE_FTP "disables FTP" OFF) +option(CURL_DISABLE_FTP "Disable FTP" OFF) mark_as_advanced(CURL_DISABLE_FTP) -option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing options to curl_easy_setopt" OFF) +option(CURL_DISABLE_GETOPTIONS "Disable curl_easy_options API for existing options to curl_easy_setopt" OFF) mark_as_advanced(CURL_DISABLE_GETOPTIONS) -option(CURL_DISABLE_GOPHER "disables Gopher" OFF) +option(CURL_DISABLE_GOPHER "Disable Gopher" OFF) mark_as_advanced(CURL_DISABLE_GOPHER) -option(CURL_DISABLE_HEADERS_API "disables headers-api support" OFF) +option(CURL_DISABLE_HEADERS_API "Disable headers-api support" OFF) mark_as_advanced(CURL_DISABLE_HEADERS_API) -option(CURL_DISABLE_HSTS "disables HSTS support" OFF) +option(CURL_DISABLE_HSTS "Disable HSTS support" OFF) mark_as_advanced(CURL_DISABLE_HSTS) -option(CURL_DISABLE_HTTP "disables HTTP" OFF) +option(CURL_DISABLE_HTTP "Disable HTTP" OFF) mark_as_advanced(CURL_DISABLE_HTTP) -option(CURL_DISABLE_HTTP_AUTH "disables all HTTP authentication methods" OFF) +option(CURL_DISABLE_HTTP_AUTH "Disable all HTTP authentication methods" OFF) mark_as_advanced(CURL_DISABLE_HTTP_AUTH) -option(CURL_DISABLE_IMAP "disables IMAP" OFF) +option(CURL_DISABLE_IMAP "Disable IMAP" OFF) mark_as_advanced(CURL_DISABLE_IMAP) -option(CURL_DISABLE_LDAP "disables LDAP" OFF) +option(CURL_DISABLE_LDAP "Disable LDAP" OFF) mark_as_advanced(CURL_DISABLE_LDAP) -option(CURL_DISABLE_LDAPS "disables LDAPS" OFF) +option(CURL_DISABLE_LDAPS "Disable LDAPS" ${CURL_DISABLE_LDAP}) mark_as_advanced(CURL_DISABLE_LDAPS) -option(CURL_DISABLE_LIBCURL_OPTION "disables --libcurl option from the curl tool" OFF) +option(CURL_DISABLE_LIBCURL_OPTION "Disable --libcurl option from the curl tool" OFF) mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION) -option(CURL_DISABLE_MIME "disables MIME support" OFF) +option(CURL_DISABLE_MIME "Disable MIME support" OFF) mark_as_advanced(CURL_DISABLE_MIME) -option(CURL_DISABLE_MQTT "disables MQTT" OFF) +option(CURL_DISABLE_MQTT "Disable MQTT" OFF) mark_as_advanced(CURL_DISABLE_BINDLOCAL) -option(CURL_DISABLE_BINDLOCAL "disables local binding support" OFF) +option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) mark_as_advanced(CURL_DISABLE_MQTT) -option(CURL_DISABLE_NETRC "disables netrc parser" OFF) +option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) -option(CURL_DISABLE_NTLM "disables NTLM support" OFF) +option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) mark_as_advanced(CURL_DISABLE_NTLM) -option(CURL_DISABLE_PARSEDATE "disables date parsing" OFF) +option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF) mark_as_advanced(CURL_DISABLE_PARSEDATE) -option(CURL_DISABLE_POP3 "disables POP3" OFF) +option(CURL_DISABLE_POP3 "Disable POP3" OFF) mark_as_advanced(CURL_DISABLE_POP3) -option(CURL_DISABLE_PROGRESS_METER "disables built-in progress meter" OFF) +option(CURL_DISABLE_PROGRESS_METER "Disable built-in progress meter" OFF) mark_as_advanced(CURL_DISABLE_PROGRESS_METER) -option(CURL_DISABLE_PROXY "disables proxy support" OFF) +option(CURL_DISABLE_PROXY "Disable proxy support" OFF) mark_as_advanced(CURL_DISABLE_PROXY) -option(CURL_DISABLE_RTSP "disables RTSP" OFF) +option(CURL_DISABLE_RTSP "Disable RTSP" OFF) +mark_as_advanced(CURL_DISABLE_SHA512_256) +option(CURL_DISABLE_SHA512_256 "Disable SHA-512/256 hash algorithm" OFF) mark_as_advanced(CURL_DISABLE_RTSP) -option(CURL_DISABLE_SHUFFLE_DNS "disables shuffle DNS feature" OFF) +option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF) mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS) -option(CURL_DISABLE_SMB "disables SMB" OFF) +option(CURL_DISABLE_SMB "Disable SMB" OFF) mark_as_advanced(CURL_DISABLE_SMB) -option(CURL_DISABLE_SMTP "disables SMTP" OFF) +option(CURL_DISABLE_SMTP "Disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) -option(CURL_DISABLE_SOCKETPAIR "disables use of socketpair for curl_multi_poll" OFF) +option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll" OFF) mark_as_advanced(CURL_DISABLE_SOCKETPAIR) -option(CURL_DISABLE_TELNET "disables Telnet" OFF) +option(CURL_DISABLE_TELNET "Disable Telnet" OFF) mark_as_advanced(CURL_DISABLE_TELNET) -option(CURL_DISABLE_TFTP "disables TFTP" OFF) +option(CURL_DISABLE_TFTP "Disable TFTP" OFF) mark_as_advanced(CURL_DISABLE_TFTP) -option(CURL_DISABLE_VERBOSE_STRINGS "disables verbose strings" OFF) +option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) +if(CURL_DISABLE_HTTP) + set(CURL_DISABLE_RTSP ON) + set(CURL_DISABLE_ALTSVC ON) + set(CURL_DISABLE_HSTS ON) +endif() + # Corresponds to HTTP_ONLY in lib/curl_setup.h -option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF) +option(HTTP_ONLY "Disable all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF) mark_as_advanced(HTTP_ONLY) if(HTTP_ONLY) @@ -449,25 +561,29 @@ if(HTTP_ONLY) set(CURL_DISABLE_TFTP ON) endif() -option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON) +if(WINDOWS_STORE) + set(CURL_DISABLE_TELNET ON) # telnet code needs fixing to compile for UWP. +endif() + +option(ENABLE_IPV6 "Enable IPv6 support" ON) mark_as_advanced(ENABLE_IPV6) if(ENABLE_IPV6 AND NOT WIN32) include(CheckStructHasMember) - check_struct_has_member("struct sockaddr_in6" sin6_addr "netinet/in.h" + check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR) - check_struct_has_member("struct sockaddr_in6" sin6_scope_id "netinet/in.h" + check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR) message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support") # Force the feature off as this name is used as guard macro... - set(ENABLE_IPV6 OFF - CACHE BOOL "Define if you want to enable IPv6 support" FORCE) + set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE) endif() - if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT ENABLE_ARES) - set(use_core_foundation_and_core_services ON) + if(APPLE AND NOT ENABLE_ARES) + set(_use_core_foundation_and_core_services ON) find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration") + mark_as_advanced(SYSTEMCONFIGURATION_FRAMEWORK) if(NOT SYSTEMCONFIGURATION_FRAMEWORK) message(FATAL_ERROR "SystemConfiguration framework not found") endif() @@ -482,9 +598,9 @@ endif() if(0) # This code not needed for building within CMake. find_package(Perl) -option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON) -option(BUILD_MISC_DOCS "to build misc man pages (e.g. curl-config and mk-ca-bundle)" ON) -option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" ON) +option(BUILD_LIBCURL_DOCS "Build libcurl man pages" ON) +option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)" ON) +option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON) if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) if(PERL_FOUND) @@ -496,25 +612,19 @@ if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) endif() endif() -if(CURL_STATIC_CRT) - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") -endif() - # Disable warnings on Borland to avoid changing 3rd party code. if(BORLAND) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w-") endif() # If we are on AIX, do the _ALL_SOURCE magic -if(${CMAKE_SYSTEM_NAME} MATCHES AIX) - set(_ALL_SOURCE 1) +if(CMAKE_SYSTEM_NAME STREQUAL "AIX") + add_definitions("-D_ALL_SOURCE") endif() # If we are on Haiku, make sure that the network library is brought in. -if(${CMAKE_SYSTEM_NAME} MATCHES Haiku) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lnetwork") +if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + list(APPEND CURL_LIBS "network") endif() # Include all the necessary files for macros @@ -527,7 +637,7 @@ include(CheckSymbolExists) include(CheckTypeSize) include(CheckCSourceCompiles) -# On windows preload settings +# Preload settings on Windows if(WIN32) include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) endif() @@ -539,7 +649,7 @@ if(ENABLE_THREADED_RESOLVER) find_package(Threads REQUIRED) set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) - set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + list(APPEND CURL_LIBS ${CMAKE_THREAD_LIBS_INIT}) endif() endif() @@ -549,18 +659,18 @@ if(HAVE_LIBSOCKET) set(CURL_LIBS "socket;${CURL_LIBS}") endif() -check_function_exists(gethostname HAVE_GETHOSTNAME) +check_function_exists("gethostname" HAVE_GETHOSTNAME) if(WIN32) list(APPEND CURL_LIBS "ws2_32" "bcrypt") endif() if(0) # This code not needed for building within CMake. -# check SSL libraries +# Check SSL libraries option(CURL_ENABLE_SSL "Enable SSL support" ON) if(CURL_DEFAULT_SSL_BACKEND) - set(valid_default_ssl_backend FALSE) + set(_valid_default_ssl_backend FALSE) endif() if(APPLE) @@ -574,34 +684,44 @@ cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_EN cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF) -set(openssl_default ON) +set(_openssl_default ON) if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL) - set(openssl_default OFF) + set(_openssl_default OFF) +endif() +cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${_openssl_default} CURL_ENABLE_SSL OFF) +option(USE_OPENSSL_QUIC "Use OpenSSL and nghttp3 libraries for HTTP/3 support" OFF) +if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) + message(WARNING "OpenSSL QUIC has been requested, but without enabling OpenSSL. Will not enable QUIC.") + set(USE_OPENSSL_QUIC OFF) endif() -cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${openssl_default} CURL_ENABLE_SSL OFF) option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) endif() -count_true(enabled_ssl_options_count +count_true(_enabled_ssl_options_count CURL_USE_SCHANNEL CURL_USE_SECTRANSP CURL_USE_OPENSSL CURL_USE_MBEDTLS CURL_USE_BEARSSL CURL_USE_WOLFSSL + CURL_USE_GNUTLS + CURL_USE_RUSTLS ) -if(enabled_ssl_options_count GREATER "1") +if(_enabled_ssl_options_count GREATER 1) set(CURL_WITH_MULTI_SSL ON) +elseif(_enabled_ssl_options_count EQUAL 0) + set(CURL_DISABLE_HSTS ON) endif() if(CURL_USE_SCHANNEL) - set(SSL_ENABLED ON) - set(USE_SCHANNEL ON) # Windows native SSL/TLS support - set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI + set(_ssl_enabled ON) + set(USE_SCHANNEL ON) # Windows native SSL/TLS support + set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "schannel") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) endif() endif() if(CURL_WINDOWS_SSPI) @@ -609,25 +729,32 @@ if(CURL_WINDOWS_SSPI) endif() if(CURL_USE_SECTRANSP) - set(use_core_foundation_and_core_services ON) + set(_use_core_foundation_and_core_services ON) find_library(SECURITY_FRAMEWORK "Security") + mark_as_advanced(SECURITY_FRAMEWORK) if(NOT SECURITY_FRAMEWORK) message(FATAL_ERROR "Security framework not found") endif() - set(SSL_ENABLED ON) + set(_ssl_enabled ON) set(USE_SECTRANSP ON) list(APPEND CURL_LIBS "-framework Security") if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) + endif() + + if(0) # This code not needed for building within CMake. + message(WARNING "Secure Transport does not support TLS 1.3.") endif() endif() -if(use_core_foundation_and_core_services) +if(_use_core_foundation_and_core_services) find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation") + mark_as_advanced(COREFOUNDATION_FRAMEWORK) find_library(CORESERVICES_FRAMEWORK "CoreServices") + mark_as_advanced(CORESERVICES_FRAMEWORK) if(NOT COREFOUNDATION_FRAMEWORK) message(FATAL_ERROR "CoreFoundation framework not found") @@ -636,7 +763,7 @@ if(use_core_foundation_and_core_services) message(FATAL_ERROR "CoreServices framework not found") endif() - list(APPEND CURL_LIBS "-framework CoreFoundation -framework CoreServices") + list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework CoreServices") endif() if(CURL_USE_OPENSSL) @@ -646,21 +773,24 @@ if(CURL_USE_OPENSSL) "Could not find OpenSSL. Install an OpenSSL development package or " "configure CMake with -DCMAKE_USE_OPENSSL=OFF to build without OpenSSL.") endif() - set(SSL_ENABLED ON) + set(_ssl_enabled ON) set(USE_OPENSSL ON) + list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "openssl") if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) endif() + set(_curl_ca_bundle_supported TRUE) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) if(NOT DEFINED HAVE_BORINGSSL) - check_symbol_exists(OPENSSL_IS_BORINGSSL "openssl/base.h" HAVE_BORINGSSL) + check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) endif() if(NOT DEFINED HAVE_AWSLC) - check_symbol_exists(OPENSSL_IS_AWSLC "openssl/base.h" HAVE_AWSLC) + check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() # Optionally build with a specific CA cert bundle. @@ -675,61 +805,101 @@ endif() if(CURL_USE_MBEDTLS) find_package(MbedTLS REQUIRED) - set(SSL_ENABLED ON) + set(_ssl_enabled ON) set(USE_MBEDTLS ON) list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mbedtls") include_directories(${MBEDTLS_INCLUDE_DIRS}) if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) endif() + set(_curl_ca_bundle_supported TRUE) endif() if(CURL_USE_BEARSSL) find_package(BearSSL REQUIRED) - set(SSL_ENABLED ON) + set(_ssl_enabled ON) set(USE_BEARSSL ON) - list(APPEND CURL_LIBS ${BEARSSL_LIBRARY}) + list(APPEND CURL_LIBS ${BEARSSL_LIBRARIES}) include_directories(${BEARSSL_INCLUDE_DIRS}) if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) + + if(0) # This code not needed for building within CMake. + message(WARNING "BearSSL does not support TLS 1.3.") endif() endif() if(CURL_USE_WOLFSSL) find_package(WolfSSL REQUIRED) - set(SSL_ENABLED ON) + set(_ssl_enabled ON) set(USE_WOLFSSL ON) - list(APPEND CURL_LIBS ${WolfSSL_LIBRARIES}) - include_directories(${WolfSSL_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${WOLFSSL_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "wolfssl") + include_directories(${WOLFSSL_INCLUDE_DIRS}) if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) endif() + set(_curl_ca_bundle_supported TRUE) endif() if(CURL_USE_GNUTLS) - find_package(GnuTLS REQUIRED) - set(SSL_ENABLED ON) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(GNUTLS "gnutls") + if(GNUTLS_FOUND) + set(GNUTLS_LIBRARIES ${GNUTLS_LINK_LIBRARIES}) + endif() + endif() + if(NOT GNUTLS_FOUND) + find_package(GnuTLS REQUIRED) + endif() + find_package(Nettle REQUIRED) + set(_ssl_enabled ON) set(USE_GNUTLS ON) - list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} "nettle") - include_directories(${GNUTLS_INCLUDE_DIRS}) + list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${NETTLE_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" "nettle") + include_directories(${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS}) + link_directories(${NETTLE_LIBRARY_DIRS}) + if(NETTLE_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NETTLE_CFLAGS}") + endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls") - set(valid_default_ssl_backend TRUE) + set(_valid_default_ssl_backend TRUE) endif() + set(_curl_ca_bundle_supported TRUE) if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP) cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES}) - check_symbol_exists(gnutls_srp_verifier "gnutls/gnutls.h" HAVE_GNUTLS_SRP) + check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP) cmake_pop_check_state() endif() endif() -if(CURL_DEFAULT_SSL_BACKEND AND NOT valid_default_ssl_backend) +if(CURL_USE_RUSTLS) + find_package(Rustls REQUIRED) + set(_ssl_enabled ON) + set(USE_RUSTLS ON) + list(APPEND CURL_LIBS ${RUSTLS_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "rustls") + include_directories(${RUSTLS_INCLUDE_DIRS}) + + if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls") + set(_valid_default_ssl_backend TRUE) + endif() + set(_curl_ca_bundle_supported TRUE) +endif() + +if(CURL_DEFAULT_SSL_BACKEND AND NOT _valid_default_ssl_backend) message(FATAL_ERROR "CURL_DEFAULT_SSL_BACKEND '${CURL_DEFAULT_SSL_BACKEND}' not enabled.") endif() @@ -743,50 +913,45 @@ if(ZLIB_FOUND) set(HAVE_LIBZ ON) set(USE_ZLIB ON) - # Depend on ZLIB via imported targets if supported by the running - # version of CMake. This allows our dependents to get our dependencies - # transitively. - if(NOT CMAKE_VERSION VERSION_LESS 3.4) - if(CMAKE_USE_SYSTEM_ZLIB) - list(APPEND CURL_LIBS ZLIB::ZLIB) - else() - list(APPEND CURL_LIBS cmzlib) - endif() + # Depend on ZLIB via imported targets. This allows our dependents to + # get our dependencies transitively. + if(CMAKE_USE_SYSTEM_ZLIB) + list(APPEND CURL_LIBS ZLIB::ZLIB) else() - list(APPEND CURL_LIBS ${ZLIB_LIBRARIES}) - include_directories(${ZLIB_INCLUDE_DIRS}) - list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) + list(APPEND CURL_LIBS cmzlib) endif() + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "zlib") endif() -option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF) +option(CURL_BROTLI "Use brotli" OFF) set(HAVE_BROTLI OFF) if(CURL_BROTLI) find_package(Brotli REQUIRED) if(BROTLI_FOUND) set(HAVE_BROTLI ON) - set(CURL_LIBS "${BROTLI_LIBRARIES};${CURL_LIBS}") # For 'ld' linker. Emulate `list(PREPEND ...)` to stay compatible with \n") + set(_include_string "") + foreach(_header IN LISTS _header_list) + set(_include_string "${_include_string}#include <${_header}>\n") endforeach() - list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1") list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}") if(HAVE_LIBLBER) @@ -1027,7 +1202,7 @@ if(NOT CURL_DISABLE_LDAP) endif() check_c_source_compiles(" - ${_INCLUDE_STRING} + ${_include_string} int main(int argc, char ** argv) { BerValue *bvp = NULL; @@ -1041,8 +1216,8 @@ if(NOT CURL_DISABLE_LDAP) set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DNEED_LBER_H") endif() - check_function_exists(ldap_url_parse HAVE_LDAP_URL_PARSE) - check_function_exists(ldap_init_fd HAVE_LDAP_INIT_FD) + check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE) + check_function_exists("ldap_init_fd" HAVE_LDAP_INIT_FD) unset(CMAKE_REQUIRED_LIBRARIES) @@ -1067,23 +1242,13 @@ if(CURL_DISABLE_LDAP) endif() endif() -# Check for idn2 -option(USE_LIBIDN2 "Use libidn2 for IDN support" ON) -if(USE_LIBIDN2) - check_library_exists("idn2" "idn2_lookup_ul" "" HAVE_LIBIDN2) - if(HAVE_LIBIDN2) - set(CURL_LIBS "idn2;${CURL_LIBS}") - check_include_file_concat("idn2.h" HAVE_IDN2_H) - endif() -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") endif() +else() + set(USE_WIN32_IDN OFF) endif() if(APPLE) @@ -1094,57 +1259,123 @@ if(APPLE) check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN) cmake_pop_check_state() if(HAVE_APPLE_IDN) - list(APPEND CURL_LIBS "icucore") + list(APPEND CURL_LIBS "icucore" "iconv") else() set(USE_APPLE_IDN OFF) endif() endif() +else() + set(USE_APPLE_IDN OFF) +endif() + +# Check for libidn2 +option(USE_LIBIDN2 "Use libidn2 for IDN support" ON) +set(HAVE_IDN2_H OFF) +set(HAVE_LIBIDN2 OFF) +if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) + find_package(Libidn2 QUIET) + if(LIBIDN2_FOUND) + set(CURL_LIBS "${LIBIDN2_LIBRARIES};${CURL_LIBS}") + list(APPEND CURL_LIBDIRS ${LIBIDN2_LIBRARY_DIRS}) + set(LIBCURL_PC_REQUIRES_PRIVATE "libidn2;${LIBCURL_PC_REQUIRES_PRIVATE}") + include_directories(${LIBIDN2_INCLUDE_DIRS}) + link_directories(${LIBIDN2_LIBRARY_DIRS}) + if(LIBIDN2_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBIDN2_CFLAGS}") + endif() + set(HAVE_IDN2_H 1) + set(HAVE_LIBIDN2 1) + endif() endif() -#libpsl -option(CURL_USE_LIBPSL "Use libPSL" ON) +# 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) + find_package(Libpsl) # TODO: add REQUIRED to match autotools if(LIBPSL_FOUND) - list(APPEND CURL_LIBS ${LIBPSL_LIBRARY}) - list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBPSL_INCLUDE_DIR}") - include_directories("${LIBPSL_INCLUDE_DIR}") + list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libpsl") + list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBPSL_INCLUDE_DIRS}") + include_directories(${LIBPSL_INCLUDE_DIRS}) set(USE_LIBPSL ON) + else() + message(WARNING "libpsl is enabled, but not found.") endif() endif() -#libSSH2 -option(CURL_USE_LIBSSH2 "Use libSSH2" ON) +# libssh2 +option(CURL_USE_LIBSSH2 "Use libssh2" ON) # FIXME: default is OFF in autotools mark_as_advanced(CURL_USE_LIBSSH2) set(USE_LIBSSH2 OFF) if(CURL_USE_LIBSSH2) - find_package(LibSSH2) + find_package(Libssh2) if(LIBSSH2_FOUND) - list(APPEND CURL_LIBS ${LIBSSH2_LIBRARY}) - list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIR}") - include_directories("${LIBSSH2_INCLUDE_DIR}") + list(APPEND CURL_LIBS ${LIBSSH2_LIBRARIES}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh2") + list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIRS}") + include_directories(${LIBSSH2_INCLUDE_DIRS}) set(USE_LIBSSH2 ON) endif() endif() # libssh -option(CURL_USE_LIBSSH "Use libSSH" OFF) +option(CURL_USE_LIBSSH "Use libssh" OFF) mark_as_advanced(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) + find_package(Libssh REQUIRED) + if(LIBSSH_FOUND) + list(APPEND CURL_LIBS ${LIBSSH_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh") + include_directories(${LIBSSH_INCLUDE_DIRS}) + link_directories(${LIBSSH_LIBRARY_DIRS}) + if(LIBSSH_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH_CFLAGS}") + endif() set(USE_LIBSSH ON) endif() endif() -option(CURL_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF) +# wolfSSH +option(CURL_USE_WOLFSSH "Use wolfSSH" OFF) +mark_as_advanced(CURL_USE_WOLFSSH) +set(USE_WOLFSSH OFF) +if(NOT USE_LIBSSH2 AND NOT USE_LIBSSH AND CURL_USE_WOLFSSH) + if(USE_WOLFSSL) + find_package(WolfSSH) + if(WOLFSSH_FOUND) + list(APPEND CURL_LIBS ${WOLFSSH_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSH_INCLUDE_DIRS}") + include_directories(${WOLFSSH_INCLUDE_DIRS}) + set(USE_WOLFSSH ON) + endif() + else() + message(WARNING "wolfSSH requires wolfSSL. Skipping.") + endif() +endif() + +option(CURL_USE_GSASL "Use libgsasl" OFF) +mark_as_advanced(CURL_USE_GSASL) +if(CURL_USE_GSASL) + find_package(Libgsasl REQUIRED) + if(LIBGSASL_FOUND) + list(APPEND CURL_LIBS ${LIBGSASL_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBGSASL_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libgsasl") + include_directories(${LIBGSASL_INCLUDE_DIRS}) + link_directories(${LIBGSASL_LIBRARY_DIRS}) + if(LIBGSASL_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBGSASL_CFLAGS}") + endif() + set(USE_GSASL ON) + endif() +endif() + +option(CURL_USE_GSSAPI "Use GSSAPI implementation" OFF) mark_as_advanced(CURL_USE_GSSAPI) if(CURL_USE_GSSAPI) @@ -1152,38 +1383,35 @@ if(CURL_USE_GSSAPI) set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) + list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRS}) - message(STATUS "Found ${GSS_FLAVOUR} GSSAPI version: \"${GSS_VERSION}\"") + string(REPLACE ";" " " GSS_CFLAGS "${GSS_CFLAGS}") + string(REPLACE ";" " " GSS_LDFLAGS "${GSS_LDFLAGS}") - list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIR}) - check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) + foreach(_dir IN LISTS GSS_LIBRARY_DIRS) + set(GSS_LDFLAGS "${GSS_LDFLAGS} -L\"${_dir}\"") + endforeach() + + check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H) - if(NOT GSS_FLAVOUR STREQUAL "Heimdal") - # MIT - set(_INCLUDE_LIST "") + if(GSS_FLAVOUR STREQUAL "MIT") + set(_include_list "") if(HAVE_GSSAPI_GSSAPI_H) - list(APPEND _INCLUDE_LIST "gssapi/gssapi.h") + list(APPEND _include_list "gssapi/gssapi.h") endif() if(HAVE_GSSAPI_GSSAPI_GENERIC_H) - list(APPEND _INCLUDE_LIST "gssapi/gssapi_generic.h") + list(APPEND _include_list "gssapi/gssapi_generic.h") endif() if(HAVE_GSSAPI_GSSAPI_KRB5_H) - list(APPEND _INCLUDE_LIST "gssapi/gssapi_krb5.h") + list(APPEND _include_list "gssapi/gssapi_krb5.h") endif() - string(REPLACE ";" " " _COMPILER_FLAGS_STR "${GSS_COMPILER_FLAGS}") - string(REPLACE ";" " " _LINKER_FLAGS_STR "${GSS_LINKER_FLAGS}") - - foreach(_dir ${GSS_LINK_DIRECTORIES}) - set(_LINKER_FLAGS_STR "${_LINKER_FLAGS_STR} -L\"${_dir}\"") - endforeach() - if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) - set(CMAKE_REQUIRED_FLAGS "${_COMPILER_FLAGS_STR} ${_LINKER_FLAGS_STR}") + set(CMAKE_REQUIRED_FLAGS "${GSS_CFLAGS} ${GSS_LDFLAGS}") set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) - check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_INCLUDE_LIST} HAVE_GSS_C_NT_HOSTBASED_SERVICE) + 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) @@ -1191,19 +1419,43 @@ if(CURL_USE_GSSAPI) endif() endif() - include_directories(${GSS_INCLUDE_DIR}) - link_directories(${GSS_LINK_DIRECTORIES}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_COMPILER_FLAGS}") - string(REPLACE ";" " " GSS_LINKER_FLAGS "${GSS_LINKER_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LINKER_FLAGS}") + include_directories(${GSS_INCLUDE_DIRS}) + link_directories(${GSS_LIBRARY_DIRS}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LDFLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LDFLAGS}") list(APPEND CURL_LIBS ${GSS_LIBRARIES}) - + if(GSS_FLAVOUR STREQUAL "MIT") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mit-krb5-gssapi") + else() # Heimdal + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "heimdal-gssapi") + endif() else() message(WARNING "GSSAPI support has been requested but no supporting libraries found. Skipping.") endif() endif() +# libuv +option(CURL_USE_LIBUV "Use libuv for event-based tests" OFF) +if(CURL_USE_LIBUV) + if(NOT ENABLE_DEBUG) + message(FATAL_ERROR "Using libuv without debug support enabled is useless") + endif() + find_package(Libuv REQUIRED) + if(LIBUV_FOUND) + list(APPEND CURL_LIBS ${LIBUV_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${LIBUV_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libuv") + include_directories(${LIBUV_INCLUDE_DIRS}) + link_directories(${LIBUV_LIBRARY_DIRS}) + if(LIBUV_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUV_CFLAGS}") + endif() + set(USE_LIBUV ON) + set(HAVE_UV_H ON) + endif() +endif() + option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) if(USE_LIBRTMP) cmake_push_check_state() @@ -1215,6 +1467,7 @@ if(USE_LIBRTMP) cmake_pop_check_state() if(HAVE_LIBRTMP) list(APPEND CURL_LIBS "rtmp") + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "librtmp") if(WIN32) list(APPEND CURL_LIBS "winmm") endif() @@ -1224,100 +1477,106 @@ if(USE_LIBRTMP) endif() endif() -option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON) +option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) if(ENABLE_UNIX_SOCKETS) - include(CheckStructHasMember) if(WIN32) set(USE_UNIX_SOCKETS ON) else() - check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS) + include(CheckStructHasMember) + check_struct_has_member("struct sockaddr_un" "sun_path" "sys/un.h" USE_UNIX_SOCKETS) endif() else() unset(USE_UNIX_SOCKETS CACHE) endif() - if(0) # This code not needed for building within CMake. # # CA handling # -set(CURL_CA_BUNDLE "auto" CACHE STRING - "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") -set(CURL_CA_FALLBACK OFF CACHE BOOL +if(_curl_ca_bundle_supported) + set(CURL_CA_BUNDLE "auto" CACHE + STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_FALLBACK OFF CACHE BOOL "Set ON to use built-in CA store of TLS backend. Defaults to OFF") -set(CURL_CA_PATH "auto" CACHE STRING - "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - -if("${CURL_CA_BUNDLE}" STREQUAL "") - message(FATAL_ERROR "Invalid value of CURL_CA_BUNDLE. Use 'none', 'auto' or file path.") -elseif("${CURL_CA_BUNDLE}" STREQUAL "none") - unset(CURL_CA_BUNDLE CACHE) -elseif("${CURL_CA_BUNDLE}" STREQUAL "auto") - unset(CURL_CA_BUNDLE CACHE) - if(NOT CMAKE_CROSSCOMPILING) - set(CURL_CA_BUNDLE_AUTODETECT TRUE) - endif() -else() - set(CURL_CA_BUNDLE_SET TRUE) -endif() + set(CURL_CA_PATH "auto" CACHE + STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_EMBED "" CACHE + STRING "Path to the CA bundle to embed into the curl tool.") + + if(CURL_CA_BUNDLE STREQUAL "") + message(FATAL_ERROR "Invalid value of CURL_CA_BUNDLE. Use 'none', 'auto' or file path.") + elseif(CURL_CA_BUNDLE STREQUAL "none") + unset(CURL_CA_BUNDLE CACHE) + elseif(CURL_CA_BUNDLE STREQUAL "auto") + unset(CURL_CA_BUNDLE CACHE) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + set(_curl_ca_bundle_autodetect TRUE) + endif() + else() + set(CURL_CA_BUNDLE_SET TRUE) + endif() + mark_as_advanced(CURL_CA_BUNDLE_SET) + + if(CURL_CA_PATH STREQUAL "") + message(FATAL_ERROR "Invalid value of CURL_CA_PATH. Use 'none', 'auto' or directory path.") + elseif(CURL_CA_PATH STREQUAL "none") + unset(CURL_CA_PATH CACHE) + elseif(CURL_CA_PATH STREQUAL "auto") + unset(CURL_CA_PATH CACHE) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + set(_curl_ca_path_autodetect TRUE) + endif() + else() + set(CURL_CA_PATH_SET TRUE) + endif() + mark_as_advanced(CURL_CA_PATH_SET) + + if(CURL_CA_BUNDLE_SET AND _curl_ca_path_autodetect) + # Skip auto-detection of unset CA path because CA bundle is set explicitly + elseif(CURL_CA_PATH_SET AND _curl_ca_bundle_autodetect) + # Skip auto-detection of unset CA bundle because CA path is set explicitly + elseif(_curl_ca_bundle_autodetect OR _curl_ca_path_autodetect) + # First try auto-detecting a CA bundle, then a CA path + + if(_curl_ca_bundle_autodetect) + foreach(_search_ca_bundle_path IN ITEMS + "/etc/ssl/certs/ca-certificates.crt" + "/etc/pki/tls/certs/ca-bundle.crt" + "/usr/share/ssl/certs/ca-bundle.crt" + "/usr/local/share/certs/ca-root-nss.crt" + "/etc/ssl/cert.pem") + if(EXISTS "${_search_ca_bundle_path}") + message(STATUS "Found CA bundle: ${_search_ca_bundle_path}") + set(CURL_CA_BUNDLE "${_search_ca_bundle_path}" CACHE + STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + break() + endif() + endforeach() + endif() -if("${CURL_CA_PATH}" STREQUAL "") - message(FATAL_ERROR "Invalid value of CURL_CA_PATH. Use 'none', 'auto' or directory path.") -elseif("${CURL_CA_PATH}" STREQUAL "none") - unset(CURL_CA_PATH CACHE) -elseif("${CURL_CA_PATH}" STREQUAL "auto") - unset(CURL_CA_PATH CACHE) - if(NOT CMAKE_CROSSCOMPILING) - set(CURL_CA_PATH_AUTODETECT TRUE) - endif() -else() - set(CURL_CA_PATH_SET TRUE) -endif() - -if(CURL_CA_BUNDLE_SET AND CURL_CA_PATH_AUTODETECT) - # Skip autodetection of unset CA path because CA bundle is set explicitly -elseif(CURL_CA_PATH_SET AND CURL_CA_BUNDLE_AUTODETECT) - # Skip autodetection of unset CA bundle because CA path is set explicitly -elseif(CURL_CA_PATH_AUTODETECT OR CURL_CA_BUNDLE_AUTODETECT) - # first try autodetecting a CA bundle, then a CA path - - if(CURL_CA_BUNDLE_AUTODETECT) - set(SEARCH_CA_BUNDLE_PATHS - /etc/ssl/certs/ca-certificates.crt - /etc/pki/tls/certs/ca-bundle.crt - /usr/share/ssl/certs/ca-bundle.crt - /usr/local/share/certs/ca-root-nss.crt - /etc/ssl/cert.pem) - - foreach(SEARCH_CA_BUNDLE_PATH ${SEARCH_CA_BUNDLE_PATHS}) - if(EXISTS "${SEARCH_CA_BUNDLE_PATH}") - message(STATUS "Found CA bundle: ${SEARCH_CA_BUNDLE_PATH}") - set(CURL_CA_BUNDLE "${SEARCH_CA_BUNDLE_PATH}" CACHE STRING - "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set") - break() + if(_curl_ca_path_autodetect AND NOT CURL_CA_PATH_SET) + set(_search_ca_path "/etc/ssl/certs") + file(GLOB _curl_ca_files_found "${_search_ca_path}/[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].0") + if(_curl_ca_files_found) + unset(_curl_ca_files_found) + message(STATUS "Found CA path: ${_search_ca_path}") + set(CURL_CA_PATH "${_search_ca_path}" CACHE + STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set") endif() - endforeach() + endif() endif() - if(CURL_CA_PATH_AUTODETECT AND (NOT CURL_CA_PATH_SET)) - if(EXISTS "/etc/ssl/certs") - set(CURL_CA_PATH "/etc/ssl/certs" CACHE STRING - "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + set(CURL_CA_EMBED_SET FALSE) + if(BUILD_CURL_EXE AND NOT CURL_CA_EMBED STREQUAL "") + if(EXISTS "${CURL_CA_EMBED}") + set(CURL_CA_EMBED_SET TRUE) + else() + message(FATAL_ERROR "CA bundle to embed is missing: '${CURL_CA_EMBED}'") endif() endif() endif() - -if(CURL_CA_PATH_SET AND - NOT USE_OPENSSL AND - NOT USE_WOLFSSL AND - NOT USE_GNUTLS AND - NOT USE_MBEDTLS) - message(STATUS - "CA path only supported by OpenSSL, wolfSSL, GnuTLS or mbedTLS. " - "Set CURL_CA_PATH=none or enable one of those TLS backends.") -endif() endif() # Check for header files @@ -1325,30 +1584,16 @@ if(WIN32) set(CURL_INCLUDES ${CURL_INCLUDES} "winsock2.h") set(CURL_INCLUDES ${CURL_INCLUDES} "ws2tcpip.h") set(CURL_INCLUDES ${CURL_INCLUDES} "windows.h") -endif() - -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=" "" OUTPUT "${OUTPUT}") - string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" OUTPUT "${OUTPUT}") # pad to 4 digits - string(TOLOWER "${OUTPUT}" HAVE_WIN32_WINNT) - message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") - endif() - # avoid storing HAVE_WIN32_WINNT in CMake cache - unset(HAVE_WIN32_WINNT CACHE) if(HAVE_WIN32_WINNT) - if(HAVE_WIN32_WINNT STRLESS "0x0501") + if(HAVE_WIN32_WINNT LESS 0x0501) # Windows XP is required for freeaddrinfo, getaddrinfo message(FATAL_ERROR "Building for Windows XP or newer is required.") endif() - # pre-fill detection results based on target OS version + # Pre-fill detection results based on target OS version if(MINGW OR MSVC) - if(HAVE_WIN32_WINNT STRLESS "0x0600") + if(HAVE_WIN32_WINNT LESS 0x0600) set(HAVE_INET_NTOP 0) set(HAVE_INET_PTON 0) else() # Windows Vista or newer @@ -1361,6 +1606,7 @@ if(WIN32) endif() endif() +check_include_file_concat("sys/eventfd.h" HAVE_SYS_EVENTFD_H) check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H) check_include_file_concat("sys/wait.h" HAVE_SYS_WAIT_H) check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H) @@ -1401,114 +1647,110 @@ check_include_file_concat("termios.h" HAVE_TERMIOS_H) check_include_file_concat("unistd.h" HAVE_UNISTD_H) check_include_file_concat("utime.h" HAVE_UTIME_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) -check_type_size("suseconds_t" SIZEOF_SUSECONDS_T) +check_type_size("size_t" SIZEOF_SIZE_T) +check_type_size("ssize_t" SIZEOF_SSIZE_T) +check_type_size("time_t" SIZEOF_TIME_T) +check_type_size("suseconds_t" SIZEOF_SUSECONDS_T) if(SIZEOF_SUSECONDS_T) set(HAVE_SUSECONDS_T 1) endif() -if(NOT CMAKE_CROSSCOMPILING) - find_file(RANDOM_FILE urandom /dev) - mark_as_advanced(RANDOM_FILE) -endif() - # Check for some functions that are used if(WIN32) - set(CMAKE_REQUIRED_LIBRARIES ws2_32) + set(CMAKE_REQUIRED_LIBRARIES "ws2_32") elseif(HAVE_LIBSOCKET) - set(CMAKE_REQUIRED_LIBRARIES socket) + set(CMAKE_REQUIRED_LIBRARIES "socket") elseif(HAVE_LIBNETWORK) - set(CMAKE_REQUIRED_LIBRARIES network) -endif() - -check_symbol_exists(fnmatch "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) -check_symbol_exists(basename "${CURL_INCLUDES};string.h" HAVE_BASENAME) -check_symbol_exists(opendir "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) -check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET) -check_symbol_exists(sched_yield "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) -check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR) -check_symbol_exists(recv "${CURL_INCLUDES}" HAVE_RECV) -check_symbol_exists(send "${CURL_INCLUDES}" HAVE_SEND) -check_symbol_exists(sendmsg "${CURL_INCLUDES}" HAVE_SENDMSG) -check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT) -check_symbol_exists(strdup "${CURL_INCLUDES};string.h" HAVE_STRDUP) -check_symbol_exists(strtok_r "${CURL_INCLUDES};string.h" HAVE_STRTOK_R) -check_symbol_exists(strcasecmp "${CURL_INCLUDES};string.h" HAVE_STRCASECMP) -check_symbol_exists(stricmp "${CURL_INCLUDES};string.h" HAVE_STRICMP) -check_symbol_exists(strcmpi "${CURL_INCLUDES};string.h" HAVE_STRCMPI) -check_symbol_exists(memrchr "${CURL_INCLUDES};string.h" HAVE_MEMRCHR) -check_symbol_exists(alarm "${CURL_INCLUDES}" HAVE_ALARM) -check_symbol_exists(arc4random "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) -check_symbol_exists(fcntl "${CURL_INCLUDES}" HAVE_FCNTL) -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(closesocket "${CURL_INCLUDES}" HAVE_CLOSESOCKET) -check_symbol_exists(sigsetjmp "${CURL_INCLUDES};setjmp.h" 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(utime "${CURL_INCLUDES}" HAVE_UTIME) -check_symbol_exists(gmtime_r "${CURL_INCLUDES};stdlib.h;time.h" HAVE_GMTIME_R) - -check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) - -check_symbol_exists(signal "${CURL_INCLUDES};signal.h" HAVE_SIGNAL) -check_symbol_exists(strtoll "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL) -check_symbol_exists(strerror_r "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R) -check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION) -check_symbol_exists(siginterrupt "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT) -check_symbol_exists(getaddrinfo "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) -check_symbol_exists(getifaddrs "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) -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(_fseeki64 "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64) -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) -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) + set(CMAKE_REQUIRED_LIBRARIES "network") +endif() + +check_symbol_exists("fnmatch" "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) +check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) +check_symbol_exists("opendir" "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) +check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) +check_symbol_exists("sched_yield" "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) +check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) +check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) +check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) +check_symbol_exists("sendmsg" "${CURL_INCLUDES}" HAVE_SENDMSG) +check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) +check_symbol_exists("strdup" "${CURL_INCLUDES};string.h" HAVE_STRDUP) +check_symbol_exists("strtok_r" "${CURL_INCLUDES};string.h" HAVE_STRTOK_R) +check_symbol_exists("strcasecmp" "${CURL_INCLUDES};string.h" HAVE_STRCASECMP) +check_symbol_exists("stricmp" "${CURL_INCLUDES};string.h" HAVE_STRICMP) +check_symbol_exists("strcmpi" "${CURL_INCLUDES};string.h" HAVE_STRCMPI) +check_symbol_exists("memrchr" "${CURL_INCLUDES};string.h" HAVE_MEMRCHR) +check_symbol_exists("alarm" "${CURL_INCLUDES}" HAVE_ALARM) +check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) +check_symbol_exists("fcntl" "${CURL_INCLUDES}" HAVE_FCNTL) +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("closesocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET) +check_symbol_exists("sigsetjmp" "${CURL_INCLUDES};setjmp.h" 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("utime" "${CURL_INCLUDES}" HAVE_UTIME) +check_symbol_exists("gmtime_r" "${CURL_INCLUDES};stdlib.h;time.h" HAVE_GMTIME_R) + +check_symbol_exists("gethostbyname_r" "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) + +check_symbol_exists("signal" "${CURL_INCLUDES};signal.h" HAVE_SIGNAL) +check_symbol_exists("strtoll" "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL) +check_symbol_exists("strerror_r" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R) +check_symbol_exists("sigaction" "signal.h" HAVE_SIGACTION) +check_symbol_exists("siginterrupt" "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT) +check_symbol_exists("getaddrinfo" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) +check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) +check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) +check_symbol_exists("pipe" "${CURL_INCLUDES}" HAVE_PIPE) +check_symbol_exists("eventfd" "${CURL_INCLUDES};sys/eventfd.h" HAVE_EVENTFD) +check_symbol_exists("ftruncate" "${CURL_INCLUDES}" HAVE_FTRUNCATE) +check_symbol_exists("_fseeki64" "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64) +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) +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) if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) - # earlier MSVC compilers had faulty snprintf implementations - check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF) + # Earlier MSVC compilers had faulty snprintf implementations + check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF) endif() -check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME) -check_symbol_exists(inet_ntop "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) +check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) +check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600)) set(HAVE_INET_NTOP OFF) endif() -check_symbol_exists(inet_pton "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) +check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) -check_symbol_exists(fsetxattr "${CURL_INCLUDES}" HAVE_FSETXATTR) +check_symbol_exists("fsetxattr" "${CURL_INCLUDES}" HAVE_FSETXATTR) if(HAVE_FSETXATTR) - foreach(CURL_TEST HAVE_FSETXATTR_5 HAVE_FSETXATTR_6) - curl_internal_test(${CURL_TEST}) + foreach(_curl_test IN ITEMS HAVE_FSETXATTR_5 HAVE_FSETXATTR_6) + curl_internal_test(${_curl_test}) endforeach() endif() -set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") -check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) -set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) -set(CMAKE_EXTRA_INCLUDE_FILES "") +set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") +check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) +set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) +set(CMAKE_EXTRA_INCLUDE_FILES "") if(WIN32) - set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") - check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) - set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) - set(CMAKE_EXTRA_INCLUDE_FILES "") + set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") + check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) + set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) + set(CMAKE_EXTRA_INCLUDE_FILES "") endif() # Do curl specific tests -foreach(CURL_TEST +foreach(_curl_test IN ITEMS HAVE_FCNTL_O_NONBLOCK HAVE_IOCTLSOCKET HAVE_IOCTLSOCKET_CAMEL @@ -1530,35 +1772,35 @@ foreach(CURL_TEST HAVE_FILE_OFFSET_BITS HAVE_ATOMIC ) - curl_internal_test(${CURL_TEST}) + curl_internal_test(${_curl_test}) endforeach() if(HAVE_FILE_OFFSET_BITS) set(_FILE_OFFSET_BITS 64) set(CMAKE_REQUIRED_FLAGS "-D_FILE_OFFSET_BITS=64") endif() -check_type_size("off_t" SIZEOF_OFF_T) +check_type_size("off_t" SIZEOF_OFF_T) # fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with # _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) # so we need to test fseeko after testing for _FILE_OFFSET_BITS -check_symbol_exists(fseeko "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) +check_symbol_exists("fseeko" "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) if(HAVE_FSEEKO) set(HAVE_DECL_FSEEKO 1) endif() -# include this header to get the type +# Include this header to get the type set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include") set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") -check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) +check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") -check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) +check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) set(CMAKE_EXTRA_INCLUDE_FILES "") if(0) # This code not needed for building within CMake. if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) - # on not-Windows and not-crosscompiling, check for writable argv[] + # On non-Windows and not cross-compiling, check for writable argv[] include(CheckCSourceRuns) check_c_source_runs(" int main(int argc, char **argv) @@ -1570,45 +1812,45 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) endif() endif() -set(CMAKE_REQUIRED_FLAGS) +unset(CMAKE_REQUIRED_FLAGS) -option(ENABLE_WEBSOCKETS "Set to ON to enable EXPERIMENTAL websockets" OFF) +option(ENABLE_WEBSOCKETS "Enable WebSockets (experimental)" OFF) if(ENABLE_WEBSOCKETS) - if(${SIZEOF_CURL_OFF_T} GREATER "4") + 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 +foreach(_curl_test IN ITEMS HAVE_GLIBC_STRERROR_R HAVE_POSIX_STRERROR_R ) - curl_internal_test(${CURL_TEST}) + curl_internal_test(${_curl_test}) endforeach() # Check for reentrant -foreach(CURL_TEST +foreach(_curl_test IN ITEMS HAVE_GETHOSTBYNAME_R_3 HAVE_GETHOSTBYNAME_R_5 HAVE_GETHOSTBYNAME_R_6) - if(NOT ${CURL_TEST}) - if(${CURL_TEST}_REENTRANT) + if(NOT ${_curl_test}) + if(${_curl_test}_REENTRANT) set(NEED_REENTRANT 1) endif() endif() endforeach() if(NEED_REENTRANT) - foreach(CURL_TEST + foreach(_curl_test IN ITEMS HAVE_GETHOSTBYNAME_R_3 HAVE_GETHOSTBYNAME_R_5 HAVE_GETHOSTBYNAME_R_6) - set(${CURL_TEST} 0) - if(${CURL_TEST}_REENTRANT) - set(${CURL_TEST} 1) + set(${_curl_test} 0) + if(${_curl_test}_REENTRANT) + set(${_curl_test} 1) endif() endforeach() endif() @@ -1618,8 +1860,10 @@ if(NOT WIN32) curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC) endif() -# Check compiler support of __builtin_available() -curl_internal_test(HAVE_BUILTIN_AVAILABLE) +if(APPLE) + # Check compiler support of __builtin_available() + curl_internal_test(HAVE_BUILTIN_AVAILABLE) +endif() # Some other minor tests @@ -1630,31 +1874,31 @@ endif() # Check for nonblocking set(HAVE_DISABLED_NONBLOCKING 1) if(HAVE_FIONBIO OR - HAVE_IOCTLSOCKET OR - HAVE_IOCTLSOCKET_CASE OR - HAVE_O_NONBLOCK) - set(HAVE_DISABLED_NONBLOCKING) + HAVE_IOCTLSOCKET OR + HAVE_IOCTLSOCKET_CASE OR + HAVE_O_NONBLOCK) + unset(HAVE_DISABLED_NONBLOCKING) endif() if(CMAKE_COMPILER_IS_GNUCC AND APPLE) include(CheckCCompilerFlag) - check_c_compiler_flag(-Wno-long-double HAVE_C_FLAG_Wno_long_double) + check_c_compiler_flag("-Wno-long-double" HAVE_C_FLAG_Wno_long_double) if(HAVE_C_FLAG_Wno_long_double) - # The Mac version of GCC warns about use of long double. Disable it. - get_source_file_property(MPRINTF_COMPILE_FLAGS mprintf.c COMPILE_FLAGS) - if(MPRINTF_COMPILE_FLAGS) - set(MPRINTF_COMPILE_FLAGS "${MPRINTF_COMPILE_FLAGS} -Wno-long-double") + # The Mac version of GCC warns about use of long double. Disable it. + get_source_file_property(_mprintf_compile_flags "mprintf.c" COMPILE_FLAGS) + if(_mprintf_compile_flags) + set(_mprintf_compile_flags "${_mprintf_compile_flags} -Wno-long-double") else() - set(MPRINTF_COMPILE_FLAGS "-Wno-long-double") + set(_mprintf_compile_flags "-Wno-long-double") endif() - set_source_files_properties(mprintf.c PROPERTIES - COMPILE_FLAGS ${MPRINTF_COMPILE_FLAGS}) + set_source_files_properties("mprintf.c" PROPERTIES + COMPILE_FLAGS ${_mprintf_compile_flags}) endif() endif() include(CMake/OtherTests.cmake) -add_definitions(-DHAVE_CONFIG_H) +add_definitions("-DHAVE_CONFIG_H") # For Windows, all compilers used by CMake should support large files if(WIN32) @@ -1678,7 +1922,7 @@ if(MSVC) # Disable default manifest added by CMake set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") - add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) + 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]") @@ -1694,10 +1938,10 @@ if(MSVC) endif() if(CURL_WERROR) - if(MSVC_VERSION) + if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") else() - # this assumes clang or gcc style options + # This assumes clang or gcc style options set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") endif() endif() @@ -1714,25 +1958,29 @@ if(CURL_LTO) if(CURL_HAS_LTO) message(STATUS "LTO supported and enabled") else() - message(FATAL_ERROR "LTO was requested - but compiler doesn't support it\n${CURL_LTO_ERROR}") + message(FATAL_ERROR "LTO was requested - but compiler does not support it\n${CURL_LTO_ERROR}") endif() endif() -# Ugly (but functional) way to include "Makefile.inc" by transforming it (= regenerate it). -function(transform_makefile_inc INPUT_FILE OUTPUT_FILE) - file(READ ${INPUT_FILE} MAKEFILE_INC_TEXT) - string(REPLACE "$(top_srcdir)" "\${CURL_SOURCE_DIR}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) - string(REPLACE "$(top_builddir)" "\${CURL_BINARY_DIR}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) +# Ugly (but functional) way to include "Makefile.inc" by transforming it +# (= regenerate it). +function(transform_makefile_inc _input_file _output_file) + file(READ ${_input_file} _makefile_inc_text) + string(REPLACE "$(top_srcdir)" "\${CURL_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "$(top_builddir)" "\${CURL_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) - string(REGEX REPLACE "\\\\\n" "!π!α!" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) - string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "SET(\\1 \\2)" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) - string(REPLACE "!π!α!" "\n" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) + string(REGEX REPLACE "\\\\\n" "!π!α!" _makefile_inc_text ${_makefile_inc_text}) + string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "set(\\1 \\2)" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "!π!α!" "\n" _makefile_inc_text ${_makefile_inc_text}) - string(REGEX REPLACE "\\$\\(([a-zA-Z_][a-zA-Z0-9_]*)\\)" "\${\\1}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) # Replace $() with ${} - string(REGEX REPLACE "@([a-zA-Z_][a-zA-Z0-9_]*)@" "\${\\1}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT}) # Replace @@ with ${}, even if that may not be read by CMake scripts. - file(WRITE ${OUTPUT_FILE} ${MAKEFILE_INC_TEXT}) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${INPUT_FILE}") + # Replace $() with ${} + string(REGEX REPLACE "\\$\\(([a-zA-Z_][a-zA-Z0-9_]*)\\)" "\${\\1}" _makefile_inc_text ${_makefile_inc_text}) + # Replace @@ with ${}, even if that may not be read by CMake scripts. + string(REGEX REPLACE "@([a-zA-Z_][a-zA-Z0-9_]*)@" "\${\\1}" _makefile_inc_text ${_makefile_inc_text}) + + file(WRITE ${_output_file} ${_makefile_inc_text}) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_input_file}") endfunction() #----------------------------------------------------------------------------- @@ -1753,11 +2001,11 @@ return() # The rest of this file is not needed for building within CMake. include(GNUInstallDirs) -set(CURL_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) +set(CURL_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") -set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") -set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") -set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +set(_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") +set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") cmake_dependent_option(BUILD_TESTING "Build tests" ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" @@ -1782,172 +2030,199 @@ if(BUILD_TESTING) add_subdirectory(tests) endif() -if(NOT CURL_DISABLE_INSTALL) - - install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) - - # Helper to populate a list (_items) with a label when conditions (the remaining - # args) are satisfied - macro(_add_if label) - # needs to be a macro to allow this indirection - if(${ARGN}) - set(_items ${_items} "${label}") - endif() - endmacro() - - # NTLM support requires crypto function adaptions from various SSL libs - if(NOT (CURL_DISABLE_NTLM) AND - (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS)) - set(use_curl_ntlm_core ON) - endif() - - # Clear list and try to detect available features - set(_items) - _add_if("SSL" SSL_ENABLED) - _add_if("IPv6" ENABLE_IPV6) - _add_if("UnixSockets" USE_UNIX_SOCKETS) - _add_if("libz" HAVE_LIBZ) - _add_if("brotli" HAVE_BROTLI) - _add_if("zstd" HAVE_ZSTD) - _add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) - _add_if("IDN" HAVE_LIBIDN2 OR USE_WIN32_IDN OR USE_APPLE_IDN) - _add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND - ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) - _add_if("SSPI" USE_WINDOWS_SSPI) - _add_if("GSS-API" HAVE_GSSAPI) - _add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) - _add_if("HSTS" NOT CURL_DISABLE_HSTS) - _add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) - _add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND - (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) - _add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND - (use_curl_ntlm_core OR USE_WINDOWS_SSPI)) - _add_if("TLS-SRP" USE_TLS_SRP) - _add_if("HTTP2" USE_NGHTTP2) - _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC) - _add_if("MultiSSL" CURL_WITH_MULTI_SSL) - # TODO wolfSSL only support this from v5.0.0 onwards - _add_if("HTTPS-proxy" SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS - OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR - USE_MBEDTLS OR USE_SECTRANSP)) - _add_if("unicode" ENABLE_UNICODE) - _add_if("threadsafe" HAVE_ATOMIC OR - (USE_THREADS_POSIX AND HAVE_PTHREAD_H) 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}") - - # Clear list and try to detect available protocols - set(_items) - _add_if("HTTP" NOT CURL_DISABLE_HTTP) - _add_if("IPFS" NOT CURL_DISABLE_HTTP) - _add_if("IPNS" NOT CURL_DISABLE_HTTP) - _add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED) - _add_if("ECH" HAVE_ECH) - _add_if("HTTPSRR" HAVE_ECH) - _add_if("FTP" NOT CURL_DISABLE_FTP) - _add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED) - _add_if("FILE" NOT CURL_DISABLE_FILE) - _add_if("TELNET" NOT CURL_DISABLE_TELNET) - _add_if("LDAP" NOT CURL_DISABLE_LDAP) - # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS - _add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND - ((USE_OPENLDAP AND SSL_ENABLED) OR - (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) - _add_if("DICT" NOT CURL_DISABLE_DICT) - _add_if("TFTP" NOT CURL_DISABLE_TFTP) - _add_if("GOPHER" NOT CURL_DISABLE_GOPHER) - _add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND SSL_ENABLED) - _add_if("POP3" NOT CURL_DISABLE_POP3) - _add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED) - _add_if("IMAP" NOT CURL_DISABLE_IMAP) - _add_if("IMAPS" NOT CURL_DISABLE_IMAP AND SSL_ENABLED) - _add_if("SMB" NOT CURL_DISABLE_SMB AND - use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) - _add_if("SMBS" NOT CURL_DISABLE_SMB AND SSL_ENABLED AND - use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) - _add_if("SMTP" NOT CURL_DISABLE_SMTP) - _add_if("SMTPS" NOT CURL_DISABLE_SMTP AND SSL_ENABLED) - _add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH) - _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) +# Helper to populate a list (_items) with a label when conditions +# (the remaining args) are satisfied +macro(_add_if _label) + # Needs to be a macro to allow this indirection + if(${ARGN}) + set(_items ${_items} "${_label}") endif() - string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") - message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}") - - # Clear list and collect SSL backends - set(_items) - _add_if("Schannel" SSL_ENABLED AND USE_SCHANNEL) - _add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL) - _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP) - _add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS) - _add_if("BearSSL" SSL_ENABLED AND USE_BEARSSL) - _add_if("wolfSSL" SSL_ENABLED AND USE_WOLFSSL) - _add_if("GnuTLS" SSL_ENABLED AND USE_GNUTLS) +endmacro() - if(_items) +# NTLM support requires crypto functions from various SSL libs. +# These conditions must match those in lib/curl_setup.h. +if(NOT CURL_DISABLE_NTLM AND + (USE_OPENSSL OR + USE_MBEDTLS OR + USE_GNUTLS OR + USE_SECTRANSP OR + USE_WIN32_CRYPTO OR + (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) + set(_use_curl_ntlm_core ON) +endif() + +# Clear list and try to detect available protocols +unset(_items) +_add_if("HTTP" NOT CURL_DISABLE_HTTP) +_add_if("IPFS" NOT CURL_DISABLE_HTTP) +_add_if("IPNS" NOT CURL_DISABLE_HTTP) +_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND _ssl_enabled) +_add_if("FTP" NOT CURL_DISABLE_FTP) +_add_if("FTPS" NOT CURL_DISABLE_FTP AND _ssl_enabled) +_add_if("FILE" NOT CURL_DISABLE_FILE) +_add_if("TELNET" NOT CURL_DISABLE_TELNET) +_add_if("LDAP" NOT CURL_DISABLE_LDAP) +# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS +_add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND + ((USE_OPENLDAP AND _ssl_enabled) OR + (NOT USE_OPENLDAP AND HAVE_LDAP_SSL))) +_add_if("DICT" NOT CURL_DISABLE_DICT) +_add_if("TFTP" NOT CURL_DISABLE_TFTP) +_add_if("GOPHER" NOT CURL_DISABLE_GOPHER) +_add_if("GOPHERS" NOT CURL_DISABLE_GOPHER AND _ssl_enabled) +_add_if("POP3" NOT CURL_DISABLE_POP3) +_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) +_add_if("IMAP" NOT CURL_DISABLE_IMAP) +_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) +_add_if("SMB" NOT CURL_DISABLE_SMB AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND + _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) +_add_if("SMTP" NOT CURL_DISABLE_SMTP) +_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) +_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +_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 AND _ssl_enabled) +if(_items) + list(SORT _items) +endif() +string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}") +string(TOLOWER "${SUPPORT_PROTOCOLS}" _support_protocols_lower) +message(STATUS "Protocols: ${_support_protocols_lower}") + +# Clear list and try to detect available features +unset(_items) +_add_if("SSL" _ssl_enabled) +_add_if("IPv6" ENABLE_IPV6) +_add_if("UnixSockets" USE_UNIX_SOCKETS) +_add_if("libz" HAVE_LIBZ) +_add_if("brotli" HAVE_BROTLI) +_add_if("gsasl" USE_GSASL) +_add_if("zstd" HAVE_ZSTD) +_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) +_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR + USE_WIN32_IDN OR + USE_APPLE_IDN) +_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND + ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) +_add_if("SSPI" USE_WINDOWS_SSPI) +_add_if("GSS-API" HAVE_GSSAPI) +_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) +_add_if("HSTS" NOT CURL_DISABLE_HSTS) +_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND + (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) +_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND + (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) +_add_if("TLS-SRP" USE_TLS_SRP) +_add_if("HTTP2" USE_NGHTTP2) +_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC) +_add_if("MultiSSL" CURL_WITH_MULTI_SSL) +_add_if("HTTPS-proxy" _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS + OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR + USE_MBEDTLS OR USE_SECTRANSP OR + (USE_WOLFSSL AND HAVE_WOLFSSL_FULL_BIO))) +_add_if("Unicode" ENABLE_UNICODE) +_add_if("threadsafe" HAVE_ATOMIC OR + (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR + (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) +_add_if("Debug" ENABLE_DEBUG) +_add_if("TrackMemory" ENABLE_CURLDEBUG) +_add_if("ECH" _ssl_enabled AND HAVE_ECH) +_add_if("PSL" USE_LIBPSL) +_add_if("CAcert" CURL_CA_EMBED_SET) +if(_items) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + list(SORT _items CASE INSENSITIVE) + else() list(SORT _items) endif() - string(REPLACE ";" " " SSL_BACKENDS "${_items}") - message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") - if(CURL_DEFAULT_SSL_BACKEND) - message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}") +endif() +string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") +message(STATUS "Features: ${SUPPORT_FEATURES}") + +# Clear list and collect SSL backends +unset(_items) +_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) +_add_if("OpenSSL" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) +_add_if("OpenSSL v3+" _ssl_enabled AND USE_OPENSSL AND NOT OPENSSL_VERSION VERSION_LESS 3.0.0) +_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP) +_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) +_add_if("BearSSL" _ssl_enabled AND USE_BEARSSL) +_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) +_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) +_add_if("rustls" _ssl_enabled AND USE_RUSTLS) + +if(_items) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + list(SORT _items CASE INSENSITIVE) + else() + list(SORT _items) endif() +endif() +string(REPLACE ";" " " SSL_BACKENDS "${_items}") +message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") +if(CURL_DEFAULT_SSL_BACKEND) + message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}") +endif() + +if(NOT CURL_DISABLE_INSTALL) # curl-config needs the following options to be set. set(CC "${CMAKE_C_COMPILER}") - # TODO probably put a -D... options here? + # TODO: probably put a -D... options here? set(CONFIGURE_OPTIONS "") set(CURLVERSION "${CURL_VERSION}") + set(VERSIONNUM "${CURL_VERSION_NUM}") + set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") set(includedir "\${prefix}/include") set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") - set(LIBCURL_LIBS "") set(libdir "${CMAKE_INSTALL_PREFIX}/lib") + # "a" (Linux) or "lib" (Windows) + string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") - # For processing full path libraries into -L and -l ld options, - # the directories that go with the -L option are cached, so they - # only get added once per such directory. - set(_libcurl_libs_dirs) - # To avoid getting unnecessary -L options for known system directories, - # _libcurl_libs_dirs is seeded with them. - foreach(_libdir ${CMAKE_SYSTEM_PREFIX_PATH}) + set(_ldflags "") + set(LIBCURL_PC_LIBS_PRIVATE "") + + # Avoid getting unnecessary -L options for known system directories. + unset(_sys_libdirs) + foreach(_libdir IN LISTS CMAKE_SYSTEM_PREFIX_PATH) if(_libdir MATCHES "/$") set(_libdir "${_libdir}lib") else() set(_libdir "${_libdir}/lib") endif() if(IS_DIRECTORY "${_libdir}") - list(APPEND _libcurl_libs_dirs "${_libdir}") + list(APPEND _sys_libdirs "${_libdir}") endif() if(DEFINED CMAKE_LIBRARY_ARCHITECTURE) set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}") if(IS_DIRECTORY "${_libdir}") - list(APPEND _libcurl_libs_dirs "${_libdir}") + list(APPEND _sys_libdirs "${_libdir}") endif() endif() endforeach() - foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) + foreach(_libdir IN LISTS CURL_LIBDIRS) + list(FIND _sys_libdirs "${_libdir}" _libdir_index) + if(_libdir_index LESS 0) + list(APPEND _ldflags "-L${_libdir}") + endif() + endforeach() + + foreach(_lib IN LISTS CMAKE_C_IMPLICIT_LINK_LIBRARIES CURL_LIBS) if(TARGET "${_lib}") set(_libname "${_lib}") get_target_property(_imported "${_libname}" IMPORTED) if(NOT _imported) # Reading the LOCATION property on non-imported target will error out. - # Assume the user won't need this information in the .pc file. + # Assume the user will not need this information in the .pc file. continue() endif() get_target_property(_lib "${_libname}" LOCATION) @@ -1956,129 +2231,172 @@ if(NOT CURL_DISABLE_INSTALL) continue() endif() endif() - if(_lib MATCHES "^-") - set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + if(_lib MATCHES "^-") # '-framework ' + list(APPEND _ldflags "${_lib}") elseif(_lib MATCHES ".*/.*") # This gets a bit more complex, because we want to specify the # directory separately, and only once per directory - string(REGEX REPLACE "^(.*)/[^/]*$" "\\1" _libdir "${_lib}") - string(REGEX REPLACE "^.*/([^/.]*).*$" "\\1" _libname "${_lib}") + get_filename_component(_libdir ${_lib} DIRECTORY) + get_filename_component(_libname ${_lib} NAME_WE) if(_libname MATCHES "^lib") - list(FIND _libcurl_libs_dirs "${_libdir}" _libdir_index) + list(FIND _sys_libdirs "${_libdir}" _libdir_index) if(_libdir_index LESS 0) - list(APPEND _libcurl_libs_dirs "${_libdir}") - set(LIBCURL_LIBS "${LIBCURL_LIBS} -L${_libdir}") + list(APPEND _ldflags "-L${_libdir}") endif() string(REGEX REPLACE "^lib" "" _libname "${_libname}") - set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_libname}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_libname}") else() - set(LIBCURL_LIBS "${LIBCURL_LIBS} ${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") endif() else() - set(LIBCURL_LIBS "${LIBCURL_LIBS} -l${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}") endif() endforeach() + + if(LIBCURL_PC_REQUIRES_PRIVATE) + string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}") + endif() + if(LIBCURL_PC_LIBS_PRIVATE) + string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}") + endif() + if(_ldflags) + list(REMOVE_DUPLICATES _ldflags) + string(REPLACE ";" " " _ldflags "${_ldflags}") + set(LIBCURL_PC_LIBS_PRIVATE "${_ldflags} ${LIBCURL_PC_LIBS_PRIVATE}") + string(STRIP "${LIBCURL_PC_LIBS_PRIVATE}" LIBCURL_PC_LIBS_PRIVATE) + endif() + set(LIBCURL_PC_CFLAGS_PRIVATE "-DCURL_STATICLIB") + + # Merge pkg-config private fields into public ones when static-only if(BUILD_SHARED_LIBS) - set(ENABLE_SHARED "yes") - set(LIBCURL_NO_SHARED "") - set(CPPFLAG_CURL_STATICLIB "") + set(ENABLE_SHARED "yes") + set(LIBCURL_PC_REQUIRES "") + set(LIBCURL_PC_LIBS "") + set(LIBCURL_PC_CFLAGS "") else() - set(ENABLE_SHARED "no") - set(LIBCURL_NO_SHARED "${LIBCURL_LIBS}") - set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB") + set(ENABLE_SHARED "no") + set(LIBCURL_PC_REQUIRES "${LIBCURL_PC_REQUIRES_PRIVATE}") + set(LIBCURL_PC_LIBS "${LIBCURL_PC_LIBS_PRIVATE}") + set(LIBCURL_PC_CFLAGS "${LIBCURL_PC_CFLAGS_PRIVATE}") endif() if(BUILD_STATIC_LIBS) - set(ENABLE_STATIC "yes") + set(ENABLE_STATIC "yes") else() - set(ENABLE_STATIC "no") - endif() - # "a" (Linux) or "lib" (Windows) - string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(prefix "${CMAKE_INSTALL_PREFIX}") - # Set this to "yes" to append all libraries on which -lcurl is dependent - set(REQUIRE_LIB_DEPS "no") - # SUPPORT_FEATURES - # SUPPORT_PROTOCOLS - set(VERSIONNUM "${CURL_VERSION_NUM}") - - # Finally generate a "curl-config" matching this config - # Use: - # * ENABLE_SHARED - # * ENABLE_STATIC - configure_file("${CURL_SOURCE_DIR}/curl-config.in" - "${CURL_BINARY_DIR}/curl-config" @ONLY) + set(ENABLE_STATIC "no") + endif() + + # Finally generate a "curl-config" matching this config. + # Consumed variables: + # CC + # CONFIGURE_OPTIONS + # CURLVERSION + # CURL_CA_BUNDLE + # ENABLE_SHARED + # ENABLE_STATIC + # exec_prefix + # includedir + # LDFLAGS + # LIBCURL_PC_CFLAGS + # LIBCURL_PC_LIBS_PRIVATE + # libdir + # libext + # prefix + # SSL_BACKENDS + # SUPPORT_FEATURES + # SUPPORT_PROTOCOLS + # VERSIONNUM + configure_file( + "${CURL_SOURCE_DIR}/curl-config.in" + "${CURL_BINARY_DIR}/curl-config" @ONLY) install(FILES "${CURL_BINARY_DIR}/curl-config" - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) # Finally generate a pkg-config file matching this config - configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in" - "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) + # Consumed variables: + # CURLVERSION + # exec_prefix + # includedir + # LIBCURL_PC_CFLAGS + # LIBCURL_PC_CFLAGS_PRIVATE + # LIBCURL_PC_LIBS + # LIBCURL_PC_LIBS_PRIVATE + # LIBCURL_PC_REQUIRES + # LIBCURL_PC_REQUIRES_PRIVATE + # libdir + # prefix + # SUPPORT_FEATURES + # SUPPORT_PROTOCOLS + configure_file( + "${CURL_SOURCE_DIR}/libcurl.pc.in" + "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) install(FILES "${CURL_BINARY_DIR}/libcurl.pc" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") - # install headers + # Install headers install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.h") + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.h") include(CMakePackageConfigHelpers) write_basic_package_version_file( - "${version_config}" - VERSION ${CURL_VERSION} - COMPATIBILITY SameMajorVersion - ) - file(READ "${version_config}" generated_version_config) - file(WRITE "${version_config}" - "if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\") + "${_version_config}" + VERSION ${CURL_VERSION} + COMPATIBILITY SameMajorVersion) + file(READ "${_version_config}" _generated_version_config) + file(WRITE "${_version_config}" " + if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\") # Version 8 satisfies version 7... requirements set(PACKAGE_FIND_VERSION_MAJOR 8) set(PACKAGE_FIND_VERSION_COUNT 1) - endif() - ${generated_version_config}" - ) - - # Use: - # * TARGETS_EXPORT_NAME - # * PROJECT_NAME - configure_package_config_file(CMake/curl-config.cmake.in - "${project_config}" - INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} - ) + endif() + ${_generated_version_config}") + + # Consumed custom variables: + # LIB_SELECTED + # TARGETS_EXPORT_NAME + # USE_OPENSSL + # USE_ZLIB + configure_package_config_file("CMake/curl-config.cmake.in" + "${_project_config}" + INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} + PATH_VARS CMAKE_INSTALL_INCLUDEDIR) if(CURL_ENABLE_EXPORT_TARGET) - install( - EXPORT "${TARGETS_EXPORT_NAME}" - NAMESPACE "${PROJECT_NAME}::" - DESTINATION ${CURL_INSTALL_CMAKE_DIR} - ) + install(EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION ${CURL_INSTALL_CMAKE_DIR}) endif() - install( - FILES ${version_config} ${project_config} - DESTINATION ${CURL_INSTALL_CMAKE_DIR} - ) + install(FILES ${_version_config} ${_project_config} + DESTINATION ${CURL_INSTALL_CMAKE_DIR}) # Workaround for MSVS10 to avoid the Dialog Hell # FIXME: This could be removed with future version of CMake. if(MSVC_VERSION EQUAL 1600) - set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") - if(EXISTS "${CURL_SLN_FILENAME}") - file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n") + set(_curl_sln_filename "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln") + if(EXISTS "${_curl_sln_filename}") + file(APPEND "${_curl_sln_filename}" "\n# This should be regenerated!\n") endif() endif() 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) + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake" + IMMEDIATE @ONLY) add_custom_target(curl_uninstall - COMMAND ${CMAKE_COMMAND} -P - ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake) + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake") endif() + + install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) endif() diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 6d6abfbb0..00b346ef8 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -34,24 +34,32 @@ #endif /* Compile-time deprecation macros. */ -#if defined(__GNUC__) && \ - ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) && \ +#if (defined(__GNUC__) && \ + ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ !defined(__INTEL_COMPILER) && \ !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL) #define CURL_DEPRECATED(version, message) \ __attribute__((deprecated("since " # version ". " message))) +#if defined(__IAR_SYSTEMS_ICC__) +#define CURL_IGNORE_DEPRECATION(statements) \ + _Pragma("diag_suppress=Pe1444") \ + statements \ + _Pragma("diag_default=Pe1444") +#else #define CURL_IGNORE_DEPRECATION(statements) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ statements \ _Pragma("GCC diagnostic pop") +#endif #else #define CURL_DEPRECATED(version, message) #define CURL_IGNORE_DEPRECATION(statements) statements #endif #include "curlver.h" /* libcurl version defines */ -#include "system.h" /* determine things run-time */ +#include "system.h" /* determine things runtime */ #include #include @@ -68,8 +76,8 @@ #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) #if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) -/* The check above prevents the winsock2 inclusion if winsock.h already was - included, since they can't co-exist without problems */ +/* The check above prevents the winsock2.h inclusion if winsock.h already was + included, since they cannot co-exist without problems */ #include #include #endif @@ -189,9 +197,9 @@ struct curl_httppost { files */ long flags; /* as defined below */ -/* specified content is a file name */ +/* specified content is a filename */ #define CURL_HTTPPOST_FILENAME (1<<0) -/* specified content is a file name */ +/* specified content is a filename */ #define CURL_HTTPPOST_READFILE (1<<1) /* name is only stored pointer do not free in formfree */ #define CURL_HTTPPOST_PTRNAME (1<<2) @@ -207,8 +215,8 @@ struct curl_httppost { /* use size in 'contentlen', added in 7.46.0 */ #define CURL_HTTPPOST_LARGE (1<<7) - char *showfilename; /* The file name to show. If not set, the - actual file name will be used (if this + char *showfilename; /* The filename to show. If not set, the + actual filename will be used (if this is a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ @@ -350,13 +358,13 @@ typedef long (*curl_chunk_bgn_callback)(const void *transfer_info, download of an individual chunk finished. Note! After this callback was set then it have to be called FOR ALL chunks. Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC. - This is the reason why we don't need "transfer_info" parameter in this + This is the reason why we do not need "transfer_info" parameter in this callback and we are not interested in "remains" parameter too. */ typedef long (*curl_chunk_end_callback)(void *ptr); /* return codes for FNMATCHFUNCTION */ #define CURL_FNMATCHFUNC_MATCH 0 /* string corresponds to the pattern */ -#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern doesn't match the string */ +#define CURL_FNMATCHFUNC_NOMATCH 1 /* pattern does not match the string */ #define CURL_FNMATCHFUNC_FAIL 2 /* an error occurred */ /* callback type for wildcard downloading pattern matching. If the @@ -368,7 +376,7 @@ typedef int (*curl_fnmatch_callback)(void *ptr, /* These are the return codes for the seek callbacks */ #define CURL_SEEKFUNC_OK 0 #define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ -#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking cannot be done, so libcurl might try other means instead */ typedef int (*curl_seek_callback)(void *instream, curl_off_t offset, @@ -451,7 +459,7 @@ typedef curlioerr (*curl_ioctl_callback)(CURL *handle, #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* * The following typedef's are signatures of malloc, free, realloc, strdup and - * calloc respectively. Function pointers of these types can be passed to the + * calloc respectively. Function pointers of these types can be passed to the * curl_global_init_mem() function to set user defined memory management * callback routines. */ @@ -539,17 +547,17 @@ typedef enum { CURLE_WRITE_ERROR, /* 23 */ CURLE_OBSOLETE24, /* 24 - NOT USED */ CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ - CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_READ_ERROR, /* 26 - could not open/read from file */ CURLE_OUT_OF_MEMORY, /* 27 */ CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ CURLE_OBSOLETE29, /* 29 - NOT USED */ CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ CURLE_OBSOLETE32, /* 32 - NOT USED */ - CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" did not work */ CURLE_HTTP_POST_ERROR, /* 34 */ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ - CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - could not resume download */ CURLE_FILE_COULDNT_READ_FILE, /* 37 */ CURLE_LDAP_CANNOT_BIND, /* 38 */ CURLE_LDAP_SEARCH_FAILED, /* 39 */ @@ -573,9 +581,9 @@ typedef enum { CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ CURLE_OBSOLETE57, /* 57 - NOT IN USE */ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ - CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CIPHER, /* 59 - could not use specified cipher */ CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint - wasn't verified fine */ + was not verified fine */ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ CURLE_OBSOLETE62, /* 62 - NOT IN USE since 7.82.0 */ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ @@ -604,7 +612,7 @@ typedef enum { CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL connection */ CURLE_AGAIN, /* 81 - socket is not ready for send/recv, - wait till it's ready and try again (Added + wait till it is ready and try again (Added in 7.18.2) */ CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or wrong format (Added in 7.19.0) */ @@ -713,6 +721,8 @@ typedef enum { with them. */ #define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 #define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 +#define CURLOPT_OBSOLETE72 9999 +#define CURLOPT_OBSOLETE40 9999 #endif /* !CURL_NO_OLDIES */ @@ -763,7 +773,7 @@ typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ void *ssl_ctx, /* actually an OpenSSL - or WolfSSL SSL_CTX, + or wolfSSL SSL_CTX, or an mbedTLS mbedtls_ssl_config */ void *userptr); @@ -780,7 +790,7 @@ typedef enum { CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the - host name rather than the IP address. added + hostname rather than the IP address. added in 7.18.0 */ } curl_proxytype; /* this enum was added in 7.10 */ @@ -860,7 +870,7 @@ 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. + CURLKHSTAT_DEFER, /* do not accept it, but we cannot 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 */ @@ -1080,7 +1090,7 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, #define CURLOPT(na,t,nu) na = t + nu #define CURLOPTDEPRECATED(na,t,nu,v,m) na CURL_DEPRECATED(v,m) = t + nu -/* CURLOPT aliases that make no run-time difference */ +/* CURLOPT aliases that make no runtime difference */ /* 'char *' argument to a string with a trailing zero */ #define CURLOPTTYPE_STRINGPOINT CURLOPTTYPE_OBJECTPOINT @@ -1147,7 +1157,7 @@ typedef enum { * * For large file support, there is also a _LARGE version of the key * which takes an off_t type, allowing platforms with larger off_t - * sizes to handle larger files. See below for INFILESIZE_LARGE. + * sizes to handle larger files. See below for INFILESIZE_LARGE. */ CURLOPT(CURLOPT_INFILESIZE, CURLOPTTYPE_LONG, 14), @@ -1180,7 +1190,7 @@ typedef enum { * * Note there is also a _LARGE version of this key which uses * off_t types, allowing for large file offsets on platforms which - * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. */ CURLOPT(CURLOPT_RESUME_FROM, CURLOPTTYPE_LONG, 21), @@ -1242,8 +1252,7 @@ typedef enum { /* send linked-list of post-transfer QUOTE commands */ CURLOPT(CURLOPT_POSTQUOTE, CURLOPTTYPE_SLISTPOINT, 39), - /* OBSOLETE, do not use! */ - CURLOPT(CURLOPT_OBSOLETE40, CURLOPTTYPE_OBJECTPOINT, 40), + /* 40 is not used */ /* talk a lot */ CURLOPT(CURLOPT_VERBOSE, CURLOPTTYPE_LONG, 41), @@ -1316,9 +1325,9 @@ typedef enum { /* Set the interface string to use as outgoing network interface */ CURLOPT(CURLOPT_INTERFACE, CURLOPTTYPE_STRINGPOINT, 62), - /* Set the krb4/5 security level, this also enables krb4/5 awareness. This - * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but doesn't match one of these, 'private' will be used. */ + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but does not match one of these, 'private' will be used. */ CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ @@ -1344,22 +1353,20 @@ typedef enum { /* Max amount of cached alive connections */ CURLOPT(CURLOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 71), - /* OBSOLETE, do not use! */ - CURLOPT(CURLOPT_OBSOLETE72, CURLOPTTYPE_LONG, 72), - + /* 72 = OBSOLETE */ /* 73 = OBSOLETE */ /* Set to explicitly use a new connection for the upcoming transfer. - Do not use this unless you're absolutely sure of this, as it makes the + Do not use this unless you are absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ CURLOPT(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74), /* Set to explicitly forbid the upcoming transfer's connection to be reused - when done. Do not use this unless you're absolutely sure of this, as it + when done. Do not use this unless you are absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ CURLOPT(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75), - /* Set to a file name that contains random data for libcurl to use to + /* Set to a filename that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ CURLOPTDEPRECATED(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76, 7.84.0, "Serves no purpose anymore"), @@ -1386,11 +1393,11 @@ typedef enum { * provided hostname. */ CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), - /* Specify which file name to write all known cookies in after completed - operation. Set file name to "-" (dash) to make it go to stdout. */ + /* Specify which filename to write all known cookies in after completed + operation. Set filename to "-" (dash) to make it go to stdout. */ CURLOPT(CURLOPT_COOKIEJAR, CURLOPTTYPE_STRINGPOINT, 82), - /* Specify which SSL ciphers to use */ + /* Specify which TLS 1.2 (1.1, 1.0) ciphers to use */ CURLOPT(CURLOPT_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 83), /* Specify which HTTP version to use! This must be set to one of the @@ -1486,7 +1493,7 @@ typedef enum { CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), /* Set the ssl context callback function, currently only for OpenSSL or - WolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. + wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. The function must match the curl_ssl_ctx_callback prototype. */ CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), @@ -1506,7 +1513,7 @@ typedef enum { CURLOPT(CURLOPT_PROXYAUTH, CURLOPTTYPE_VALUES, 111), /* Option that changes the timeout, in seconds, associated with getting a - response. This is different from transfer timeout time and essentially + 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), @@ -1520,7 +1527,7 @@ typedef enum { an HTTP or FTP server. Note there is also _LARGE version which adds large file support for - platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ CURLOPT(CURLOPT_MAXFILESIZE, CURLOPTTYPE_LONG, 114), /* See the comment for INFILESIZE above, but in short, specifies @@ -1528,17 +1535,17 @@ typedef enum { */ CURLOPT(CURLOPT_INFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 115), - /* Sets the continuation offset. There is also a CURLOPTTYPE_LONG version + /* Sets the continuation offset. There is also a CURLOPTTYPE_LONG version * of this; look above for RESUME_FROM. */ CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), /* Sets the maximum size of data that will be downloaded from - * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), - /* Set this option to the file name of your .netrc file you want libcurl + /* Set this option to the filename of your .netrc file you want libcurl to parse (using the CURLOPT_NETRC option). If not set, libcurl will do a poor attempt to find the user's home directory and check for a .netrc file in there. */ @@ -1685,7 +1692,7 @@ typedef enum { /* Callback function for opening socket (instead of socket(2)). Optionally, callback is able change the address or refuse to connect returning - CURL_SOCKET_BAD. The callback should have type + CURL_SOCKET_BAD. The callback should have type curl_opensocket_callback */ CURLOPT(CURLOPT_OPENSOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 163), CURLOPT(CURLOPT_OPENSOCKETDATA, CURLOPTTYPE_CBPOINT, 164), @@ -1755,7 +1762,7 @@ typedef enum { CURLOPTDEPRECATED(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182, 7.85.0, "Use CURLOPT_REDIR_PROTOCOLS_STR"), - /* set the SSH knownhost file name to use */ + /* set the SSH knownhost filename to use */ CURLOPT(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_STRINGPOINT, 183), /* set the SSH host key callback, must point to a curl_sshkeycallback @@ -1836,7 +1843,7 @@ typedef enum { future libcurl release. libcurl will ask for the compressed methods it knows of, and if that - isn't any, it will not ask for transfer-encoding at all even if this + is not any, it will not ask for transfer-encoding at all even if this option is set to 1. */ @@ -1938,7 +1945,7 @@ typedef enum { /* Service Name */ CURLOPT(CURLOPT_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 236), - /* Wait/don't wait for pipe/mutex to clarify */ + /* Wait/do not wait for pipe/mutex to clarify */ CURLOPT(CURLOPT_PIPEWAIT, CURLOPTTYPE_LONG, 237), /* Set the protocol used when curl is given a URL without a protocol */ @@ -2014,7 +2021,7 @@ typedef enum { /* password for the SSL private key for proxy */ CURLOPT(CURLOPT_PROXY_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 258), - /* Specify which SSL ciphers to use for proxy */ + /* Specify which TLS 1.2 (1.1, 1.0) ciphers to use for proxy */ CURLOPT(CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 259), /* CRL file for proxy */ @@ -2099,7 +2106,7 @@ typedef enum { /* alt-svc control bitmask */ CURLOPT(CURLOPT_ALTSVC_CTRL, CURLOPTTYPE_LONG, 286), - /* alt-svc cache file name to possibly read from/write to */ + /* alt-svc cache filename to possibly read from/write to */ CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287), /* maximum age (idle time) of a connection to consider it for reuse @@ -2125,13 +2132,13 @@ typedef enum { /* the EC curves requested by the TLS client (RFC 8422, 5.1); * OpenSSL support via 'set_groups'/'set_curves': - * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html + * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/ */ CURLOPT(CURLOPT_SSL_EC_CURVES, CURLOPTTYPE_STRINGPOINT, 298), /* HSTS bitmask */ CURLOPT(CURLOPT_HSTS_CTRL, CURLOPTTYPE_LONG, 299), - /* HSTS file name */ + /* HSTS filename */ CURLOPT(CURLOPT_HSTS, CURLOPTTYPE_STRINGPOINT, 300), /* HSTS read callback */ @@ -2195,7 +2202,7 @@ typedef enum { /* specify which protocols that libcurl is allowed to follow directs to */ CURLOPT(CURLOPT_REDIR_PROTOCOLS_STR, CURLOPTTYPE_STRINGPOINT, 319), - /* websockets options */ + /* WebSockets options */ CURLOPT(CURLOPT_WS_OPTIONS, CURLOPTTYPE_LONG, 320), /* CA cache timeout */ @@ -2210,9 +2217,12 @@ typedef enum { /* millisecond version */ CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324), - /* set ECH configuration */ + /* set ECH configuration */ CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325), + /* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */ + CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -2263,9 +2273,9 @@ typedef enum { /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ enum { - CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd - like the library to choose the best possible - for us! */ + CURL_HTTP_VERSION_NONE, /* setting this means we do not care, and that we + would like the library to choose the best + possible for us! */ CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ @@ -2425,7 +2435,7 @@ CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name); * * DESCRIPTION * - * Set mime part remote file name. + * Set mime part remote filename. */ CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part, const char *filename); @@ -2634,7 +2644,7 @@ CURL_EXTERN char *curl_getenv(const char *variable); * * DESCRIPTION * - * Returns a static ascii string of the libcurl version. + * Returns a static ASCII string of the libcurl version. */ CURL_EXTERN char *curl_version(void); @@ -2706,10 +2716,10 @@ CURL_EXTERN CURLcode curl_global_init(long flags); * DESCRIPTION * * curl_global_init() or curl_global_init_mem() should be invoked exactly once - * for each application that uses libcurl. This function can be used to + * for each application that uses libcurl. This function can be used to * initialize libcurl and set user defined memory management callback - * functions. Users can implement memory management routines to check for - * memory leaks, check for mis-use of the curl library etc. User registered + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered * callback routines will be invoked by this library instead of the system * memory management routines like malloc, free etc. */ @@ -2827,7 +2837,7 @@ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ struct curl_certinfo { int num_of_certs; /* number of certificates with information */ - struct curl_slist **certinfo; /* for each index in this array, there's a + struct curl_slist **certinfo; /* for each index in this array, there is a linked list with textual information for a certificate in the format "name:content". eg "Subject:foo", "Issuer:bar", etc. */ @@ -2942,7 +2952,8 @@ typedef enum { CURLINFO_CONN_ID = CURLINFO_OFF_T + 64, CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65, CURLINFO_USED_PROXY = CURLINFO_LONG + 66, - CURLINFO_LASTONE = 66 + CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67, + CURLINFO_LASTONE = 67 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3018,7 +3029,7 @@ typedef enum { } CURLSHcode; typedef enum { - CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_NONE, /* do not use */ CURLSHOPT_SHARE, /* specify a data type to share */ CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ @@ -3177,7 +3188,7 @@ CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); * DESCRIPTION * * The curl_easy_strerror function may be used to turn a CURLcode value - * into the equivalent human readable error string. This is useful + * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ CURL_EXTERN const char *curl_easy_strerror(CURLcode); @@ -3188,7 +3199,7 @@ CURL_EXTERN const char *curl_easy_strerror(CURLcode); * DESCRIPTION * * The curl_share_strerror function may be used to turn a CURLSHcode value - * into the equivalent human readable error string. This is useful + * into the equivalent human readable error string. This is useful * for printing meaningful error messages. */ CURL_EXTERN const char *curl_share_strerror(CURLSHcode); @@ -3225,9 +3236,11 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #include "options.h" #include "header.h" #include "websockets.h" +#ifndef CURL_SKIP_INCLUDE_MPRINTF #include "mprintf.h" +#endif -/* the typechecker doesn't work in C++ (yet) */ +/* the typechecker does not work in C++ (yet) */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index e63ad2215..25ed10c46 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.8.0" +#define LIBCURL_VERSION "8.10.1" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 8 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_MINOR 10 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -48,7 +48,7 @@ Where XX, YY and ZZ are the main version, release and patch numbers in hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 appears as "0x090b07". This 6-digit (24 bits) hexadecimal number does not show pre-release number, @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080800 +#define LIBCURL_VERSION_NUM 0x080a01 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index 1285101c5..71b8dd467 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h @@ -50,7 +50,7 @@ CURL_EXTERN void curl_easy_cleanup(CURL *curl); * * Request internal information from the curl session with this function. * The third argument MUST be pointing to the specific type of the used option - * which is documented in each man page of the option. The data pointed to + * which is documented in each manpage of the option. The data pointed to * will be filled in accordingly and can be relied upon only if the function * returns CURLE_OK. This function is intended to get used *AFTER* a performed * transfer, all results from this function are undefined until the transfer diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h index 4f704548d..88059c851 100644 --- a/Utilities/cmcurl/include/curl/mprintf.h +++ b/Utilities/cmcurl/include/curl/mprintf.h @@ -32,12 +32,18 @@ extern "C" { #endif -#if (defined(__GNUC__) || defined(__clang__)) && \ +#ifndef CURL_TEMP_PRINTF +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(CURL_NO_FMT_CHECKS) #if defined(__MINGW32__) && !defined(__clang__) +#if defined(__MINGW_PRINTF_FORMAT) /* mingw-w64 3.0.0+. Needs stdio.h. */ #define CURL_TEMP_PRINTF(fmt, arg) \ - __attribute__((format(gnu_printf, fmt, arg))) + __attribute__((format(__MINGW_PRINTF_FORMAT, fmt, arg))) +#else +#define CURL_TEMP_PRINTF(fmt, arg) +#endif #else #define CURL_TEMP_PRINTF(fmt, arg) \ __attribute__((format(printf, fmt, arg))) @@ -45,6 +51,7 @@ extern "C" { #else #define CURL_TEMP_PRINTF(fmt, arg) #endif +#endif CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2); diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index 561470ce7..7b6c351ad 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h @@ -24,7 +24,7 @@ * ***************************************************************************/ /* - This is an "external" header file. Don't give away any internals here! + This is an "external" header file. Do not give away any internals here! GOALS @@ -66,7 +66,7 @@ typedef enum { CURLM_OK, CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ - CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you are in deep sh*t */ CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ @@ -109,7 +109,7 @@ struct CURLMsg { typedef struct CURLMsg CURLMsg; /* Based on poll(2) structure and values. - * We don't use pollfd and POLL* constants explicitly + * We do not use pollfd and POLL* constants explicitly * to cover platforms without poll(). */ #define CURL_WAIT_POLLIN 0x0001 #define CURL_WAIT_POLLPRI 0x0002 @@ -205,7 +205,7 @@ CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); /* * Name: curl_multi_perform() * - * Desc: When the app thinks there's data available for curl it calls this + * Desc: When the app thinks there is data available for curl it calls this * function to read/write whatever there is right now. This returns * as soon as the reads and writes are done. This function does not * require that there actually is data available for reading or that @@ -236,7 +236,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); /* * Name: curl_multi_info_read() * - * Desc: Ask the multi handle if there's any messages/informationals from + * Desc: Ask the multi handle if there is any messages/informationals from * the individual transfers. Messages include informationals such as * error code from the transfer or just the fact that a transfer is * completed. More details on these should be written down as well. @@ -253,7 +253,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * we will provide the particular "transfer handle" in that struct * and that should/could/would be used in subsequent * curl_easy_getinfo() calls (or similar). The point being that we - * must never expose complex structs to applications, as then we'll + * must never expose complex structs to applications, as then we will * undoubtably get backwards compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out @@ -268,7 +268,7 @@ CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, * Name: curl_multi_strerror() * * Desc: The curl_multi_strerror function may be used to turn a CURLMcode - * value into the equivalent human readable error string. This is + * value into the equivalent human readable error string. This is * useful for printing meaningful error messages. * * Returns: A pointer to a null-terminated error message. @@ -282,7 +282,7 @@ CURL_EXTERN const char *curl_multi_strerror(CURLMcode); * Desc: An alternative version of curl_multi_perform() that allows the * application to pass in one of the file descriptors that have been * detected to have "action" on them and let libcurl perform. - * See man page for details. + * See manpage for details. */ #define CURL_POLL_NONE 0 #define CURL_POLL_IN 1 diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 81a1b817d..e5be25684 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -31,7 +31,7 @@ * changed. * * In order to differentiate between platforms/compilers/architectures use - * only compiler built in predefined preprocessor symbols. + * only compiler built-in predefined preprocessor symbols. * * curl_off_t * ---------- @@ -46,7 +46,7 @@ * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall * only be violated if off_t is the only 64-bit data type available and the * size of off_t is independent of large file support settings. Keep your - * build on the safe side avoiding an off_t gating. If you have a 64-bit + * build on the safe side avoiding an off_t gating. If you have a 64-bit * off_t then take for sure that another 64-bit data type exists, dig deeper * and you will find it. * @@ -402,7 +402,7 @@ # define CURL_PULL_SYS_SOCKET_H 1 #else -/* generic "safe guess" on old 32 bit style */ +/* generic "safe guess" on old 32-bit style */ # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" # define CURL_FORMAT_CURL_OFF_TU "lu" diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index 873a49e02..e532e6997 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h @@ -34,11 +34,11 @@ * _curl_easy_setopt_err_sometype below * * NOTE: We use two nested 'if' statements here instead of the && operator, in - * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x + * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x * when compiling with -Wlogical-op. * - * To add an option that uses the same type as an existing option, you'll just - * need to extend the appropriate _curl_*_option macro + * To add an option that uses the same type as an existing option, you will + * just need to extend the appropriate _curl_*_option macro */ #define curl_easy_setopt(handle, option, value) \ __extension__({ \ @@ -245,7 +245,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, /* To add a new option to one of the groups, just add * (option) == CURLOPT_SOMETHING - * to the or-expression. If the option takes a long or curl_off_t, you don't + * to the or-expression. If the option takes a long or curl_off_t, you do not * have to do anything */ @@ -678,7 +678,7 @@ typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX - * this will of course break if we're included before OpenSSL headers... + * this will of course break if we are included before OpenSSL headers... */ typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index 19388c3c0..b4a6e5d56 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -97,11 +97,12 @@ typedef enum { #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the scheme is unknown. */ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the host name in punycode */ +#define CURLU_PUNYCODE (1<<12) /* get the hostname in punycode */ #define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ #define CURLU_GET_EMPTY (1<<14) /* allow empty queries and fragments when extracting the URL or the components */ +#define CURLU_NO_GUESS_SCHEME (1<<15) /* for get, do not accept a guess */ typedef struct Curl_URL CURLU; @@ -142,7 +143,7 @@ CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, /* * curl_url_strerror() turns a CURLUcode value into the equivalent human - * readable error string. This is useful for printing meaningful error + * readable error string. This is useful for printing meaningful error * messages. */ CURL_EXTERN const char *curl_url_strerror(CURLUcode); diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index ca88a4d30..3f99f9f27 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -23,29 +23,27 @@ ########################################################################### set(LIB_NAME libcurl) set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library") -add_definitions(-DBUILDING_LIBCURL) +add_definitions("-DBUILDING_LIBCURL") -configure_file(curl_config.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) +configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") +# Get 'CSOURCES', 'HHEADERS' variables transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") -include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake) +include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") # DllMain is added later for DLL builds only. -list(REMOVE_ITEM CSOURCES dllmain.c) +list(REMOVE_ITEM CSOURCES "dllmain.c") -list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) +list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") # The rest of the build -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) -include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories( + "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" +) if(USE_ARES) - include_directories(${CARES_INCLUDE_DIR}) + include_directories(${CARES_INCLUDE_DIRS}) endif() #----------------------------------------------------------------------------- @@ -88,42 +86,19 @@ return() # The rest of this file is not needed for building within CMake. if(BUILD_TESTING) add_library( - curlu # special libcurlu library just for unittests + curlu # special libcurlu library just for unittests STATIC EXCLUDE_FROM_ALL ${HHEADERS} ${CSOURCES} ) - target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) + target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB") target_link_libraries(curlu PRIVATE ${CURL_LIBS}) endif() if(ENABLE_CURLDEBUG) # We must compile these sources separately to avoid memdebug.h redefinitions # applying to them. - set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) -endif() - -transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") -include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) - -if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR - CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR - CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR - CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR - - # FreeBSD comes with the a.out and elf flavours - # but a.out was supported up to version 3.x and - # elf from 3.x. I cannot imagine someone running - # CMake on those ancient systems - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR - - CMAKE_SYSTEM_NAME STREQUAL "Haiku") - - math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}") - set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}") -else() - unset(CMAKESONAME) + set_source_files_properties("memdebug.c" "curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() ## Library definition @@ -158,13 +133,12 @@ if(SHARE_LIB_OBJECT) # exported libcurl symbols. We handle exports via libcurl.def instead. # Except with symbol hiding disabled or debug mode enabled, when we export # _all_ symbols from libcurl DLL, without using libcurl.def. - set_property(TARGET ${LIB_OBJECT} APPEND - PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") endif() target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) - if(HIDES_CURL_PRIVATE_SYMBOLS) + if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() @@ -175,22 +149,21 @@ if(SHARE_LIB_OBJECT) endif() target_include_directories(${LIB_OBJECT} INTERFACE - $ - $) + "$" + "$") set(LIB_SOURCE $) else() set(LIB_SOURCE ${HHEADERS} ${CSOURCES}) endif() -# we want it to be called libcurl on all platforms +# We want it to be called libcurl on all platforms if(BUILD_STATIC_LIBS) list(APPEND libcurl_export ${LIB_STATIC}) add_library(${LIB_STATIC} STATIC ${LIB_SOURCE}) add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC}) if(WIN32) - set_property(TARGET ${LIB_OBJECT} APPEND - PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") endif() target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS}) # Remove the "lib" prefix since the library is already named "libcurl". @@ -198,7 +171,7 @@ if(BUILD_STATIC_LIBS) PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") - if(HIDES_CURL_PRIVATE_SYMBOLS) + if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() @@ -207,14 +180,10 @@ if(BUILD_STATIC_LIBS) INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) endif() - if(CMAKEVERSION AND CMAKESONAME) - set_target_properties(${LIB_STATIC} PROPERTIES - VERSION ${CMAKEVERSION} SOVERSION ${CMAKESONAME}) - endif() target_include_directories(${LIB_STATIC} INTERFACE - $ - $) + "$" + "$") endif() if(BUILD_SHARED_LIBS) @@ -223,17 +192,17 @@ if(BUILD_SHARED_LIBS) add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) if(WIN32 OR CYGWIN) if(CYGWIN) - # For cygwin always compile dllmain.c as a separate unit since it - # includes windows.h, which shouldn't be included in other units. - set_source_files_properties(dllmain.c PROPERTIES + # For Cygwin always compile dllmain.c as a separate unit since it + # includes windows.h, which should not be included in other units. + set_source_files_properties("dllmain.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c") endif() if(WIN32) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc) - if(HIDES_CURL_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/libcurl.def") + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc") + if(CURL_HIDES_PRIVATE_SYMBOLS) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/lib/libcurl.def") endif() endif() target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS}) @@ -242,7 +211,7 @@ if(BUILD_SHARED_LIBS) PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" POSITION_INDEPENDENT_CODE ON) - if(HIDES_CURL_PRIVATE_SYMBOLS) + if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() @@ -251,14 +220,82 @@ if(BUILD_SHARED_LIBS) INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) endif() - if(CMAKEVERSION AND CMAKESONAME) + + target_include_directories(${LIB_SHARED} INTERFACE + "$" + "$") + + if(CMAKE_DLL_NAME_WITH_SOVERSION OR + CYGWIN OR + APPLE OR + CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR + CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR + CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR + # FreeBSD comes with the a.out and ELF flavours but a.out was supported + # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake + # on those ancient systems. + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(_soversion_default TRUE) + else() + set(_soversion_default FALSE) + endif() + + option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${_soversion_default}) + option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF) + + if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS) + # Get 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' variables + transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") + include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") + + math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}") + set(_cmakeversion "${_cmakesoname}.${VERSIONDEL}.${VERSIONADD}") + endif() + + if(CURL_LIBCURL_SOVERSION) set_target_properties(${LIB_SHARED} PROPERTIES - VERSION ${CMAKEVERSION} SOVERSION ${CMAKESONAME}) + VERSION "${_cmakeversion}" SOVERSION "${_cmakesoname}") endif() - target_include_directories(${LIB_SHARED} INTERFACE - $ - $) + ## Versioned symbols + + if(CURL_LIBCURL_VERSIONED_SYMBOLS) + if(NOT DEFINED CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX) + # Default to prefixes used by autotools + if(CURL_WITH_MULTI_SSL) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MULTISSL_") + elseif(CURL_USE_OPENSSL) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_") + elseif(CURL_USE_MBEDTLS) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_") + elseif(CURL_USE_BEARSSL) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_") + elseif(CURL_USE_WOLFSSL) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_") + elseif(CURL_USE_GNUTLS) + set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "GNUTLS_") + endif() + endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers" " + HIDDEN {}; + CURL_${CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX}${_cmakesoname} + { + global: curl_*; + local: *; + };") + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers") + check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS) + if(HAVE_VERSIONED_SYMBOLS) + # Superseded by LINK_OPTIONS in CMake 3.13 and later. + set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}") + else() + message(WARNING "Versioned symbols requested, but not supported by the toolchain.") + endif() + unset(CMAKE_REQUIRED_LINK_OPTIONS) + endif() endif() add_library(${LIB_NAME} ALIAS ${LIB_SELECTED}) @@ -283,7 +320,7 @@ if(CURL_ENABLE_EXPORT_TARGET) endif() export(TARGETS ${libcurl_export} - FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake - NAMESPACE ${PROJECT_NAME}:: + FILE "${PROJECT_BINARY_DIR}/libcurl-target.cmake" + NAMESPACE ${PROJECT_NAME}:: ) endif() diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index b72a59612..dcedc491c 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -211,7 +211,7 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) CURLcode result = CURLE_OK; FILE *fp; - /* we need a private copy of the file name so that the altsvc cache file + /* we need a private copy of the filename so that the altsvc cache file name survives an easy handle reset */ free(asi->filename); asi->filename = strdup(file); @@ -270,7 +270,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " - "%u %d\n", + "%u %u\n", Curl_alpnid2str(as->src.alpnid), src6_pre, as->src.host, src6_post, as->src.port, @@ -337,13 +337,13 @@ CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) */ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; if(*altsvcp) { + struct Curl_llist_node *e; + struct Curl_llist_node *n; struct altsvcinfo *altsvc = *altsvcp; - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; + for(e = Curl_llist_head(&altsvc->list); e; e = n) { + struct altsvc *as = Curl_node_elem(e); + n = Curl_node_next(e); altsvc_free(as); } free(altsvc->filename); @@ -358,8 +358,6 @@ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp) CURLcode Curl_altsvc_save(struct Curl_easy *data, struct altsvcinfo *altsvc, const char *file) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; CURLcode result = CURLE_OK; FILE *out; char *tempstore = NULL; @@ -373,17 +371,19 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data, file = altsvc->filename; if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0]) - /* marked as read-only, no file or zero length file name */ + /* marked as read-only, no file or zero length filename */ return CURLE_OK; result = Curl_fopen(data, file, &out, &tempstore); if(!result) { + struct Curl_llist_node *e; + struct Curl_llist_node *n; 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); - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; + for(e = Curl_llist_head(&altsvc->list); e; e = n) { + struct altsvc *as = Curl_node_elem(e); + n = Curl_node_next(e); result = altsvc_out(as, out); if(result) break; @@ -430,7 +430,7 @@ static bool hostcompare(const char *host, const char *check) if(hlen && (host[hlen - 1] == '.')) hlen--; if(hlen != clen) - /* they can't match if they have different lengths */ + /* they cannot match if they have different lengths */ return FALSE; return strncasecompare(host, check, hlen); } @@ -440,15 +440,15 @@ static bool hostcompare(const char *host, const char *check) static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, unsigned short srcport) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; - for(e = asi->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; + struct Curl_llist_node *e; + struct Curl_llist_node *n; + for(e = Curl_llist_head(&asi->list); e; e = n) { + struct altsvc *as = Curl_node_elem(e); + n = Curl_node_next(e); if((srcalpnid == as->src.alpnid) && (srcport == as->src.port) && hostcompare(srchost, as->src.host)) { - Curl_llist_remove(&asi->list, e, NULL); + Curl_node_remove(e); altsvc_free(as); } } @@ -462,7 +462,7 @@ static time_t altsvc_debugtime(void *unused) char *timestr = getenv("CURL_TIME"); (void)unused; if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); + long val = strtol(timestr, NULL, 10); return (time_t)val; } return time(NULL); @@ -477,11 +477,11 @@ static time_t altsvc_debugtime(void *unused) * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. * - * 'value' points to the header *value*. That's contents to the right of the + * 'value' points to the header *value*. That is contents to the right of the * header name. * * Currently this function rejects invalid data without returning an error. - * Invalid host name, port number will result in the specific alternative + * Invalid hostname, port number will result in the specific alternative * being rejected. Unknown protocols are skipped. */ CURLcode Curl_altsvc_parse(struct Curl_easy *data, @@ -531,7 +531,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, bool valid = TRUE; p++; if(*p != ':') { - /* host name starts here */ + /* hostname starts here */ const char *hostp = p; if(*p == '[') { /* pass all valid IPv6 letters - does not handle zone id */ @@ -549,7 +549,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."); + infof(data, "Excessive alt-svc hostname, ignoring."); valid = FALSE; } else { @@ -624,7 +624,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, num = strtoul(value_ptr, &end_ptr, 10); if((end_ptr != value_ptr) && (num < ULONG_MAX)) { if(strcasecompare("ma", option)) - maxage = num; + maxage = (time_t)num; else if(strcasecompare("persist", option) && (num == 1)) persist = TRUE; } @@ -651,7 +651,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, } else break; - /* after the double quote there can be a comma if there's another + /* after the double quote there can be a comma if there is another string or a semicolon if no more */ if(*p == ',') { /* comma means another alternative is presented */ @@ -677,26 +677,26 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, struct altsvc **dstentry, const int versions) /* one or more bits */ { - struct Curl_llist_element *e; - struct Curl_llist_element *n; + struct Curl_llist_node *e; + struct Curl_llist_node *n; time_t now = time(NULL); DEBUGASSERT(asi); DEBUGASSERT(srchost); DEBUGASSERT(dstentry); - for(e = asi->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; + for(e = Curl_llist_head(&asi->list); e; e = n) { + struct altsvc *as = Curl_node_elem(e); + n = Curl_node_next(e); if(as->expires < now) { /* an expired entry, remove */ - Curl_llist_remove(&asi->list, e, NULL); + Curl_node_remove(e); altsvc_free(as); continue; } if((as->src.alpnid == srcalpnid) && hostcompare(srchost, as->src.host) && (as->src.port == srcport) && - (versions & as->dst.alpnid)) { + (versions & (int)as->dst.alpnid)) { /* match */ *dstentry = as; return TRUE; diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h index 7fea1434a..48999efb3 100644 --- a/Utilities/cmcurl/lib/altsvc.h +++ b/Utilities/cmcurl/lib/altsvc.h @@ -47,8 +47,8 @@ struct altsvc { struct althost dst; time_t expires; bool persist; - int prio; - struct Curl_llist_element node; + unsigned int prio; + struct Curl_llist_node node; }; struct altsvcinfo { diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c index 139309b11..1321c53c4 100644 --- a/Utilities/cmcurl/lib/amigaos.c +++ b/Utilities/cmcurl/lib/amigaos.c @@ -117,7 +117,7 @@ void Curl_amiga_cleanup(void) #ifdef CURLRES_AMIGA /* - * Because we need to handle the different cases in hostip4.c at run-time, + * Because we need to handle the different cases in hostip4.c at runtime, * 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() diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h index 228b4466e..d641a01da 100644 --- a/Utilities/cmcurl/lib/arpa_telnet.h +++ b/Utilities/cmcurl/lib/arpa_telnet.h @@ -77,7 +77,7 @@ static const char * const telnetoptions[]= #define CURL_GA 249 /* Go Ahead, reverse the line */ #define CURL_SB 250 /* SuBnegotiation */ #define CURL_WILL 251 /* Our side WILL use this option */ -#define CURL_WONT 252 /* Our side WON'T use this option */ +#define CURL_WONT 252 /* Our side will not use this option */ #define CURL_DO 253 /* DO use this option! */ #define CURL_DONT 254 /* DON'T use this option! */ #define CURL_IAC 255 /* Interpret As Command */ diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 8fed61760..782e3ac65 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -65,7 +65,7 @@ # define CARES_STATICLIB #endif #include -#include /* really old c-ares didn't include this by +#include /* really old c-ares did not include this by itself */ #if ARES_VERSION >= 0x010500 @@ -112,8 +112,8 @@ struct thread_data { /* How long we are willing to wait for additional parallel responses after obtaining a "definitive" one. For old c-ares without getaddrinfo. - This is intended to equal the c-ares default timeout. cURL always uses that - default value. Unfortunately, c-ares doesn't expose its default timeout in + This is intended to equal the c-ares default timeout. cURL always uses that + default value. Unfortunately, c-ares does not expose its default timeout in its API, but it is officially documented as 5 seconds. See query_completed_cb() for an explanation of how this is used. @@ -126,8 +126,8 @@ static int ares_ver = 0; /* * Curl_resolver_global_init() - the generic low-level asynchronous name - * resolve API. Called from curl_global_init() to initialize global resolver - * environment. Initializes ares library. + * resolve API. Called from curl_global_init() to initialize global resolver + * environment. Initializes ares library. */ int Curl_resolver_global_init(void) { @@ -169,7 +169,7 @@ static void sock_state_cb(void *data, ares_socket_t socket_fd, * * Called from curl_easy_init() -> Curl_open() to initialize resolver * URL-state specific environment ('resolver' member of the UrlState - * structure). Fills the passed pointer by the initialized ares_channel. + * structure). Fills the passed pointer by the initialized ares_channel. */ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) { @@ -211,7 +211,7 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) * * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver * URL-state specific environment ('resolver' member of the UrlState - * structure). Destroys the ares channel. + * structure). Destroys the ares channel. */ void Curl_resolver_cleanup(void *resolver) { @@ -222,7 +222,7 @@ void Curl_resolver_cleanup(void *resolver) * Curl_resolver_duphandle() * * Called from curl_easy_duphandle() to duplicate resolver URL-state specific - * environment ('resolver' member of the UrlState structure). Duplicates the + * environment ('resolver' member of the UrlState structure). Duplicates the * 'from' ares channel and passes the resulting channel to the 'to' pointer. */ CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) @@ -250,12 +250,12 @@ void Curl_resolver_cancel(struct Curl_easy *data) } /* - * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We + * We are equivalent to Curl_resolver_cancel() for the c-ares resolver. We * never block. */ void Curl_resolver_kill(struct Curl_easy *data) { - /* We don't need to check the resolver state because we can be called safely + /* We do not need to check the resolver state because we can be called safely at any time and we always do the same thing. */ Curl_resolver_cancel(data); } @@ -280,7 +280,7 @@ static void destroy_async_data(struct Curl_async *async) /* * Curl_resolver_getsock() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking + * (using curl_multi_fdset()) wants to get our fd_set setup and we are talking * with ares. The caller must make sure that this function is only called when * we have a working ares channel. * @@ -350,7 +350,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) } if(num) { - nfds = Curl_poll(pfd, num, timeout_ms); + nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms); if(nfds < 0) return -1; } @@ -359,7 +359,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms) if(!nfds) /* Call ares_process() unconditionally here, even if we simply timed out - above, as otherwise the ares name resolve won't timeout! */ + above, as otherwise the ares name resolve will not timeout! */ ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD, ARES_SOCKET_BAD); else { @@ -394,8 +394,8 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, return CURLE_UNRECOVERABLE_POLL; #ifndef HAVE_CARES_GETADDRINFO - /* Now that we've checked for any last minute results above, see if there are - any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer + /* Now that we have checked for any last minute results above, see if there + are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer expires. */ if(res && res->num_pending @@ -410,7 +410,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time)); /* Cancel the raw c-ares request, which will fire query_completed_cb() with - ARES_ECANCELLED synchronously for all pending responses. This will + ARES_ECANCELLED synchronously for all pending responses. This will leave us with res->num_pending == 0, which is perfect for the next block. */ ares_cancel((ares_channel)data->state.async.resolver); @@ -523,7 +523,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, *entry = data->state.async.dns; if(result) - /* close the connection, since we can't return failure here without + /* close the connection, since we cannot return failure here without cleaning up this connection properly. */ connclose(data->conn, "c-ares resolve failed"); @@ -603,57 +603,57 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */ /* If there are responses still pending, we presume they must be the complementary IPv4 or IPv6 lookups that we started in parallel in - Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a + Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we have got a "definitive" response from one of a set of parallel queries, we need to - think about how long we're willing to wait for more responses. */ + think about how long we are willing to wait for more responses. */ if(res->num_pending /* Only these c-ares status values count as "definitive" for these - purposes. For example, ARES_ENODATA is what we expect when there is - no IPv6 entry for a domain name, and that's not a reason to get more - aggressive in our timeouts for the other response. Other errors are + purposes. For example, ARES_ENODATA is what we expect when there is + no IPv6 entry for a domain name, and that is not a reason to get more + aggressive in our timeouts for the other response. Other errors are either a result of bad input (which should affect all parallel requests), local or network conditions, non-definitive server responses, or us cancelling the request. */ && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { - /* Right now, there can only be up to two parallel queries, so don't + /* Right now, there can only be up to two parallel queries, so do not bother handling any other cases. */ DEBUGASSERT(res->num_pending == 1); - /* It's possible that one of these parallel queries could succeed - quickly, but the other could always fail or timeout (when we're + /* it is possible that one of these parallel queries could succeed + quickly, but the other could always fail or timeout (when we are talking to a pool of DNS servers that can only successfully resolve IPv4 address, for example). - It's also possible that the other request could always just take + it is also possible that the other request could always just take longer because it needs more time or only the second DNS server can - fulfill it successfully. But, to align with the philosophy of Happy - Eyeballs, we don't want to wait _too_ long or users will think - requests are slow when IPv6 lookups don't actually work (but IPv4 ones - do). + fulfill it successfully. But, to align with the philosophy of Happy + Eyeballs, we do not want to wait _too_ long or users will think + requests are slow when IPv6 lookups do not actually work (but IPv4 + ones do). So, now that we have a usable answer (some IPv4 addresses, some IPv6 addresses, or "no such domain"), we start a timeout for the remaining - pending responses. Even though it is typical that this resolved - request came back quickly, that needn't be the case. It might be that - this completing request didn't get a result from the first DNS server - or even the first round of the whole DNS server pool. So it could - already be quite some time after we issued the DNS queries in the - first place. Without modifying c-ares, we can't know exactly where in - its retry cycle we are. We could guess based on how much time has - gone by, but it doesn't really matter. Happy Eyeballs tells us that, - given usable information in hand, we simply don't want to wait "too - much longer" after we get a result. + pending responses. Even though it is typical that this resolved + request came back quickly, that needn't be the case. It might be that + this completing request did not get a result from the first DNS + server or even the first round of the whole DNS server pool. So it + could already be quite some time after we issued the DNS queries in + the first place. Without modifying c-ares, we cannot know exactly + where in its retry cycle we are. We could guess based on how much + time has gone by, but it does not really matter. Happy Eyeballs tells + us that, given usable information in hand, we simply do not want to + wait "too much longer" after we get a result. We simply wait an additional amount of time equal to the default - c-ares query timeout. That is enough time for a typical parallel - response to arrive without being "too long". Even on a network + c-ares query timeout. That is enough time for a typical parallel + response to arrive without being "too long". Even on a network where one of the two types of queries is failing or timing out constantly, this will usually mean we wait a total of the default c-ares timeout (5 seconds) plus the round trip time for the successful - request, which seems bearable. The downside is that c-ares might race + request, which seems bearable. The downside is that c-ares might race with us to issue one more retry just before we give up, but it seems better to "waste" that request instead of trying to guess the perfect - timeout to prevent it. After all, we don't even know where in the + timeout to prevent it. After all, we do not even know where in the c-ares retry cycle each request is. */ res->happy_eyeballs_dns_time = Curl_now(); @@ -849,8 +849,8 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data, /* If server is NULL or empty, this would purge all DNS servers * from ares library, which will cause any and all queries to fail. - * So, just return OK if none are configured and don't actually make - * any changes to c-ares. This lets c-ares use its defaults, which + * So, just return OK if none are configured and do not actually make + * any changes to c-ares. This lets c-ares use its defaults, which * it gets from the OS (for instance from /etc/resolv.conf on Linux). */ if(!(servers && servers[0])) diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index 1760d6cb3..79b9c239c 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -54,7 +54,6 @@ # define RESOLVER_ENOMEM ENOMEM #endif -#include "system_win32.h" #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -145,22 +144,9 @@ static bool init_resolve_thread(struct Curl_easy *data, const char *hostname, int port, const struct addrinfo *hints); -#ifdef _WIN32 -/* Thread sync data used by GetAddrInfoExW for win8+ */ -struct thread_sync_data_w8 -{ - OVERLAPPED overlapped; - ADDRINFOEXW_ *res; - HANDLE cancel_ev; - ADDRINFOEXW_ hints; -}; -#endif /* Data for synchronization between resolver thread and its parent */ struct thread_sync_data { -#ifdef _WIN32 - struct thread_sync_data_w8 w8; -#endif curl_mutex_t *mtx; int done; int port; @@ -168,7 +154,7 @@ struct thread_sync_data { duplicate */ #ifndef CURL_DISABLE_SOCKETPAIR struct Curl_easy *data; - curl_socket_t sock_pair[2]; /* socket pair */ + curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ #endif int sock_error; struct Curl_addrinfo *res; @@ -179,9 +165,6 @@ struct thread_sync_data { }; struct thread_data { -#ifdef _WIN32 - HANDLE complete_ev; -#endif curl_thread_t thread_hnd; unsigned int poll_interval; timediff_t interval_end; @@ -251,7 +234,7 @@ int init_thread_sync_data(struct thread_data *td, #ifndef CURL_DISABLE_SOCKETPAIR /* create socket pair or pipe */ - if(wakeup_create(&tsd->sock_pair[0]) < 0) { + if(wakeup_create(tsd->sock_pair, FALSE) < 0) { tsd->sock_pair[0] = CURL_SOCKET_BAD; tsd->sock_pair[1] = CURL_SOCKET_BAD; goto err_exit; @@ -286,158 +269,13 @@ static CURLcode getaddrinfo_complete(struct Curl_easy *data) 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. + cache. Set our copy to NULL so destroy_thread_sync_data does not free it. */ tsd->res = NULL; return result; } -#ifdef _WIN32 -static VOID WINAPI -query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped) -{ - size_t ss_size; - const ADDRINFOEXW_ *ai; - struct Curl_addrinfo *ca; - struct Curl_addrinfo *cafirst = NULL; - struct Curl_addrinfo *calast = NULL; -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-align" -#endif - struct thread_sync_data *tsd = - CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - struct thread_data *td = tsd->td; - const ADDRINFOEXW_ *res = tsd->w8.res; - int error = (int)err; - (void)bytes; - - if(error == ERROR_SUCCESS) { - /* traverse the addrinfo list */ - - for(ai = res; ai != NULL; ai = ai->ai_next) { - size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ - if(ai->ai_family == AF_INET) - ss_size = sizeof(struct sockaddr_in); -#ifdef USE_IPV6 - else if(ai->ai_family == AF_INET6) - ss_size = sizeof(struct sockaddr_in6); -#endif - else - continue; - - /* ignore elements without required address info */ - if(!ai->ai_addr || !(ai->ai_addrlen > 0)) - continue; - - /* ignore elements with bogus address size */ - if((size_t)ai->ai_addrlen < ss_size) - continue; - - ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); - if(!ca) { - error = EAI_MEMORY; - break; - } - - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ - ca->ai_flags = ai->ai_flags; - ca->ai_family = ai->ai_family; - ca->ai_socktype = ai->ai_socktype; - ca->ai_protocol = ai->ai_protocol; - ca->ai_addrlen = (curl_socklen_t)ss_size; - ca->ai_addr = NULL; - ca->ai_canonname = NULL; - ca->ai_next = NULL; - - ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); - memcpy(ca->ai_addr, ai->ai_addr, ss_size); - - if(namelen) { - size_t i; - ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size); - for(i = 0; i < namelen; ++i) /* convert wide string to ascii */ - ca->ai_canonname[i] = (char)ai->ai_canonname[i]; - ca->ai_canonname[namelen] = '\0'; - } - - /* if the return list is empty, this becomes the first element */ - if(!cafirst) - cafirst = ca; - - /* add this element last in the return list */ - if(calast) - calast->ai_next = ca; - calast = ca; - } - - /* if we failed, also destroy the Curl_addrinfo list */ - if(error) { - Curl_freeaddrinfo(cafirst); - cafirst = NULL; - } - else if(!cafirst) { -#ifdef EAI_NONAME - /* rfc3493 conformant */ - error = EAI_NONAME; -#else - /* rfc3493 obsoleted */ - error = EAI_NODATA; -#endif -#ifdef USE_WINSOCK - SET_SOCKERRNO(error); -#endif - } - tsd->res = cafirst; - } - - if(tsd->w8.res) { - Curl_FreeAddrInfoExW(tsd->w8.res); - tsd->w8.res = NULL; - } - - if(error) { - tsd->sock_error = SOCKERRNO?SOCKERRNO:error; - if(tsd->sock_error == 0) - tsd->sock_error = RESOLVER_ENOMEM; - } - else { - Curl_addrinfo_set_port(tsd->res, tsd->port); - } - - Curl_mutex_acquire(tsd->mtx); - if(tsd->done) { - /* too late, gotta clean up the mess */ - Curl_mutex_release(tsd->mtx); - destroy_thread_sync_data(tsd); - free(td); - } - else { -#ifndef CURL_DISABLE_SOCKETPAIR - char buf[1]; - if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - /* DNS has been resolved, signal client task */ - buf[0] = 1; - if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { - /* update sock_erro to errno */ - tsd->sock_error = SOCKERRNO; - } - } -#endif - tsd->done = 1; - Curl_mutex_release(tsd->mtx); - if(td->complete_ev) - SetEvent(td->complete_ev); /* Notify caller that the query completed */ - } -} -#endif #ifdef HAVE_GETADDRINFO @@ -447,14 +285,25 @@ query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped) * For builds without ARES, but with USE_IPV6, create a resolver thread * and wait on it. */ -static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) +static +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +DWORD +#else +unsigned int +#endif +CURL_STDCALL getaddrinfo_thread(void *arg) { struct thread_sync_data *tsd = (struct thread_sync_data *)arg; struct thread_data *td = tsd->td; char service[12]; int rc; #ifndef CURL_DISABLE_SOCKETPAIR +#ifdef USE_EVENTFD + const void *buf; + const uint64_t val = 1; +#else char buf[1]; +#endif #endif msnprintf(service, sizeof(service), "%d", tsd->port); @@ -480,9 +329,13 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) else { #ifndef CURL_DISABLE_SOCKETPAIR if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - /* DNS has been resolved, signal client task */ +#ifdef USE_EVENTFD + buf = &val; +#else buf[0] = 1; - if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { +#endif + /* DNS has been resolved, signal client task */ + if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { /* update sock_erro to errno */ tsd->sock_error = SOCKERRNO; } @@ -500,7 +353,13 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) /* * gethostbyname_thread() resolves a name and then exits. */ -static unsigned int CURL_STDCALL gethostbyname_thread(void *arg) +static +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) +DWORD +#else +unsigned int +#endif +CURL_STDCALL gethostbyname_thread(void *arg) { struct thread_sync_data *tsd = (struct thread_sync_data *)arg; struct thread_data *td = tsd->td; @@ -553,26 +412,9 @@ static void destroy_async_data(struct Curl_async *async) Curl_mutex_release(td->tsd.mtx); if(!done) { -#ifdef _WIN32 - if(td->complete_ev) { - CloseHandle(td->complete_ev); - td->complete_ev = NULL; - } -#endif - if(td->thread_hnd != curl_thread_t_null) { - Curl_thread_destroy(td->thread_hnd); - td->thread_hnd = curl_thread_t_null; - } + Curl_thread_destroy(td->thread_hnd); } else { -#ifdef _WIN32 - if(td->complete_ev) { - Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); - WaitForSingleObject(td->complete_ev, INFINITE); - CloseHandle(td->complete_ev); - td->complete_ev = NULL; - } -#endif if(td->thread_hnd != curl_thread_t_null) Curl_thread_join(&td->thread_hnd); @@ -618,9 +460,6 @@ static bool init_resolve_thread(struct Curl_easy *data, asp->status = 0; asp->dns = NULL; td->thread_hnd = curl_thread_t_null; -#ifdef _WIN32 - td->complete_ev = NULL; -#endif if(!init_thread_sync_data(td, hostname, port, hints)) { asp->tdata = NULL; @@ -636,41 +475,6 @@ static bool init_resolve_thread(struct Curl_easy *data, /* The thread will set this to 1 when complete. */ td->tsd.done = 0; -#ifdef _WIN32 - if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW && - Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) { -#define MAX_NAME_LEN 256 /* max domain name is 253 chars */ -#define MAX_PORT_LEN 8 - WCHAR namebuf[MAX_NAME_LEN]; - WCHAR portbuf[MAX_PORT_LEN]; - /* calculate required length */ - int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname, - -1, NULL, 0); - if((w_len > 0) && (w_len < MAX_NAME_LEN)) { - /* do utf8 conversion */ - w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len); - if((w_len > 0) && (w_len < MAX_NAME_LEN)) { - swprintf(portbuf, MAX_PORT_LEN, L"%d", port); - td->tsd.w8.hints.ai_family = hints->ai_family; - td->tsd.w8.hints.ai_socktype = hints->ai_socktype; - td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!td->complete_ev) { - /* failed to start, mark it as done here for proper cleanup. */ - td->tsd.done = 1; - goto err_exit; - } - err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS, - NULL, &td->tsd.w8.hints, &td->tsd.w8.res, - NULL, &td->tsd.w8.overlapped, - &query_complete, &td->tsd.w8.cancel_ev); - if(err != WSA_IO_PENDING) - query_complete(err, 0, &td->tsd.w8.overlapped); - return TRUE; - } - } - } -#endif - #ifdef HAVE_GETADDRINFO td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); #else @@ -707,23 +511,9 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data, DEBUGASSERT(data); td = data->state.async.tdata; DEBUGASSERT(td); -#ifdef _WIN32 - DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null); -#else DEBUGASSERT(td->thread_hnd != curl_thread_t_null); -#endif /* wait for the thread to resolve the name */ -#ifdef _WIN32 - if(td->complete_ev) { - WaitForSingleObject(td->complete_ev, INFINITE); - CloseHandle(td->complete_ev); - td->complete_ev = NULL; - if(entry) - result = getaddrinfo_complete(data); - } - else -#endif if(Curl_thread_join(&td->thread_hnd)) { if(entry) result = getaddrinfo_complete(data); @@ -757,16 +547,9 @@ void Curl_resolver_kill(struct Curl_easy *data) { struct thread_data *td = data->state.async.tdata; - /* If we're still resolving, we must wait for the threads to fully clean up, - unfortunately. Otherwise, we can simply cancel to clean up any resolver + /* If we are still resolving, we must wait for the threads to fully clean up, + unfortunately. Otherwise, we can simply cancel to clean up any resolver data. */ -#ifdef _WIN32 - if(td && td->complete_ev) { - Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev); - (void)thread_wait_resolv(data, NULL, FALSE); - } - else -#endif if(td && td->thread_hnd != curl_thread_t_null && (data->set.quick_exit != 1L)) (void)thread_wait_resolv(data, NULL, FALSE); @@ -829,7 +612,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data, } else { /* poll for name lookup done with exponential backoff up to 250ms */ - /* should be fine even if this converts to 32 bit */ + /* should be fine even if this converts to 32-bit */ timediff_t elapsed = Curl_timediff(Curl_now(), data->progress.t_startsingle); if(elapsed < 0) diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h index 7e207c4f5..0ff204884 100644 --- a/Utilities/cmcurl/lib/asyn.h +++ b/Utilities/cmcurl/lib/asyn.h @@ -58,7 +58,7 @@ void Curl_resolver_global_cleanup(void); * Curl_resolver_init() * Called from curl_easy_init() -> Curl_open() to initialize resolver * URL-state specific environment ('resolver' member of the UrlState - * structure). Should fill the passed pointer by the initialized handler. + * structure). Should fill the passed pointer by the initialized handler. * Returning anything else than CURLE_OK fails curl_easy_init() with the * correspondent code. */ @@ -68,7 +68,7 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver); * Curl_resolver_cleanup() * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver * URL-state specific environment ('resolver' member of the UrlState - * structure). Should destroy the handler and free all resources connected to + * structure). Should destroy the handler and free all resources connected to * it. */ void Curl_resolver_cleanup(void *resolver); @@ -76,9 +76,9 @@ void Curl_resolver_cleanup(void *resolver); /* * Curl_resolver_duphandle() * Called from curl_easy_duphandle() to duplicate resolver URL-state specific - * environment ('resolver' member of the UrlState structure). Should + * environment ('resolver' member of the UrlState structure). Should * duplicate the 'from' handle and pass the resulting handle to the 'to' - * pointer. Returning anything else than CURLE_OK causes failed + * pointer. Returning anything else than CURLE_OK causes failed * curl_easy_duphandle() call. */ CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, @@ -89,7 +89,7 @@ CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, * * It is called from inside other functions to cancel currently performing * resolver request. Should also free any temporary resources allocated to - * perform a request. This never waits for resolver threads to complete. + * perform a request. This never waits for resolver threads to complete. * * It is safe to call this when conn is in any state. */ @@ -99,8 +99,8 @@ void Curl_resolver_cancel(struct Curl_easy *data); * Curl_resolver_kill(). * * This acts like Curl_resolver_cancel() except it will block until any threads - * associated with the resolver are complete. This never blocks for resolvers - * that do not use threads. This is intended to be the "last chance" function + * associated with the resolver are complete. This never blocks for resolvers + * that do not use threads. This is intended to be the "last chance" function * that cleans up an in-progress resolver completely (before its owner is about * to die). * @@ -161,7 +161,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, int *waitp); #ifndef CURLRES_ASYNCH -/* convert these functions if an asynch resolver isn't used */ +/* convert these functions if an asynch resolver is not used */ #define Curl_resolver_cancel(x) Curl_nop_stmt #define Curl_resolver_kill(x) Curl_nop_stmt #define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST diff --git a/Utilities/cmcurl/lib/bufq.c b/Utilities/cmcurl/lib/bufq.c index c3245516c..46e6eaa38 100644 --- a/Utilities/cmcurl/lib/bufq.c +++ b/Utilities/cmcurl/lib/bufq.c @@ -91,6 +91,23 @@ static size_t chunk_read(struct buf_chunk *chunk, } } +static size_t chunk_unwrite(struct buf_chunk *chunk, size_t len) +{ + size_t n = chunk->w_offset - chunk->r_offset; + DEBUGASSERT(chunk->w_offset >= chunk->r_offset); + if(!n) { + return 0; + } + else if(n <= len) { + chunk->r_offset = chunk->w_offset = 0; + return n; + } + else { + chunk->w_offset -= len; + return len; + } +} + static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len, Curl_bufq_reader *reader, void *reader_ctx, CURLcode *err) @@ -363,6 +380,49 @@ static void prune_head(struct bufq *q) } } +static struct buf_chunk *chunk_prev(struct buf_chunk *head, + struct buf_chunk *chunk) +{ + while(head) { + if(head == chunk) + return NULL; + if(head->next == chunk) + return head; + head = head->next; + } + return NULL; +} + +static void prune_tail(struct bufq *q) +{ + struct buf_chunk *chunk; + + while(q->tail && chunk_is_empty(q->tail)) { + chunk = q->tail; + q->tail = chunk_prev(q->head, chunk); + if(q->tail) + q->tail->next = NULL; + if(q->head == chunk) + q->head = q->tail; + if(q->pool) { + bufcp_put(q->pool, chunk); + --q->chunk_count; + } + else if((q->chunk_count > q->max_chunks) || + (q->opts & BUFQ_OPT_NO_SPARES)) { + /* SOFT_LIMIT allowed us more than max. free spares until + * we are at max again. Or free them if we are configured + * to not use spares. */ + free(chunk); + --q->chunk_count; + } + else { + chunk->next = q->spare; + q->spare = chunk; + } + } +} + static struct buf_chunk *get_non_full_tail(struct bufq *q) { struct buf_chunk *chunk; @@ -428,6 +488,15 @@ CURLcode Curl_bufq_cwrite(struct bufq *q, return result; } +CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len) +{ + while(len && q->tail) { + len -= chunk_unwrite(q->head, len); + prune_tail(q); + } + return len? CURLE_AGAIN : CURLE_OK; +} + ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode *err) { diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h index 87ffa45da..ec415648f 100644 --- a/Utilities/cmcurl/lib/bufq.h +++ b/Utilities/cmcurl/lib/bufq.h @@ -182,6 +182,12 @@ CURLcode Curl_bufq_cwrite(struct bufq *q, const char *buf, size_t len, size_t *pnwritten); +/** + * Remove `len` bytes from the end of the buffer queue again. + * Returns CURLE_AGAIN if less than `len` bytes were in the queue. + */ +CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len); + /** * Read buf from the start of the buffer queue. The buf is copied * and the amount of copied bytes is returned. diff --git a/Utilities/cmcurl/lib/bufref.c b/Utilities/cmcurl/lib/bufref.c index f0a0e2a7d..f048b5701 100644 --- a/Utilities/cmcurl/lib/bufref.c +++ b/Utilities/cmcurl/lib/bufref.c @@ -48,7 +48,7 @@ void Curl_bufref_init(struct bufref *br) } /* - * Free the buffer and re-init the necessary fields. It doesn't touch the + * Free the buffer and re-init the necessary fields. It does not touch the * 'signature' field and thus this buffer reference can be reused. */ diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index 0593d9706..a9a62d0a6 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -119,7 +119,7 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx, DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen)); result = Curl_conn_send(data, io_ctx->sockindex, - (void *)buf, buflen, &nwrote); + (void *)buf, buflen, FALSE, &nwrote); if(result == CURLE_AGAIN) { DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen)); /* would block, register interest */ @@ -206,7 +206,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) struct SingleRequest *k = &data->req; CURLcode result = CURLE_OK; - if(0 == k->bodywrites) { + if(!k->bodywritten) { #if defined(USE_NTLM) struct connectdata *conn = data->conn; if(conn->bits.close && @@ -324,7 +324,7 @@ static CURLcode empty_header(struct Curl_easy *data) result = hyper_each_header(data, NULL, 0, NULL, 0) ? CURLE_WRITE_ERROR : CURLE_OK; if(result) - failf(data, "hyperstream: couldn't pass blank header"); + failf(data, "hyperstream: could not pass blank header"); /* Hyper does chunked decoding itself. If it was added during * response header processing, remove it again. */ Curl_cwriter_remove_by_name(data, "chunked"); @@ -352,6 +352,8 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, (void)conn; if(data->hyp.send_body_waker) { + /* If there is still something to upload, wake it to give it + * another try. */ hyper_waker_wake(data->hyp.send_body_waker); data->hyp.send_body_waker = NULL; } @@ -367,7 +369,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, h->write_waker = NULL; } - do { + while(1) { hyper_task_return_type t; task = hyper_executor_poll(h->exec); if(!task) { @@ -391,22 +393,22 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, switch(code) { case HYPERE_ABORTED_BY_CALLBACK: result = CURLE_OK; - break; + goto out; case HYPERE_UNEXPECTED_EOF: if(!data->req.bytecount) result = CURLE_GOT_NOTHING; else result = CURLE_RECV_ERROR; - break; + goto out; case HYPERE_INVALID_PEER_MESSAGE: /* bump headerbytecount to avoid the count remaining at zero and appearing to not having read anything from the peer at all */ data->req.headerbytecount++; result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */ - break; + goto out; default: result = CURLE_RECV_ERROR; - break; + goto out; } } data->req.done = TRUE; @@ -416,115 +418,127 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, else if(t == HYPER_TASK_EMPTY) { void *userdata = hyper_task_userdata(task); hyper_task_free(task); - if((userdata_t)userdata == USERDATA_RESP_BODY) { + if(userdata == (void *)USERDATA_RESP_BODY) { /* end of transfer */ data->req.done = TRUE; infof(data, "hyperstream is done"); - if(!k->bodywrites) { - /* hyper doesn't always call the body write callback */ + if(!k->bodywritten) { + /* hyper does not always call the body write callback */ result = Curl_http_firstwrite(data); } break; } else { /* A background task for hyper; ignore */ + DEBUGF(infof(data, "hyper: some background task done")); continue; } } + else if(t == HYPER_TASK_RESPONSE) { + resp = hyper_task_value(task); + hyper_task_free(task); - DEBUGASSERT(HYPER_TASK_RESPONSE); - - resp = hyper_task_value(task); - hyper_task_free(task); - - *didwhat = KEEP_RECV; - if(!resp) { - failf(data, "hyperstream: couldn't get response"); - return CURLE_RECV_ERROR; - } + *didwhat = KEEP_RECV; + if(!resp) { + failf(data, "hyperstream: could not get response"); + result = CURLE_RECV_ERROR; + goto out; + } - http_status = hyper_response_status(resp); - http_version = hyper_response_version(resp); - reasonp = hyper_response_reason_phrase(resp); - reason_len = hyper_response_reason_phrase_len(resp); + http_status = hyper_response_status(resp); + http_version = hyper_response_version(resp); + reasonp = hyper_response_reason_phrase(resp); + reason_len = hyper_response_reason_phrase_len(resp); - if(http_status == 417 && Curl_http_exp100_is_selected(data)) { - infof(data, "Got 417 while waiting for a 100"); - data->state.disableexpect = TRUE; - data->req.newurl = strdup(data->state.url); - Curl_req_abort_sending(data); - } + if(http_status == 417 && Curl_http_exp100_is_selected(data)) { + infof(data, "Got 417 while waiting for a 100"); + data->state.disableexpect = TRUE; + data->req.newurl = strdup(data->state.url); + Curl_req_abort_sending(data); + } - result = status_line(data, conn, - http_status, http_version, reasonp, reason_len); - if(result) - break; + result = status_line(data, conn, + http_status, http_version, reasonp, reason_len); + if(result) + goto out; - headers = hyper_response_headers(resp); - if(!headers) { - failf(data, "hyperstream: couldn't get response headers"); - result = CURLE_RECV_ERROR; - break; - } + headers = hyper_response_headers(resp); + if(!headers) { + failf(data, "hyperstream: could not get response headers"); + result = CURLE_RECV_ERROR; + goto out; + } - /* the headers are already received */ - hyper_headers_foreach(headers, hyper_each_header, data); - if(data->state.hresult) { - result = data->state.hresult; - break; - } + /* the headers are already received */ + hyper_headers_foreach(headers, hyper_each_header, data); + if(data->state.hresult) { + result = data->state.hresult; + goto out; + } - result = empty_header(data); - if(result) - break; + result = empty_header(data); + if(result) + goto out; - k->deductheadercount = - (100 <= http_status && 199 >= http_status)?k->headerbytecount:0; + 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, NULL, 0); - if(result) - return result; - } - else { - failf(data, "Expected 101, got %u", k->httpcode); - result = CURLE_HTTP_RETURNED_ERROR; - break; + if(k->upgr101 == UPGR101_WS) { + if(http_status == 101) { + /* verify the response */ + result = Curl_ws_accept(data, NULL, 0); + if(result) + goto out; + } + else { + failf(data, "Expected 101, got %u", k->httpcode); + result = CURLE_HTTP_RETURNED_ERROR; + goto out; + } } - } #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. */ - result = Curl_http_auth_act(data); - if(result) - break; + /* 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. */ + result = Curl_http_auth_act(data); + if(result) + goto out; - resp_body = hyper_response_body(resp); - if(!resp_body) { - failf(data, "hyperstream: couldn't get response body"); - result = CURLE_RECV_ERROR; - break; - } - foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data); - if(!foreach) { - failf(data, "hyperstream: body foreach failed"); - result = CURLE_OUT_OF_MEMORY; - break; + resp_body = hyper_response_body(resp); + if(!resp_body) { + failf(data, "hyperstream: could not get response body"); + result = CURLE_RECV_ERROR; + goto out; + } + foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data); + if(!foreach) { + failf(data, "hyperstream: body foreach failed"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY); + if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) { + failf(data, "Couldn't hyper_executor_push the body-foreach"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + hyper_response_free(resp); + resp = NULL; } - hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY); - if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) { - failf(data, "Couldn't hyper_executor_push the body-foreach"); - result = CURLE_OUT_OF_MEMORY; - break; + else { + DEBUGF(infof(data, "hyper: unhandled tasktype %x", t)); } + } /* while(1) */ - hyper_response_free(resp); - resp = NULL; - } while(1); + if(!result && Curl_xfer_needs_flush(data)) { + DEBUGF(infof(data, "Curl_hyper_stream(), connection needs flush")); + result = Curl_xfer_flush(data); + } + +out: + DEBUGF(infof(data, "Curl_hyper_stream() -> %d", result)); if(resp) hyper_response_free(resp); return result; @@ -669,12 +683,15 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, goto out; } /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent */ + do not know exactly when the body is sent */ data->req.writebytecount += fillcount; + if(eos) + data->req.eos_read = TRUE; Curl_pgrsSetUploadCounter(data, data->req.writebytecount); rc = HYPER_POLL_READY; } else if(eos) { + data->req.eos_read = TRUE; *chunk = NULL; rc = HYPER_POLL_READY; } @@ -686,9 +703,15 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, rc = HYPER_POLL_PENDING; } + if(!data->req.upload_done && data->req.eos_read) { + DEBUGF(infof(data, "hyper: uploadstreamed(), upload is done")); + result = Curl_req_set_upload_done(data); + } + out: Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); data->state.hresult = result; + DEBUGF(infof(data, "hyper: uploadstreamed() -> %d", result)); return rc; } @@ -702,8 +725,9 @@ static CURLcode finalize_request(struct Curl_easy *data, { CURLcode result = CURLE_OK; struct dynbuf req; - if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) + if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { Curl_pgrsSetUploadSize(data, 0); /* no request body */ + } else { hyper_body *body; Curl_dyn_init(&req, DYN_HTTP_REQUEST); @@ -772,7 +796,7 @@ static void http1xx_cb(void *arg, struct hyper_response *resp) if(!result) { headers = hyper_response_headers(resp); if(!headers) { - failf(data, "hyperstream: couldn't get 1xx response headers"); + failf(data, "hyperstream: could not get 1xx response headers"); result = CURLE_RECV_ERROR; } } @@ -821,21 +845,21 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) *done = TRUE; result = Curl_client_start(data); if(result) - return result; + goto out; /* Add collecting of headers written to client. For a new connection, * we might have done that already, but reuse * or multiplex needs it here as well. */ result = Curl_headers_init(data); if(result) - return result; + goto out; infof(data, "Time for the Hyper dance"); memset(h, 0, sizeof(struct hyptransfer)); result = Curl_http_host(data, conn); if(result) - return result; + goto out; Curl_http_method(data, conn, &method, &httpreq); @@ -846,33 +870,35 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) char *pq = NULL; if(data->state.up.query) { pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) - return CURLE_OUT_OF_MEMORY; + if(!pq) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } result = Curl_http_output_auth(data, conn, method, httpreq, (pq ? pq : data->state.up.path), FALSE); free(pq); if(result) - return result; + goto out; } result = Curl_http_req_set_reader(data, httpreq, &te); if(result) - goto error; + goto out; result = Curl_http_range(data, httpreq); if(result) - return result; + goto out; result = Curl_http_useragent(data); if(result) - return result; + goto out; io = hyper_io_new(); if(!io) { failf(data, "Couldn't create hyper IO"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } /* tell Hyper how to read/write network data */ h->io_ctx.data = data; @@ -887,7 +913,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!h->exec) { failf(data, "Couldn't create hyper executor"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } } @@ -895,12 +921,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!options) { failf(data, "Couldn't create hyper client options"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } if(conn->alpn == CURL_HTTP_VERSION_2) { failf(data, "ALPN protocol h2 not supported with Hyper"); result = CURLE_UNSUPPORTED_PROTOCOL; - goto error; + goto out; } hyper_clientconn_options_set_preserve_header_case(options, 1); hyper_clientconn_options_set_preserve_header_order(options, 1); @@ -913,7 +939,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!handshake) { failf(data, "Couldn't create hyper client handshake"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } io = NULL; options = NULL; @@ -921,7 +947,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { failf(data, "Couldn't hyper_executor_push the handshake"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } handshake = NULL; /* ownership passed on */ @@ -929,7 +955,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!task) { failf(data, "Couldn't hyper_executor_poll the handshake"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } client = hyper_task_value(task); @@ -939,7 +965,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!req) { failf(data, "Couldn't hyper_request_new"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } if(!Curl_use_http_1_1plus(data, conn)) { @@ -947,57 +973,57 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) HYPER_HTTP_VERSION_1_0)) { failf(data, "error setting HTTP version"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } } if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { failf(data, "error setting method"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } result = request_target(data, conn, method, req); if(result) - goto error; + goto out; headers = hyper_request_headers(req); if(!headers) { failf(data, "hyper_request_headers"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } rc = hyper_request_on_informational(req, http1xx_cb, data); if(rc) { result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } if(data->state.aptr.host) { result = Curl_hyper_header(data, headers, data->state.aptr.host); if(result) - goto error; + goto out; } #ifndef CURL_DISABLE_PROXY if(data->state.aptr.proxyuserpwd) { result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); if(result) - goto error; + goto out; } #endif if(data->state.aptr.userpwd) { result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); if(result) - goto error; + goto out; } if((data->state.use_range && data->state.aptr.rangeline)) { result = Curl_hyper_header(data, headers, data->state.aptr.rangeline); if(result) - goto error; + goto out; } if(data->set.str[STRING_USERAGENT] && @@ -1005,7 +1031,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) data->state.aptr.uagent) { result = Curl_hyper_header(data, headers, data->state.aptr.uagent); if(result) - goto error; + goto out; } p_accept = Curl_checkheaders(data, @@ -1013,12 +1039,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(p_accept) { result = Curl_hyper_header(data, headers, p_accept); if(result) - goto error; + goto out; } if(te) { result = Curl_hyper_header(data, headers, te); if(result) - goto error; + goto out; } #ifndef CURL_DISABLE_ALTSVC @@ -1027,11 +1053,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) conn->conn_to_host.name, conn->conn_to_port); if(!altused) { result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } result = Curl_hyper_header(data, headers, altused); if(result) - goto error; + goto out; free(altused); } #endif @@ -1042,7 +1068,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); if(result) - goto error; + goto out; } #endif @@ -1054,17 +1080,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) else result = Curl_hyper_header(data, headers, data->state.aptr.ref); if(result) - goto error; + goto out; } #ifdef HAVE_LIBZ /* we only consider transfer-encoding magic if libz support is built-in */ result = Curl_transferencode(data); if(result) - goto error; + goto out; result = Curl_hyper_header(data, headers, data->state.aptr.te); if(result) - goto error; + goto out; #endif if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && @@ -1078,29 +1104,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = Curl_hyper_header(data, headers, data->state.aptr.accept_encoding); if(result) - goto error; + goto out; } else Curl_safefree(data->state.aptr.accept_encoding); result = cookies(data, conn, headers); if(result) - goto error; + goto out; 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; + goto out; result = Curl_add_custom_headers(data, FALSE, headers); if(result) - goto error; + goto out; result = finalize_request(data, headers, req, httpreq); if(result) - goto error; + goto out; Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); @@ -1114,14 +1140,14 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!sendtask) { failf(data, "hyper_clientconn_send"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } req = NULL; if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { failf(data, "Couldn't hyper_executor_push the send"); result = CURLE_OUT_OF_MEMORY; - goto error; + goto out; } sendtask = NULL; /* ownership passed on */ @@ -1131,9 +1157,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { /* HTTP GET/HEAD download */ Curl_pgrsSetUploadSize(data, 0); /* nothing */ + result = Curl_req_set_upload_done(data); + if(result) + goto out; } - Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); conn->datastream = Curl_hyper_stream; /* clear userpwd and proxyuserpwd to avoid reusing old credentials @@ -1142,24 +1171,20 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #ifndef CURL_DISABLE_PROXY Curl_safefree(data->state.aptr.proxyuserpwd); #endif - return CURLE_OK; -error: - DEBUGASSERT(result); - if(io) - hyper_io_free(io); - - if(options) - hyper_clientconn_options_free(options); - - if(handshake) - hyper_task_free(handshake); - - if(client) - hyper_clientconn_free(client); - - if(req) - hyper_request_free(req); +out: + if(result) { + if(io) + hyper_io_free(io); + if(options) + hyper_clientconn_options_free(options); + if(handshake) + hyper_task_free(handshake); + if(client) + hyper_clientconn_free(client); + if(req) + hyper_request_free(req); + } return result; } @@ -1206,6 +1231,7 @@ static const struct Curl_crtype cr_hyper_protocol = { Curl_creader_def_resume_from, Curl_creader_def_rewind, cr_hyper_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct Curl_creader) }; diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index 093c33be5..6de33d0e2 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c @@ -65,7 +65,6 @@ typedef enum { /* struct for HTTP CONNECT tunneling */ struct h1_tunnel_state { - struct HTTP CONNECT; struct dynbuf rcvbuf; struct dynbuf request_data; size_t nsent; @@ -182,8 +181,8 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, data->info.httpcode = 0; /* clear it as it might've been used for the proxy */ /* If a proxy-authorization header was used for the proxy, then we should - make sure that it isn't accidentally used for the document request - after we've connected. So let's free and clear it here. */ + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ Curl_safefree(data->state.aptr.proxyuserpwd); #ifdef USE_HYPER data->state.hconnect = FALSE; @@ -222,8 +221,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, int http_minor; CURLcode result; - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here + /* This only happens if we have looped here due to authentication + reasons, and we do not really use the newly cloned URL here then. Just free() it. */ Curl_safefree(data->req.newurl); @@ -267,7 +266,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, blen -= ts->nsent; buf += ts->nsent; - nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result); + nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { result = CURLE_OK; @@ -422,7 +421,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(ts->cl) { /* A Content-Length based body: simply count down the counter - and make sure to break out of the loop when we're done! */ + and make sure to break out of the loop when we are done! */ ts->cl--; if(ts->cl <= 0) { ts->keepon = KEEPON_DONE; @@ -440,7 +439,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(result) return result; if(Curl_httpchunk_is_done(data, &ts->ch)) { - /* we're done reading chunks! */ + /* we are done reading chunks! */ infof(data, "chunk reading DONE"); ts->keepon = KEEPON_DONE; } @@ -475,7 +474,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(result) return result; - /* Newlines are CRLF, so the CR is ignored as the line isn't + /* Newlines are CRLF, so the CR is ignored as the line is not really terminated until the LF comes. Treat a following CR as end-of-headers as well.*/ @@ -490,15 +489,14 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, ts->keepon = KEEPON_IGNORE; if(ts->cl) { - infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T - " bytes of response-body", ts->cl); + infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl); } else if(ts->chunked_encoding) { infof(data, "Ignore chunked response-body"); } else { /* without content-length or chunked encoding, we - can't keep the connection alive since the close is + cannot keep the connection alive since the close is the end signal so we bail out at once instead */ CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); ts->keepon = KEEPON_DONE; @@ -518,7 +516,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return result; Curl_dyn_reset(&ts->rcvbuf); - } /* while there's buffer left and loop is requested */ + } /* while there is buffer left and loop is requested */ if(error) result = CURLE_RECV_ERROR; @@ -666,8 +664,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, goto error; } - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here + /* This only happens if we have looped here due to authentication + reasons, and we do not really use the newly cloned URL here then. Just free() it. */ Curl_safefree(data->req.newurl); @@ -955,7 +953,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); if(data->info.httpproxycode/100 != 2) { - /* a non-2xx response and we have no next url to try. */ + /* a non-2xx response and we have no next URL to try. */ Curl_safefree(data->req.newurl); /* failure, close this connection to avoid reuse */ streamclose(conn, "proxy CONNECT failure"); @@ -1034,9 +1032,9 @@ static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, * and not waiting on something, we are tunneling. */ curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); if(ts) { - /* when we've sent a CONNECT to a proxy, we should rather either + /* when we have sent a CONNECT to a proxy, we should rather either wait for the socket to become readable to be able to get the - response headers or if we're still sending the request, wait + response headers or if we are still sending the request, wait for write. */ if(tunnel_want_send(ts)) Curl_pollset_set_out_only(data, ps, sock); @@ -1077,6 +1075,7 @@ struct Curl_cftype Curl_cft_h1_proxy = { cf_h1_proxy_destroy, cf_h1_proxy_connect, cf_h1_proxy_close, + Curl_cf_def_shutdown, Curl_cf_http_proxy_get_host, cf_h1_proxy_adjust_pollset, Curl_cf_def_data_pending, diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index 0bff15f38..0a60ae47c 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c @@ -73,7 +73,6 @@ struct tunnel_stream { char *authority; int32_t stream_id; uint32_t error; - size_t upload_blocked_len; h2_tunnel_state state; BIT(has_final_response); BIT(closed); @@ -162,8 +161,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id); ts->state = new_state; /* If a proxy-authorization header was used for the proxy, then we should - make sure that it isn't accidentally used for the document request - after we've connected. So let's free and clear it here. */ + make sure that it is not accidentally used for the document request + after we have connected. So let's free and clear it here. */ Curl_safefree(data->state.aptr.proxyuserpwd); break; } @@ -181,7 +180,8 @@ struct cf_h2_proxy_ctx { int32_t goaway_error; int32_t last_stream_id; BIT(conn_closed); - BIT(goaway); + BIT(rcvd_goaway); + BIT(sent_goaway); BIT(nw_out_blocked); }; @@ -216,11 +216,13 @@ static void drain_tunnel(struct Curl_cfilter *cf, struct Curl_easy *data, struct tunnel_stream *tunnel) { + struct cf_h2_proxy_ctx *ctx = cf->ctx; unsigned char bits; (void)cf; bits = CURL_CSELECT_IN; - if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) + if(!tunnel->closed && !tunnel->reset && + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) bits |= CURL_CSELECT_OUT; if(data->state.select_bits != bits) { CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", @@ -259,7 +261,7 @@ static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, - err); + FALSE, err); CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", buflen, nwritten, *err); } @@ -694,7 +696,7 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, } break; case NGHTTP2_GOAWAY: - ctx->goaway = TRUE; + ctx->rcvd_goaway = TRUE; break; default: break; @@ -1078,7 +1080,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, } while(ts->state == H2_TUNNEL_INIT); out: - if(result || ctx->tunnel.closed) + if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); return result; } @@ -1166,6 +1168,50 @@ static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, } } +static CURLcode cf_h2_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result; + int rv; + + if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + + if(!ctx->sent_goaway) { + rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE, + 0, 0, + (const uint8_t *)"shutdown", + sizeof("shutdown")); + if(rv) { + failf(data, "nghttp2_submit_goaway() failed: %s(%d)", + nghttp2_strerror(rv), rv); + result = CURLE_SEND_ERROR; + goto out; + } + ctx->sent_goaway = TRUE; + } + /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */ + result = CURLE_OK; + if(nghttp2_session_want_write(ctx->h2)) + result = proxy_h2_progress_egress(cf, data); + if(!result && nghttp2_session_want_read(ctx->h2)) + result = proxy_h2_progress_ingress(cf, data); + + *done = (ctx->conn_closed || + (!result && !nghttp2_session_want_write(ctx->h2) && + !nghttp2_session_want_read(ctx->h2))); +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -1182,12 +1228,20 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, struct easy_pollset *ps) { struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); bool want_recv, want_send; - Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(!cf->connected && ctx->h2) { + want_send = nghttp2_session_want_write(ctx->h2) || + !Curl_bufq_is_empty(&ctx->outbufq) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); + want_recv = nghttp2_session_want_read(ctx->h2); + } + else + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(ctx->h2 && (want_recv || want_send)) { - struct cf_call_data save; bool c_exhaust, s_exhaust; CF_DATA_SAVE(save, cf, data); @@ -1197,9 +1251,25 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, ctx->h2, ctx->tunnel.stream_id); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || - (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + (!c_exhaust && nghttp2_session_want_write(ctx->h2)) || + !Curl_bufq_is_empty(&ctx->outbufq) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); Curl_pollset_set(data, ps, sock, want_recv, want_send); + CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", + want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + else if(ctx->sent_goaway && !cf->shutdown) { + /* shutdown in progress */ + CF_DATA_SAVE(save, cf, data); + want_send = nghttp2_session_want_write(ctx->h2) || + !Curl_bufq_is_empty(&ctx->outbufq) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); + want_recv = nghttp2_session_want_read(ctx->h2); + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", + want_recv, want_send); CF_DATA_RESTORE(cf, save); } } @@ -1214,7 +1284,7 @@ static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " "connection", ctx->tunnel.stream_id); - connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; } @@ -1259,7 +1329,8 @@ static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(ctx->tunnel.reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx->goaway && ctx->last_stream_id < ctx->tunnel.stream_id)) { + (ctx->rcvd_goaway && + ctx->last_stream_id < ctx->tunnel.stream_id)) { *err = CURLE_RECV_ERROR; nread = -1; } @@ -1305,16 +1376,7 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, } result = proxy_h2_progress_egress(cf, data); - if(result == CURLE_AGAIN) { - /* pending data to send, need to be called again. Ideally, we'd - * monitor the socket for POLLOUT, but we might not be in SENDING - * transfer state any longer and are unable to make this happen. - */ - CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN", - ctx->tunnel.stream_id); - drain_tunnel(cf, data, &ctx->tunnel); - } - else if(result) { + if(result && (result != CURLE_AGAIN)) { *err = result; nread = -1; } @@ -1334,15 +1396,16 @@ out: static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_h2_proxy_ctx *ctx = cf->ctx; struct cf_call_data save; int rv; ssize_t nwritten; CURLcode result; - int blocked = 0; + (void)eos; /* TODO, maybe useful for blocks? */ if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { *err = CURLE_SEND_ERROR; return -1; @@ -1354,29 +1417,10 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, *err = CURLE_SEND_ERROR; goto out; } - else if(ctx->tunnel.upload_blocked_len) { - /* the data in `buf` has already been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len); - if(len < ctx->tunnel.upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happen. We are not prepared to handle that. */ - failf(data, "HTTP/2 proxy, send again with decreased length"); - *err = CURLE_HTTP2; - nwritten = -1; - goto out; - } - nwritten = (ssize_t)ctx->tunnel.upload_blocked_len; - ctx->tunnel.upload_blocked_len = 0; - *err = CURLE_OK; - } else { nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); - if(nwritten < 0) { - if(*err != CURLE_AGAIN) - goto out; - nwritten = 0; - } + if(nwritten < 0 && (*err != CURLE_AGAIN)) + goto out; } if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { @@ -1399,52 +1443,13 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, /* Call the nghttp2 send loop and flush to write ALL buffered data, * headers and/or request body completely out to the network */ result = proxy_h2_progress_egress(cf, data); - if(result == CURLE_AGAIN) { - blocked = 1; - } - else if(result) { + if(result && (result != CURLE_AGAIN)) { *err = result; nwritten = -1; goto out; } - else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { - /* although we wrote everything that nghttp2 wants to send now, - * there is data left in our stream send buffer unwritten. This may - * be due to the stream's HTTP/2 flow window being exhausted. */ - blocked = 1; - } - - if(blocked) { - /* Unable to send all data, due to connection blocked or H2 window - * exhaustion. Data is left in our stream buffer, or nghttp2's internal - * frame buffer or our network out buffer. */ - size_t rwin = nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id); - if(rwin == 0) { - /* H2 flow window exhaustion. - * FIXME: there is no way to HOLD all transfers that use this - * proxy connection AND to UNHOLD all of them again when the - * window increases. - * We *could* iterate over all data on this conn maybe? */ - CURL_TRC_CF(data, cf, "[%d] remote flow " - "window is exhausted", ctx->tunnel.stream_id); - } - /* Whatever the cause, we need to return CURL_EAGAIN for this call. - * We have unwritten state that needs us being invoked again and EAGAIN - * is the only way to ensure that. */ - ctx->tunnel.upload_blocked_len = nwritten; - CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "blocked_len=%zu", - ctx->tunnel.stream_id, len, - nghttp2_session_get_remote_window_size(ctx->h2), rwin, - nwritten); - drain_tunnel(cf, data, &ctx->tunnel); - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(proxy_h2_should_close_session(ctx)) { + if(proxy_h2_should_close_session(ctx)) { /* nghttp2 thinks this session is done. If the stream has not been * closed, this is an error state for out transfer */ if(ctx->tunnel.closed) { @@ -1477,6 +1482,38 @@ out: return nwritten; } +static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + /* resume the potentially suspended tunnel */ + int rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); + if(nghttp2_is_fatal(rv)) { + result = CURLE_SEND_ERROR; + goto out; + } + } + + result = proxy_h2_progress_egress(cf, data); + +out: + CURL_TRC_CF(data, cf, "[%d] flush -> %d, " + "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", + ctx->tunnel.stream_id, result, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, ctx->tunnel.stream_id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->tunnel.sendbuf), + Curl_bufq_len(&ctx->outbufq)); + CF_DATA_RESTORE(cf, save); + return result; +} + static bool proxy_h2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending) @@ -1489,8 +1526,8 @@ static bool proxy_h2_connisalive(struct Curl_cfilter *cf, return FALSE; if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, only "protocol frames" */ CURLcode result; ssize_t nread = -1; @@ -1530,6 +1567,52 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, return result; } +static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_h2_proxy_ctx *ctx = cf->ctx; + + switch(query) { + case CF_QUERY_NEED_FLUSH: { + if(!Curl_bufq_is_empty(&ctx->outbufq) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { + CURL_TRC_CF(data, cf, "needs flush"); + *pres1 = TRUE; + return CURLE_OK; + } + break; + } + default: + break; + } + return cf->next? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static CURLcode cf_h2_proxy_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + struct cf_call_data save; + + (void)arg1; + (void)arg2; + + switch(event) { + case CF_CTRL_FLUSH: + CF_DATA_SAVE(save, cf, data); + result = cf_h2_proxy_flush(cf, data); + CF_DATA_RESTORE(cf, save); + break; + default: + break; + } + return result; +} + struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, @@ -1537,15 +1620,16 @@ struct Curl_cftype Curl_cft_h2_proxy = { cf_h2_proxy_destroy, cf_h2_proxy_connect, cf_h2_proxy_close, + cf_h2_proxy_shutdown, Curl_cf_http_proxy_get_host, cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, cf_h2_proxy_send, cf_h2_proxy_recv, - Curl_cf_def_cntrl, + cf_h2_proxy_cntrl, cf_h2_proxy_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + cf_h2_proxy_query, }; CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index 2abc4d754..0fc7625c4 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c @@ -70,8 +70,9 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, { struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; - const char *tcp_version; const char *client_ip; + struct ip_quadruple ipquad; + int is_ipv6; DEBUGASSERT(ctx); DEBUGASSERT(ctx->state == HAPROXY_INIT); @@ -81,19 +82,20 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); else { #endif /* USE_UNIX_SOCKETS */ + result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad); + if(result) + return result; + /* Emit the correct prefix for IPv6 */ - tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4"; if(data->set.str[STRING_HAPROXY_CLIENT_IP]) client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; else - client_ip = data->info.primary.local_ip; + client_ip = ipquad.local_ip; result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", - tcp_version, - client_ip, - data->info.primary.remote_ip, - data->info.primary.local_port, - data->info.primary.remote_port); + is_ipv6? "TCP6" : "TCP4", + client_ip, ipquad.remote_ip, + ipquad.local_port, ipquad.remote_port); #ifdef USE_UNIX_SOCKETS } @@ -129,17 +131,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, case HAPROXY_SEND: len = Curl_dyn_len(&ctx->data_out); if(len > 0) { - size_t written; - result = Curl_conn_send(data, cf->sockindex, - Curl_dyn_ptr(&ctx->data_out), - len, &written); - if(result == CURLE_AGAIN) { + ssize_t nwritten; + nwritten = Curl_conn_cf_send(cf->next, data, + Curl_dyn_ptr(&ctx->data_out), len, FALSE, + &result); + if(nwritten < 0) { + if(result != CURLE_AGAIN) + goto out; result = CURLE_OK; - written = 0; + nwritten = 0; } - else if(result) - goto out; - Curl_dyn_tail(&ctx->data_out, len - written); + Curl_dyn_tail(&ctx->data_out, len - (size_t)nwritten); if(Curl_dyn_len(&ctx->data_out) > 0) { result = CURLE_OK; goto out; @@ -194,6 +196,7 @@ struct Curl_cftype Curl_cft_haproxy = { cf_haproxy_destroy, cf_haproxy_connect, cf_haproxy_close, + Curl_cf_def_shutdown, Curl_cf_def_get_host, cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index 50ac8d4bc..bc7159872 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c @@ -55,7 +55,8 @@ struct cf_hc_baller { CURLcode result; struct curltime started; int reply_ms; - bool enabled; + BIT(enabled); + BIT(shutdown); }; static void cf_hc_baller_reset(struct cf_hc_baller *b, @@ -95,6 +96,21 @@ static bool cf_hc_baller_data_pending(struct cf_hc_baller *b, return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data); } +static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b, + struct Curl_easy *data) +{ + return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data); +} + +static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + if(b->cf && !b->result) + return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2); + return CURLE_OK; +} + struct cf_hc_ctx { cf_hc_state state; const struct Curl_dns_entry *remotehost; @@ -173,7 +189,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, switch(cf->conn->alpn) { case CURL_HTTP_VERSION_3: - infof(data, "using HTTP/3"); break; case CURL_HTTP_VERSION_2: #ifdef USE_NGHTTP2 @@ -186,16 +201,12 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, return result; } #endif - infof(data, "using HTTP/2"); break; default: - infof(data, "using HTTP/1.x"); break; } ctx->state = CF_HC_SUCCESS; cf->connected = TRUE; - Curl_conn_cf_cntrl(cf->next, data, TRUE, - CF_CTRL_CONN_INFO_UPDATE, 0, NULL); return result; } @@ -322,6 +333,49 @@ out: return result; } +static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_hc_ctx *ctx = cf->ctx; + struct cf_hc_baller *ballers[2]; + size_t i; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* shutdown all ballers that have not done so already. If one fails, + * continue shutting down others until all are shutdown. */ + ballers[0] = &ctx->h3_baller; + ballers[1] = &ctx->h21_baller; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + struct cf_hc_baller *b = ballers[i]; + bool bdone = FALSE; + if(!cf_hc_baller_is_active(b) || b->shutdown) + continue; + b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); + if(b->result || bdone) + b->shutdown = TRUE; /* treat a failed shutdown as done */ + } + + *done = TRUE; + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + if(ballers[i] && !ballers[i]->shutdown) + *done = FALSE; + } + if(*done) { + for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) { + if(ballers[i] && ballers[i]->result) + result = ballers[i]->result; + } + } + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + return result; +} + static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -384,6 +438,8 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) { + struct cf_hc_ctx *ctx = cf->ctx; + if(!cf->connected) { switch(query) { case CF_QUERY_TIMER_CONNECT: { @@ -396,6 +452,14 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); return CURLE_OK; } + case CF_QUERY_NEED_FLUSH: { + if(cf_hc_baller_needs_flush(&ctx->h3_baller, data) + || cf_hc_baller_needs_flush(&ctx->h21_baller, data)) { + *pres1 = TRUE; + return CURLE_OK; + } + break; + } default: break; } @@ -405,6 +469,23 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } +static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_hc_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(!cf->connected) { + result = cf_hc_baller_cntrl(&ctx->h3_baller, data, event, arg1, arg2); + if(!result || (result == CURLE_AGAIN)) + result = cf_hc_baller_cntrl(&ctx->h21_baller, data, event, arg1, arg2); + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; +} + static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); @@ -434,12 +515,13 @@ struct Curl_cftype Curl_cft_http_connect = { cf_hc_destroy, cf_hc_connect, cf_hc_close, + cf_hc_shutdown, Curl_cf_def_get_host, cf_hc_adjust_pollset, cf_hc_data_pending, Curl_cf_def_send, Curl_cf_def_recv, - Curl_cf_def_cntrl, + cf_hc_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, cf_hc_query, @@ -510,7 +592,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { result = Curl_conn_may_http3(data, conn); - if(result) /* can't do it */ + if(result) /* cannot do it */ goto out; try_h3 = TRUE; try_h21 = FALSE; diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index 3e87889f9..e4d6a5b86 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -35,6 +35,9 @@ #elif defined(HAVE_NETINET_TCP_H) #include #endif +#ifdef HAVE_NETINET_UDP_H +#include +#endif #ifdef HAVE_SYS_IOCTL_H #include #endif @@ -53,6 +56,11 @@ #include #endif +#ifdef __DragonFly__ +/* Required for __DragonFly_version */ +#include +#endif + #include "urldata.h" #include "bufq.h" #include "sendf.h" @@ -73,6 +81,7 @@ #include "multihandle.h" #include "rand.h" #include "share.h" +#include "strdup.h" #include "version_win32.h" /* The last 3 #include files should be in this order */ @@ -115,7 +124,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) } #ifdef SO_NOSIGPIPE -/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when +/* The preferred method on macOS (10.2 and later) to prevent SIGPIPEs when sending data to a dead peer (instead of relying on the 4th argument to send being MSG_NOSIGNAL). Possibly also existing and in use on other BSD systems? */ @@ -137,8 +146,18 @@ static void nosigpipe(struct Curl_easy *data, #define nosigpipe(x,y) Curl_nop_stmt #endif -#if defined(__DragonFly__) || defined(USE_WINSOCK) -/* DragonFlyBSD and Windows use millisecond units */ +#if defined(USE_WINSOCK) && \ + defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) +/* Win 10, v 1709 (10.0.16299) and later can use SetSockOpt TCP_KEEP____ + * so should use seconds */ +#define CURL_WINSOCK_KEEP_SSO +#define KEEPALIVE_FACTOR(x) +#elif defined(USE_WINSOCK) || \ + (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ + (defined(__DragonFly__) && __DragonFly_version < 500702) || \ + (defined(_WIN32) && !defined(TCP_KEEPIDLE)) +/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299 + * use millisecond units. */ #define KEEPALIVE_FACTOR(x) (x *= 1000) #else #define KEEPALIVE_FACTOR(x) @@ -164,44 +183,80 @@ tcpkeepalive(struct Curl_easy *data, if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, sizeof(optval)) < 0) { infof(data, "Failed to set SO_KEEPALIVE on fd " - "%" CURL_FORMAT_SOCKET_T ": errno %d", + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } else { -#if defined(SIO_KEEPALIVE_VALS) +#if defined(SIO_KEEPALIVE_VALS) /* Windows */ +/* Windows 10, version 1709 (10.0.16299) and later versions */ +#if defined(CURL_WINSOCK_KEEP_SSO) + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPIDLE on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPINTVL on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (const char *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPCNT on fd " + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#else /* Windows < 10.0.16299 */ struct tcp_keepalive vals; DWORD dummy; vals.onoff = 1; optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); - vals.keepalivetime = optval; + vals.keepalivetime = (u_long)optval; optval = curlx_sltosi(data->set.tcp_keepintvl); KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = optval; + vals.keepaliveinterval = (u_long)optval; if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), NULL, 0, &dummy, NULL, NULL) != 0) { infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd " - "%" CURL_FORMAT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } -#else +#endif +#else /* !Windows */ #ifdef TCP_KEEPIDLE optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&optval, sizeof(optval)) < 0) { infof(data, "Failed to set TCP_KEEPIDLE on fd " - "%" CURL_FORMAT_SOCKET_T ": errno %d", + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #elif defined(TCP_KEEPALIVE) - /* Mac OS X style */ + /* macOS style */ optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, (void *)&optval, sizeof(optval)) < 0) { infof(data, "Failed to set TCP_KEEPALIVE on fd " - "%" CURL_FORMAT_SOCKET_T ": errno %d", + "%" FMT_SOCKET_T ": errno %d", + sockfd, SOCKERRNO); + } +#elif defined(TCP_KEEPALIVE_THRESHOLD) + /* Solaris <11.4 style */ + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #endif @@ -211,9 +266,37 @@ tcpkeepalive(struct Curl_easy *data, if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&optval, sizeof(optval)) < 0) { infof(data, "Failed to set TCP_KEEPINTVL on fd " - "%" CURL_FORMAT_SOCKET_T ": errno %d", + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } +#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD) + /* Solaris <11.4 style */ + /* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to + * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms. + * The default value of TCP_KEEPCNT is 9 on Linux, + * 8 on *BSD/macOS, 5 or 10 on Windows. We use the + * default config for Solaris <11.4 because there is + * no default value for TCP_KEEPCNT on Solaris 11.4. + * + * Note that the consequent probes will not be sent + * at equal intervals on Solaris, but will be sent + * using the exponential backoff algorithm. */ + optval = curlx_sltosi(data->set.tcp_keepcnt) * + curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } +#endif +#ifdef TCP_KEEPCNT + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (void *)&optval, sizeof(optval)) < 0) { + infof(data, "Failed to set TCP_KEEPCNT on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } #endif #endif } @@ -249,7 +332,7 @@ void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, dest->protocol = IPPROTO_UDP; break; } - dest->addrlen = ai->ai_addrlen; + dest->addrlen = (unsigned int)ai->ai_addrlen; if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) dest->addrlen = sizeof(struct Curl_sockaddr_storage); @@ -314,7 +397,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex dummy; if(!addr) - /* if the caller doesn't want info back, use a local temp copy */ + /* if the caller does not want info back, use a local temp copy */ addr = &dummy; Curl_sock_assign_addr(addr, ai, transport); @@ -324,6 +407,9 @@ CURLcode Curl_socket_open(struct Curl_easy *data, static int socket_close(struct Curl_easy *data, struct connectdata *conn, int use_callback, curl_socket_t sock) { + if(CURL_SOCKET_BAD == sock) + return 0; + if(use_callback && conn && conn->fclosesocket) { int rc; Curl_multi_closed(data, sock); @@ -363,14 +449,14 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, Buffer Size The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips + Windows. Following function trying to detect OS version and skips SO_SNDBUF adjustment for Windows Vista and above. */ #define DETECT_OS_NONE 0 #define DETECT_OS_PREVISTA 1 #define DETECT_OS_VISTA_OR_LATER 2 -void Curl_sndbufset(curl_socket_t sockfd) +void Curl_sndbuf_init(curl_socket_t sockfd) { int val = CURL_MAX_WRITE_SIZE + 32; int curval = 0; @@ -395,7 +481,88 @@ void Curl_sndbufset(curl_socket_t sockfd) setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); } -#endif +#endif /* USE_WINSOCK */ + +/* + * Curl_parse_interface() + * + * This is used to parse interface argument in the following formats. + * In all the examples, `host` can be an IP address or a hostname. + * + * - can be either an interface name or a host. + * if! - interface name. + * host! - hostname. + * ifhost!! - interface name and hostname. + * + * Parameters: + * + * input [in] - input string. + * len [in] - length of the input string. + * dev [in/out] - address where a pointer to newly allocated memory + * holding the interface-or-host will be stored upon + * completion. + * iface [in/out] - address where a pointer to newly allocated memory + * holding the interface will be stored upon completion. + * host [in/out] - address where a pointer to newly allocated memory + * holding the host will be stored upon completion. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_parse_interface(const char *input, + char **dev, char **iface, char **host) +{ + static const char if_prefix[] = "if!"; + static const char host_prefix[] = "host!"; + static const char if_host_prefix[] = "ifhost!"; + size_t len; + + DEBUGASSERT(dev); + DEBUGASSERT(iface); + DEBUGASSERT(host); + + len = strlen(input); + if(len > 512) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(!strncmp(if_prefix, input, strlen(if_prefix))) { + input += strlen(if_prefix); + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *iface = Curl_memdup0(input, len - strlen(if_prefix)); + return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY; + } + else if(!strncmp(host_prefix, input, strlen(host_prefix))) { + input += strlen(host_prefix); + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *host = Curl_memdup0(input, len - strlen(host_prefix)); + return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY; + } + else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) { + const char *host_part; + input += strlen(if_host_prefix); + len -= strlen(if_host_prefix); + host_part = memchr(input, '!', len); + if(!host_part || !*(host_part + 1)) + return CURLE_BAD_FUNCTION_ARGUMENT; + *iface = Curl_memdup0(input, host_part - input); + if(!*iface) + return CURLE_OUT_OF_MEMORY; + ++host_part; + *host = Curl_memdup0(host_part, len - (host_part - input)); + if(!*host) { + free(*iface); + *iface = NULL; + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; + } + + if(!*input) + return CURLE_BAD_FUNCTION_ARGUMENT; + *dev = Curl_memdup0(input, len); + return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} #ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, @@ -415,6 +582,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* how many port numbers to try to bind to, increasing one at a time */ int portnum = data->set.localportrange; const char *dev = data->set.str[STRING_DEVICE]; + const char *iface_input = data->set.str[STRING_INTERFACE]; + const char *host_input = data->set.str[STRING_BINDHOST]; + const char *iface = iface_input ? iface_input : dev; + const char *host = host_input ? host_input : dev; int error; #ifdef IP_BIND_ADDRESS_NO_PORT int on = 1; @@ -426,83 +597,77 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /************************************************************* * Select device to bind socket to *************************************************************/ - if(!dev && !port) + if(!iface && !host && !port) /* no local kind of binding was requested */ return CURLE_OK; memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); - if(dev && (strlen(dev)<255) ) { + if(iface && (strlen(iface)<255) ) { char myhost[256] = ""; int done = 0; /* -1 for error, 1 for address found */ - bool is_interface = FALSE; - bool is_host = FALSE; - static const char *if_prefix = "if!"; - static const char *host_prefix = "host!"; - - if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { - dev += strlen(if_prefix); - is_interface = TRUE; - } - else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { - dev += strlen(host_prefix); - is_host = TRUE; - } + if2ip_result_t if2ip_result = IF2IP_NOT_FOUND; /* interface */ - if(!is_host) { #ifdef SO_BINDTODEVICE - /* - * This binds the local socket to a particular interface. This will - * force even requests to other local interfaces to go out the external - * interface. Only bind to the interface when specified as interface, - * not just as a hostname or ip address. - * - * The interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try to - * use it straight away. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev) + 1) == 0) { - /* This is often "errno 1, error: Operation not permitted" if you're - * not running as root or another suitable privileged user. If it - * succeeds it means the parameter was a valid interface and not an IP - * address. Return immediately. - */ - infof(data, "socket successfully bound to interface '%s'", dev); + /* + * This binds the local socket to a particular interface. This will + * force even requests to other local interfaces to go out the external + * interface. Only bind to the interface when specified as interface, + * not just as a hostname or ip address. + * + * The interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try to + * use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + iface, (curl_socklen_t)strlen(iface) + 1) == 0) { + /* This is often "errno 1, error: Operation not permitted" if you are + * not running as root or another suitable privileged user. If it + * succeeds it means the parameter was a valid interface and not an IP + * address. Return immediately. + */ + if(!host_input) { + infof(data, "socket successfully bound to interface '%s'", iface); return CURLE_OK; } + } #endif - - switch(Curl_if2ip(af, + if(!host_input) { + /* Discover IP from input device, then bind to it */ + if2ip_result = Curl_if2ip(af, #ifdef USE_IPV6 - scope, conn->scope_id, -#endif - dev, myhost, sizeof(myhost))) { - case IF2IP_NOT_FOUND: - if(is_interface) { - /* Do not fall back to treating it as a host name */ - failf(data, "Couldn't bind to interface '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - break; - case IF2IP_AF_NOT_SUPPORTED: - /* Signal the caller to try another address family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - case IF2IP_FOUND: - is_interface = TRUE; - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - infof(data, "Local Interface %s is ip %s using address family %i", - dev, myhost, af); - done = 1; - break; - } + scope, conn->scope_id, +#endif + iface, myhost, sizeof(myhost)); + } + switch(if2ip_result) { + case IF2IP_NOT_FOUND: + if(iface_input && !host_input) { + /* Do not fall back to treating it as a hostname */ + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "Couldn't bind to interface '%s' with errno %d: %s", + iface, error, Curl_strerror(error, buffer, sizeof(buffer))); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + host = myhost; + infof(data, "Local Interface %s is ip %s using address family %i", + iface, host, af); + done = 1; + break; } - if(!is_interface) { + if(!iface_input || host_input) { /* - * This was not an interface, resolve the name as a host name + * This was not an interface, resolve the name as a hostname * or IP number * * Temporarily force name resolution to use only the address type @@ -519,18 +684,19 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, conn->ip_version = CURL_IPRESOLVE_V6; #endif - rc = Curl_resolv(data, dev, 80, FALSE, &h); + rc = Curl_resolv(data, host, 80, FALSE, &h); if(rc == CURLRESOLV_PENDING) (void)Curl_resolver_wait_resolv(data, &h); conn->ip_version = ipver; if(h) { + int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i", - dev, af, myhost, h->addr->ai_family); - Curl_resolv_unlock(data, h); - if(af != h->addr->ai_family) { + host, af, myhost, h_af); + Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */ + if(af != h_af) { /* bad IP version combo, signal the caller to try another address family if available */ return CURLE_UNSUPPORTED_PROTOCOL; @@ -540,7 +706,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, else { /* * provided dev was no interface (or interfaces are not supported - * e.g. solaris) no ip address and no domain we fail here + * e.g. Solaris) no ip address and no domain we fail here */ done = -1; } @@ -562,7 +728,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, if(scope_ptr) { /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if + IDs and the former returns none at all. So the scope ID, if present, is known to be numeric */ unsigned long scope_id = strtoul(scope_ptr, NULL, 10); if(scope_id > UINT_MAX) @@ -589,8 +755,11 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* errorbuf is set false so failf will overwrite any message already in the error buffer, so the user receives this error message instead of a generic resolve error. */ + char buffer[STRERROR_LEN]; data->state.errorbuf = FALSE; - failf(data, "Couldn't bind to '%s'", dev); + data->state.os_errno = error = SOCKERRNO; + failf(data, "Couldn't bind to '%s' with errno %d: %s", + host, error, Curl_strerror(error, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } } @@ -667,8 +836,8 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * Gisle Vanem could reproduce the former problems with this function, but * could avoid them by adding this SleepEx() call below: * - * "I don't have Rational Quantify, but the hint from his post was - * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe + * "I do not have Rational Quantify, but the hint from his post was + * ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe * just Sleep(0) would be enough?) would release whatever * mutex/critical-section the ntdll call is waiting on. * @@ -686,14 +855,14 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) err = SOCKERRNO; #ifdef _WIN32_WCE - /* Old WinCE versions don't support SO_ERROR */ + /* Old Windows CE versions do not support SO_ERROR */ if(WSAENOPROTOOPT == err) { SET_SOCKERRNO(0); err = 0; } #endif #if defined(EBADIOCTL) && defined(__minix) - /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ + /* Minix 3.1.x does not support getsockopt on UDP sockets */ if(EBADIOCTL == err) { SET_SOCKERRNO(0); err = 0; @@ -703,7 +872,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) /* we are connected, awesome! */ rc = TRUE; else - /* This wasn't a successful connect */ + /* This was not a successful connect */ rc = FALSE; if(error) *error = err; @@ -765,11 +934,14 @@ struct cf_socket_ctx { int transport; struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ - struct bufq recvbuf; /* used when `buffer_recv` is set */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ struct curltime started_at; /* when socket was created */ struct curltime connected_at; /* when socket connected/got first byte */ struct curltime first_byte_at; /* when first byte was recvd */ +#ifdef USE_WINSOCK + struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */ + ULONG sndbuf_size; /* the last set SO_SNDBUF size */ +#endif int error; /* errno of last failure or 0 */ #ifdef DEBUGBUILD int wblock_percent; /* percent of writes doing EAGAIN */ @@ -778,10 +950,10 @@ struct cf_socket_ctx { size_t recv_max; /* max enforced read size */ #endif BIT(got_first_byte); /* if first byte was received */ + BIT(listening); /* socket is listening */ BIT(accepted); /* socket was accepted, not connected */ BIT(sock_connected); /* socket is "connected", e.g. in UDP */ BIT(active); - BIT(buffer_recv); }; static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, @@ -792,7 +964,6 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; Curl_sock_assign_addr(&ctx->addr, ai, transport); - Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS); #ifdef DEBUGBUILD { char *p = getenv("CURL_DBG_SOCK_WBLOCK"); @@ -823,72 +994,19 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, #endif } -struct reader_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; -}; - -static ssize_t nw_in_read(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err) -{ - struct reader_ctx *rctx = reader_ctx; - struct cf_socket_ctx *ctx = rctx->cf->ctx; - ssize_t nread; - - *err = CURLE_OK; - nread = sread(ctx->sock, buf, len); - - if(-1 == nread) { - int sockerr = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == sockerr) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *err = CURLE_AGAIN; - nread = -1; - } - else { - char buffer[STRERROR_LEN]; - - failf(rctx->data, "Recv failure: %s", - Curl_strerror(sockerr, buffer, sizeof(buffer))); - rctx->data->state.os_errno = sockerr; - *err = CURLE_RECV_ERROR; - nread = -1; - } - } - CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%" - CURL_FORMAT_SOCKET_T ") -> %d, err=%d", - len, ctx->sock, (int)nread, *err); - return nread; -} - static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; if(ctx && CURL_SOCKET_BAD != ctx->sock) { - CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ")", ctx->sock); + CURL_TRC_CF(data, cf, "cf_socket_close(%" FMT_SOCKET_T ")", ctx->sock); if(ctx->sock == cf->conn->sock[cf->sockindex]) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; socket_close(data, cf->conn, !ctx->accepted, ctx->sock); ctx->sock = CURL_SOCKET_BAD; if(ctx->active && cf->sockindex == FIRSTSOCKET) cf->conn->remote_addr = NULL; - Curl_bufq_reset(&ctx->recvbuf); ctx->active = FALSE; - ctx->buffer_recv = FALSE; memset(&ctx->started_at, 0, sizeof(ctx->started_at)); memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); } @@ -896,13 +1014,34 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) cf->connected = FALSE; } +static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(cf->connected) { + struct cf_socket_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" FMT_SOCKET_T ")", ctx->sock); + /* On TCP, and when the socket looks well and non-blocking mode + * can be enabled, receive dangling bytes before close to avoid + * entering RST states unnecessarily. */ + if(ctx->sock != CURL_SOCKET_BAD && + ctx->transport == TRNSPRT_TCP && + (curlx_nonblock(ctx->sock, TRUE) >= 0)) { + unsigned char buf[1024]; + (void)sread(ctx->sock, buf, sizeof(buf)); + } + } + *done = TRUE; + return CURLE_OK; +} + static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; cf_socket_close(cf, data); CURL_TRC_CF(data, cf, "destroy"); - Curl_bufq_free(&ctx->recvbuf); free(ctx); cf->ctx = NULL; } @@ -949,7 +1088,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, struct cf_socket_ctx *ctx = cf->ctx; /* store remote address and port used in this connection attempt */ - if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen, + if(!Curl_addr2string(&ctx->addr.sa_addr, (curl_socklen_t)ctx->addr.addrlen, ctx->ip.remote_ip, &ctx->ip.remote_port)) { char buffer[STRERROR_LEN]; @@ -974,7 +1113,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); ctx->started_at = Curl_now(); +#ifdef SOCK_NONBLOCK + /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set + * because we would not know how socketype is about to be used in the + * callback, SOCK_NONBLOCK might get factored out before calling socket(). + */ + if(!data->set.fopensocket) + ctx->addr.socktype |= SOCK_NONBLOCK; +#endif result = socket_open(data, &ctx->addr, &ctx->sock); +#ifdef SOCK_NONBLOCK + /* Restore the socktype after the socket is created. */ + if(!data->set.fopensocket) + ctx->addr.socktype &= ~SOCK_NONBLOCK; +#endif if(result) goto out; @@ -1004,7 +1156,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, nosigpipe(data, ctx->sock); - Curl_sndbufset(ctx->sock); + Curl_sndbuf_init(ctx->sock); if(is_tcp && data->set.tcp_keepalive) tcpkeepalive(data, ctx->sock); @@ -1045,8 +1197,27 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } #endif - /* set socket non-blocking */ - (void)curlx_nonblock(ctx->sock, TRUE); +#ifndef SOCK_NONBLOCK + /* Set socket non-blocking, must be a non-blocking socket for + * a non-blocking connect. */ + error = curlx_nonblock(ctx->sock, TRUE); + if(error < 0) { + result = CURLE_UNSUPPORTED_PROTOCOL; + ctx->error = SOCKERRNO; + goto out; + } +#else + if(data->set.fopensocket) { + /* Set socket non-blocking, must be a non-blocking socket for + * a non-blocking connect. */ + error = curlx_nonblock(ctx->sock, TRUE); + if(error < 0) { + result = CURLE_UNSUPPORTED_PROTOCOL; + ctx->error = SOCKERRNO; + goto out; + } + } +#endif ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM); out: if(result) { @@ -1060,7 +1231,7 @@ out: ctx->connected_at = Curl_now(); cf->connected = TRUE; } - CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, + CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T, result, ctx->sock); return result; } @@ -1102,8 +1273,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, (void *)&optval, sizeof(optval)) < 0) - infof(data, "Failed to enable TCP Fast Open on fd %" - CURL_FORMAT_SOCKET_T, ctx->sock); + infof(data, "Failed to enable TCP Fast Open on fd %" FMT_SOCKET_T, + ctx->sock); rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); #elif defined(MSG_FASTOPEN) /* old Linux */ @@ -1114,7 +1285,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, #endif } else { - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + rc = connect(ctx->sock, &ctx->addr.sa_addr, + (curl_socklen_t)ctx->addr.addrlen); } return rc; } @@ -1237,15 +1409,24 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, struct cf_socket_ctx *ctx = cf->ctx; if(ctx->sock != CURL_SOCKET_BAD) { - if(!cf->connected) { + /* A listening socket filter needs to be connected before the accept + * for some weird FTP interaction. This should be rewritten, so that + * FTP no longer does the socket checks and accept calls and delegates + * all that to the filter. TODO. */ + if(ctx->listening) { + Curl_pollset_set_in_only(data, ps, ctx->sock); + CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%" + FMT_SOCKET_T, ctx->sock); + } + else if(!cf->connected) { Curl_pollset_set_out_only(data, ps, ctx->sock); CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%" - CURL_FORMAT_SOCKET_T, ctx->sock); + FMT_SOCKET_T, ctx->sock); } else if(!ctx->active) { Curl_pollset_add_in(data, ps, ctx->sock); CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%" - CURL_FORMAT_SOCKET_T, ctx->sock); + FMT_SOCKET_T, ctx->sock); } } } @@ -1257,21 +1438,46 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf, int readable; (void)data; - if(!Curl_bufq_is_empty(&ctx->recvbuf)) - return TRUE; - readable = SOCKET_READABLE(ctx->sock, 0); return (readable > 0 && (readable & CURL_CSELECT_IN)); } +#ifdef USE_WINSOCK + +#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY +#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B +#endif + +static void win_update_sndbuf_size(struct cf_socket_ctx *ctx) +{ + ULONG ideal; + DWORD ideallen; + struct curltime n = Curl_now(); + + if(Curl_timediff(n, ctx->last_sndbuf_query_at) > 1000) { + if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0, + &ideal, sizeof(ideal), &ideallen, 0, 0) && + ideal != ctx->sndbuf_size && + !setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF, + (const char *)&ideal, sizeof(ideal))) { + ctx->sndbuf_size = ideal; + } + ctx->last_sndbuf_query_at = n; + } +} + +#endif /* USE_WINSOCK */ + static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t nwritten; size_t orig_len = len; + (void)eos; /* unused */ *err = CURLE_OK; fdsave = cf->conn->sock[cf->sockindex]; cf->conn->sock[cf->sockindex] = ctx->sock; @@ -1280,7 +1486,7 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* simulate network blocking/partial writes */ if(ctx->wblock_percent > 0) { unsigned char c = 0; - Curl_rand(data, &c, 1); + Curl_rand_bytes(data, FALSE, &c, 1); if(c >= ((100-ctx->wblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); *err = CURLE_AGAIN; @@ -1336,6 +1542,11 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, } } +#if defined(USE_WINSOCK) + if(!*err) + win_update_sndbuf_size(ctx); +#endif + CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", orig_len, (int)nwritten, *err); cf->conn->sock[cf->sockindex] = fdsave; @@ -1346,14 +1557,10 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err) { struct cf_socket_ctx *ctx = cf->ctx; - curl_socket_t fdsave; ssize_t nread; *err = CURLE_OK; - fdsave = cf->conn->sock[cf->sockindex]; - cf->conn->sock[cf->sockindex] = ctx->sock; - #ifdef DEBUGBUILD /* simulate network blocking/partial reads */ if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { @@ -1362,9 +1569,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(c >= ((100-ctx->rblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); *err = CURLE_AGAIN; - nread = -1; - cf->conn->sock[cf->sockindex] = fdsave; - return nread; + return -1; } } if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { @@ -1375,57 +1580,57 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } #endif - if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) { - CURL_TRC_CF(data, cf, "recv from buffer"); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } - else { - struct reader_ctx rctx; - - rctx.cf = cf; - rctx.data = data; - - /* "small" reads may trigger filling our buffer, "large" reads - * are probably not worth the additional copy */ - if(ctx->buffer_recv && len < NW_SMALL_READS) { - ssize_t nwritten; - nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err); - if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) { - /* we have a partial read with an error. need to deliver - * what we got, return the error later. */ - CURL_TRC_CF(data, cf, "partial read: empty buffer first"); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } - else if(nwritten < 0) { - nread = -1; - goto out; - } - else if(nwritten == 0) { - /* eof */ - *err = CURLE_OK; - nread = 0; - } - else { - CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten); - nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); - } + *err = CURLE_OK; + nread = sread(ctx->sock, buf, len); + + if(-1 == nread) { + int sockerr = SOCKERRNO; + + if( +#ifdef WSAEWOULDBLOCK + /* This is how Windows does it */ + (WSAEWOULDBLOCK == sockerr) +#else + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned + due to its inability to send off data without blocking. We therefore + treat both error codes the same here */ + (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) +#endif + ) { + /* this is just a case of EWOULDBLOCK */ + *err = CURLE_AGAIN; } else { - nread = nw_in_read(&rctx, (unsigned char *)buf, len, err); + char buffer[STRERROR_LEN]; + + failf(data, "Recv failure: %s", + Curl_strerror(sockerr, buffer, sizeof(buffer))); + data->state.os_errno = sockerr; + *err = CURLE_RECV_ERROR; } } -out: CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, *err); if(nread > 0 && !ctx->got_first_byte) { ctx->first_byte_at = Curl_now(); ctx->got_first_byte = TRUE; } - cf->conn->sock[cf->sockindex] = fdsave; return nread; } +static void cf_socket_update_data(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + /* Update the IP info held in the transfer, if we have that. */ + if(cf->connected && (cf->sockindex == FIRSTSOCKET)) { + struct cf_socket_ctx *ctx = cf->ctx; + data->info.primary = ctx->ip; + /* not sure if this is redundant... */ + data->info.conn_remote_port = cf->conn->remote_port; + } +} + static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; @@ -1433,22 +1638,15 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) /* use this socket from now on */ cf->conn->sock[cf->sockindex] = ctx->sock; set_local_ip(cf, data); - if(cf->sockindex == SECONDARYSOCKET) - cf->conn->secondary = ctx->ip; - else - cf->conn->primary = ctx->ip; - /* the first socket info gets some specials */ if(cf->sockindex == FIRSTSOCKET) { + cf->conn->primary = ctx->ip; cf->conn->remote_addr = &ctx->addr; #ifdef USE_IPV6 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; #endif - Curl_persistconninfo(data, cf->conn, &ctx->ip); - /* buffering is currently disabled by default because we have stalls - * in parallel transfers where not all buffered data is consumed and no - * socket events happen. - */ - ctx->buffer_recv = FALSE; + } + else { + cf->conn->secondary = ctx->ip; } ctx->active = TRUE; } @@ -1464,9 +1662,10 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, switch(event) { case CF_CTRL_CONN_INFO_UPDATE: cf_socket_active(cf, data); + cf_socket_update_data(cf, data); break; case CF_CTRL_DATA_SETUP: - Curl_persistconninfo(data, cf->conn, &ctx->ip); + cf_socket_update_data(cf, data); break; case CF_CTRL_FORGET_SOCKET: ctx->sock = CURL_SOCKET_BAD; @@ -1549,6 +1748,14 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, } return CURLE_OK; } + case CF_QUERY_IP_INFO: +#ifdef USE_IPV6 + *pres1 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; +#else + *pres1 = FALSE; +#endif + *(struct ip_quadruple *)pres2 = ctx->ip; + return CURLE_OK; default: break; } @@ -1564,6 +1771,7 @@ struct Curl_cftype Curl_cft_tcp = { cf_socket_destroy, cf_tcp_connect, cf_socket_close, + cf_socket_shutdown, cf_socket_get_host, cf_socket_adjust_pollset, cf_socket_data_pending, @@ -1608,33 +1816,35 @@ out: } static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; int rc; + int one = 1; + + (void)one; /* QUIC needs a connected socket, nonblocking */ DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); -#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC) - (void)rc; - /* On macOS OpenSSL QUIC fails on connected sockets. - * see: */ -#else - rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); + rc = connect(ctx->sock, &ctx->addr.sa_addr, + (curl_socklen_t)ctx->addr.addrlen); if(-1 == rc) { return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO); } ctx->sock_connected = TRUE; -#endif set_local_ip(cf, data); - CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T + CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T " connected: [%s:%d] -> [%s:%d]", (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port, ctx->ip.remote_ip, ctx->ip.remote_port); - (void)curlx_nonblock(ctx->sock, TRUE); + /* Currently, cf->ctx->sock is always non-blocking because the only + * caller to cf_udp_setup_quic() is cf_udp_connect() that passes the + * non-blocking socket created by cf_socket_open() to it. Thus, we + * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore. + */ switch(ctx->addr.family) { #if defined(__linux__) && defined(IP_MTU_DISCOVER) case AF_INET: { @@ -1653,6 +1863,14 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, } #endif } + +#if defined(__linux__) && defined(UDP_GRO) && \ + (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ + ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) + (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, + (socklen_t)sizeof(one)); +#endif + return CURLE_OK; } @@ -1681,12 +1899,12 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(result) goto out; CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (%s:%d)", + FMT_SOCKET_T " (%s:%d)", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); } else { CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock); + FMT_SOCKET_T " (unconnected)", ctx->sock); } *done = TRUE; cf->connected = TRUE; @@ -1702,6 +1920,7 @@ struct Curl_cftype Curl_cft_udp = { cf_socket_destroy, cf_udp_connect, cf_socket_close, + cf_socket_shutdown, cf_socket_get_host, cf_socket_adjust_pollset, cf_socket_data_pending, @@ -1753,6 +1972,7 @@ struct Curl_cftype Curl_cft_unix = { cf_socket_destroy, cf_tcp_connect, cf_socket_close, + cf_socket_shutdown, cf_socket_get_host, cf_socket_adjust_pollset, cf_socket_data_pending, @@ -1817,6 +2037,7 @@ struct Curl_cftype Curl_cft_tcp_accept = { cf_socket_destroy, cf_tcp_accept_connect, cf_socket_close, + cf_socket_shutdown, cf_socket_get_host, /* TODO: not accurate */ cf_socket_adjust_pollset, cf_socket_data_pending, @@ -1847,6 +2068,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, } ctx->transport = conn->transport; ctx->sock = *s; + ctx->listening = TRUE; ctx->accepted = FALSE; result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx); if(result) @@ -1858,8 +2080,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, ctx->active = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" - CURL_FORMAT_SOCKET_T ")", ctx->sock); + CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" FMT_SOCKET_T ")", + ctx->sock); out: if(result) { @@ -1913,8 +2135,10 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, return CURLE_FAILED_INIT; ctx = cf->ctx; + DEBUGASSERT(ctx->listening); /* discard the listen socket */ socket_close(data, conn, TRUE, ctx->sock); + ctx->listening = FALSE; ctx->sock = *s; conn->sock[sockindex] = ctx->sock; set_accepted_remote_ip(cf, data); @@ -1923,7 +2147,7 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, ctx->accepted = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T + CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T ", remote=%s port=%d)", ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h index 058af5009..35225f153 100644 --- a/Utilities/cmcurl/lib/cf-socket.h +++ b/Utilities/cmcurl/lib/cf-socket.h @@ -54,6 +54,11 @@ struct Curl_sockaddr_ex { }; #define sa_addr _sa_ex_u.addr +/* + * Parse interface option, and return the interface name and the host part. +*/ +CURLcode Curl_parse_interface(const char *input, + char **dev, char **iface, char **host); /* * Create a socket based on info from 'conn' and 'ai'. @@ -81,9 +86,9 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, Buffer Size */ -void Curl_sndbufset(curl_socket_t sockfd); +void Curl_sndbuf_init(curl_socket_t sockfd); #else -#define Curl_sndbufset(y) Curl_nop_stmt +#define Curl_sndbuf_init(y) Curl_nop_stmt #endif /** diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index a327fa194..3d7da0c69 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -45,7 +45,10 @@ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) #endif -#ifdef DEBUGBUILD +static void cf_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn); + +#ifdef UNITTESTS /* used by unit2600.c */ void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -55,6 +58,15 @@ void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif +CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + (void)cf; + (void)data; + *done = TRUE; + return CURLE_OK; +} + static void conn_report_connect_stats(struct Curl_easy *data, struct connectdata *conn); @@ -89,10 +101,11 @@ bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, } ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { return cf->next? - cf->next->cft->do_send(cf->next, data, buf, len, err) : + cf->next->cft->do_send(cf->next, data, buf, len, eos, err) : CURLE_RECV_ERROR; } @@ -166,6 +179,61 @@ void Curl_conn_close(struct Curl_easy *data, int index) if(cf) { cf->cft->do_close(cf, data); } + Curl_shutdown_clear(data, index); +} + +CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + struct curltime now; + + DEBUGASSERT(data->conn); + /* Get the first connected filter that is not shut down already. */ + cf = data->conn->cfilter[sockindex]; + while(cf && (!cf->connected || cf->shutdown)) + cf = cf->next; + + if(!cf) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + now = Curl_now(); + if(!Curl_shutdown_started(data, sockindex)) { + DEBUGF(infof(data, "shutdown start on%s connection", + sockindex? " secondary" : "")); + Curl_shutdown_start(data, sockindex, &now); + } + else { + timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now); + if(timeout_ms < 0) { + failf(data, "SSL shutdown timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + } + + while(cf) { + if(!cf->shutdown) { + bool cfdone = FALSE; + result = cf->cft->do_shutdown(cf, data, &cfdone); + if(result) { + CURL_TRC_CF(data, cf, "shut down failed with %d", result); + return result; + } + else if(!cfdone) { + CURL_TRC_CF(data, cf, "shut down not done yet"); + return CURLE_OK; + } + CURL_TRC_CF(data, cf, "shut down successfully"); + cf->shutdown = TRUE; + } + cf = cf->next; + } + *done = (!result); + return result; } ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf, @@ -192,7 +260,8 @@ ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf, } ssize_t Curl_cf_send(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) + const void *mem, size_t len, bool eos, + CURLcode *code) { struct Curl_cfilter *cf; @@ -204,7 +273,7 @@ ssize_t Curl_cf_send(struct Curl_easy *data, int num, cf = cf->next; } if(cf) { - ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, code); + ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, eos, code); DEBUGASSERT(nwritten >= 0 || *code); DEBUGASSERT(nwritten < 0 || !*code || !len); return nwritten; @@ -315,10 +384,11 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) } ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { if(cf) - return cf->cft->do_send(cf, data, buf, len, err); + return cf->cft->do_send(cf, data, buf, len, eos, err); *err = CURLE_SEND_ERROR; return -1; } @@ -345,16 +415,29 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, cf = data->conn->cfilter[sockindex]; DEBUGASSERT(cf); - if(!cf) + if(!cf) { + *done = FALSE; return CURLE_FAILED_INIT; + } *done = cf->connected; if(!*done) { + if(Curl_conn_needs_flush(data, sockindex)) { + DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); + result = Curl_conn_flush(data, sockindex); + if(result && (result != CURLE_AGAIN)) + return result; + } + result = cf->cft->do_connect(cf, data, blocking, done); if(!result && *done) { - Curl_conn_ev_update_info(data, data->conn); + /* Now that the complete filter chain is connected, let all filters + * persist information at the connection. E.g. cf-socket sets the + * socket and ip related information. */ + cf_cntrl_update_info(data, data->conn); conn_report_connect_stats(data, data->conn); data->conn->keepalive = Curl_now(); + Curl_verboseconnect(data, data->conn, sockindex); } else if(result) { conn_report_connect_stats(data, data->conn); @@ -435,6 +518,21 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex) return FALSE; } +bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + CURLcode result; + int pending = FALSE; + result = cf? cf->cft->query(cf, data, CF_QUERY_NEED_FLUSH, + &pending, NULL) : CURLE_UNKNOWN_OPTION; + return (result || pending == FALSE)? FALSE : TRUE; +} + +bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex) +{ + return Curl_conn_cf_needs_flush(data->conn->cfilter[sockindex], data); +} + void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -442,6 +540,9 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, /* Get the lowest not-connected filter, if there are any */ while(cf && !cf->connected && cf->next && !cf->next->connected) cf = cf->next; + /* Skip all filters that have already shut down */ + while(cf && cf->shutdown) + cf = cf->next; /* From there on, give all filters a chance to adjust the pollset. * Lower filters are called later, so they may override */ while(cf) { @@ -462,6 +563,42 @@ void Curl_conn_adjust_pollset(struct Curl_easy *data, } } +int Curl_conn_cf_poll(struct Curl_cfilter *cf, + struct Curl_easy *data, + timediff_t timeout_ms) +{ + struct easy_pollset ps; + struct pollfd pfds[MAX_SOCKSPEREASYHANDLE]; + unsigned int i, npfds = 0; + + DEBUGASSERT(cf); + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + memset(&ps, 0, sizeof(ps)); + memset(pfds, 0, sizeof(pfds)); + + Curl_conn_cf_adjust_pollset(cf, data, &ps); + DEBUGASSERT(ps.num <= MAX_SOCKSPEREASYHANDLE); + for(i = 0; i < ps.num; ++i) { + short events = 0; + if(ps.actions[i] & CURL_POLL_IN) { + events |= POLLIN; + } + if(ps.actions[i] & CURL_POLL_OUT) { + events |= POLLOUT; + } + if(events) { + pfds[npfds].fd = ps.sockets[i]; + pfds[npfds].events = events; + ++npfds; + } + } + + if(!npfds) + DEBUGF(infof(data, "no sockets to poll!")); + return Curl_poll(pfds, npfds, timeout_ms); +} + void Curl_conn_get_host(struct Curl_easy *data, int sockindex, const char **phost, const char **pdisplay_host, int *pport) @@ -522,6 +659,15 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, return CURL_SOCKET_BAD; } +CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, + struct Curl_easy *data, + int *is_ipv6, struct ip_quadruple *ipquad) +{ + if(cf) + return cf->cft->query(cf, data, CF_QUERY_IP_INFO, is_ipv6, ipquad); + return CURLE_UNKNOWN_OPTION; +} + curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf; @@ -588,6 +734,13 @@ CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data) CF_CTRL_DATA_IDLE, 0, NULL); } + +CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex) +{ + return Curl_conn_cf_cntrl(data->conn->cfilter[sockindex], data, FALSE, + CF_CTRL_FLUSH, 0, NULL); +} + /** * Notify connection filters that the transfer represented by `data` * is done with sending data (e.g. has uploaded everything). @@ -612,8 +765,8 @@ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause) CF_CTRL_DATA_PAUSE, do_pause, NULL); } -void Curl_conn_ev_update_info(struct Curl_easy *data, - struct connectdata *conn) +static void cf_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn) { cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); } @@ -706,9 +859,10 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, } CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, + const void *buf, size_t blen, bool eos, size_t *pnwritten) { + size_t write_len = blen; ssize_t nwritten; CURLcode result = CURLE_OK; struct connectdata *conn; @@ -718,7 +872,7 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, DEBUGASSERT(data); DEBUGASSERT(data->conn); conn = data->conn; -#ifdef CURLDEBUG +#ifdef DEBUGBUILD { /* Allow debug builds to override this logic to force short sends */ @@ -726,11 +880,14 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, if(p) { size_t altsize = (size_t)strtoul(p, NULL, 10); if(altsize) - blen = CURLMIN(blen, altsize); + write_len = CURLMIN(write_len, altsize); } } #endif - nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); + if(write_len != blen) + eos = FALSE; + nwritten = conn->send[sockindex](data, sockindex, buf, write_len, eos, + &result); DEBUGASSERT((nwritten >= 0) || result); *pnwritten = (nwritten < 0)? 0 : (size_t)nwritten; return result; diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index dcfc1b71f..af696f52a 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h @@ -24,11 +24,13 @@ * ***************************************************************************/ +#include "timediff.h" struct Curl_cfilter; struct Curl_easy; struct Curl_dns_entry; struct connectdata; +struct ip_quadruple; /* Callback to destroy resources held by this filter instance. * Implementations MUST NOT chain calls to cf->next. @@ -36,9 +38,17 @@ struct connectdata; typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data); +/* Callback to close the connection immediately. */ typedef void Curl_cft_close(struct Curl_cfilter *cf, struct Curl_easy *data); +/* Callback to close the connection filter gracefully, non-blocking. + * Implementations MUST NOT chain calls to cf->next. + */ +typedef CURLcode Curl_cft_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done); @@ -76,10 +86,10 @@ struct easy_pollset; * the pollset. Filters, whose filter "below" is not connected, should * also do no adjustments. * - * Examples: a TLS handshake, while ongoing, might remove POLL_IN - * when it needs to write, or vice versa. A HTTP/2 filter might remove - * POLL_OUT when a stream window is exhausted and a WINDOW_UPDATE needs - * to be received first and add instead POLL_IN. + * Examples: a TLS handshake, while ongoing, might remove POLL_IN when it + * needs to write, or vice versa. An HTTP/2 filter might remove POLL_OUT when + * a stream window is exhausted and a WINDOW_UPDATE needs to be received first + * and add instead POLL_IN. * * @param cf the filter to ask * @param data the easy handle the pollset is about @@ -96,6 +106,7 @@ typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ const void *buf, /* data to write */ size_t len, /* amount to write */ + bool eos, /* last chunk */ CURLcode *err); /* error to return */ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, @@ -131,6 +142,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, /* update conn info at connection and data */ #define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ #define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */ +#define CF_CTRL_FLUSH (256+2) /* 0 NULL first fail */ /** * Handle event/control for the filter. @@ -153,6 +165,9 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, * were received. * -1 if not determined yet. * - CF_QUERY_SOCKET: the socket used by the filter chain + * - CF_QUERY_NEED_FLUSH: TRUE iff any of the filters have unsent data + * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the + * ip quadruple */ /* query res1 res2 */ #define CF_QUERY_MAX_CONCURRENT 1 /* number - */ @@ -161,6 +176,8 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */ #define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */ #define CF_QUERY_STREAM_ERROR 6 /* error code - */ +#define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ +#define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -194,6 +211,7 @@ struct Curl_cftype { Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ Curl_cft_connect *do_connect; /* establish connection */ Curl_cft_close *do_close; /* close conn */ + Curl_cft_shutdown *do_shutdown; /* shutdown conn */ Curl_cft_get_host *get_host; /* host filter talks to */ Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ Curl_cft_data_pending *has_data_pending;/* conn has data pending */ @@ -213,6 +231,7 @@ struct Curl_cfilter { struct connectdata *conn; /* the connection this filter belongs to */ int sockindex; /* the index the filter is installed at */ BIT(connected); /* != 0 iff this filter is connected */ + BIT(shutdown); /* != 0 iff this filter has shut down */ }; /* Default implementations for the type functions, implementing nop. */ @@ -230,7 +249,8 @@ void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err); + const void *buf, size_t len, bool eos, + CURLcode *err); ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err); CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, @@ -244,6 +264,8 @@ CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf, CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2); +CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); /** * Create a new filter instance, unattached to the filter chain. @@ -304,7 +326,8 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, bool blocking, bool *done); void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err); + const void *buf, size_t len, bool eos, + CURLcode *err); ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err); CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, @@ -325,6 +348,12 @@ bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf); curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, struct Curl_easy *data); +CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, + struct Curl_easy *data, + int *is_ipv6, struct ip_quadruple *ipquad); + +bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, + struct Curl_easy *data); #define CURL_CF_SSL_DEFAULT -1 #define CURL_CF_SSL_DISABLE 0 @@ -371,6 +400,13 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); */ void Curl_conn_close(struct Curl_easy *data, int sockindex); +/** + * Shutdown the connection at `sockindex` non-blocking, using timeout + * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. + * Will return CURLE_OK and *done == FALSE if not finished. + */ +CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); + /** * Return if data is pending in some connection filter at chain * `sockindex` for connection `data->conn`. @@ -378,6 +414,17 @@ void Curl_conn_close(struct Curl_easy *data, int sockindex); bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex); +/** + * Return TRUE if any of the connection filters at chain `sockindex` + * have data still to send. + */ +bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex); + +/** + * Flush any pending data on the connection filters at chain `sockindex`. + */ +CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex); + /** * Return the socket used on data's connection for the index. * Returns CURL_SOCKET_BAD if not available. @@ -402,6 +449,15 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, void Curl_conn_adjust_pollset(struct Curl_easy *data, struct easy_pollset *ps); +/** + * Curl_poll() the filter chain at `cf` with timeout `timeout_ms`. + * Returns 0 on timeout, negative on error or number of sockets + * with requested poll events. + */ +int Curl_conn_cf_poll(struct Curl_cfilter *cf, + struct Curl_easy *data, + timediff_t timeout_ms); + /** * Receive data through the filter chain at `sockindex` for connection * `data->conn`. Copy at most `len` bytes into `buf`. Return the @@ -418,7 +474,7 @@ ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, * The error code is placed into `*code`. */ ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *code); + const void *buf, size_t len, bool eos, CURLcode *code); /** * The easy handle `data` is being attached to `conn`. This does @@ -467,12 +523,6 @@ void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature); */ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause); -/** - * Inform connection filters to update their info in `conn`. - */ -void Curl_conn_ev_update_info(struct Curl_easy *data, - struct connectdata *conn); - /** * Check if FIRSTSOCKET's cfilter chain deems connection alive. */ @@ -486,7 +536,9 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, struct connectdata *conn, int sockindex); +#ifdef UNITTESTS void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); +#endif void Curl_conn_get_host(struct Curl_easy *data, int sockindex, const char **phost, const char **pdisplay_host, int *pport); @@ -526,7 +578,7 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, * Will return CURLE_AGAIN iff blocked on sending. */ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t blen, + const void *buf, size_t blen, bool eos, size_t *pnwritten); diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index 2b5ee4f25..8f477827c 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c @@ -29,13 +29,17 @@ #include "urldata.h" #include "url.h" +#include "cfilters.h" #include "progress.h" #include "multiif.h" #include "sendf.h" #include "conncache.h" +#include "http_negotiate.h" +#include "http_ntlm.h" #include "share.h" #include "sigpipe.h" #include "connect.h" +#include "select.h" #include "strcase.h" /* The last 3 #include files should be in this order */ @@ -43,167 +47,242 @@ #include "curl_memory.h" #include "memdebug.h" -#define HASHKEY_SIZE 128 -static CURLcode bundle_create(struct connectbundle **bundlep) +#define CPOOL_IS_LOCKED(c) ((c) && (c)->locked) + +#define CPOOL_LOCK(c) \ + do { \ + if((c)) { \ + if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ + Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT, \ + CURL_LOCK_ACCESS_SINGLE); \ + DEBUGASSERT(!(c)->locked); \ + (c)->locked = TRUE; \ + } \ + } while(0) + +#define CPOOL_UNLOCK(c) \ + do { \ + if((c)) { \ + DEBUGASSERT((c)->locked); \ + (c)->locked = FALSE; \ + if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ + Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT); \ + } \ + } while(0) + + +/* A list of connections to the same destinationn. */ +struct cpool_bundle { + struct Curl_llist conns; /* connections in the bundle */ + size_t dest_len; /* total length of destination, including NUL */ + char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */ +}; + + +static void cpool_discard_conn(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted); +static void cpool_close_and_destroy(struct cpool *cpool, + struct connectdata *conn, + struct Curl_easy *data, + bool do_shutdown); +static void cpool_run_conn_shutdown(struct Curl_easy *data, + struct connectdata *conn, + bool *done); +static void cpool_run_conn_shutdown_handler(struct Curl_easy *data, + struct connectdata *conn); +static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn); +static void cpool_shutdown_all(struct cpool *cpool, + struct Curl_easy *data, int timeout_ms); +static void cpool_close_and_destroy_all(struct cpool *cpool); +static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool); + +static struct cpool_bundle *cpool_bundle_create(const char *dest, + size_t dest_len) { - DEBUGASSERT(*bundlep == NULL); - *bundlep = malloc(sizeof(struct connectbundle)); - if(!*bundlep) - return CURLE_OUT_OF_MEMORY; - - (*bundlep)->num_connections = 0; - (*bundlep)->multiuse = BUNDLE_UNKNOWN; - - Curl_llist_init(&(*bundlep)->conn_list, NULL); - return CURLE_OK; + struct cpool_bundle *bundle; + bundle = calloc(1, sizeof(*bundle) + dest_len); + if(!bundle) + return NULL; + Curl_llist_init(&bundle->conns, NULL); + bundle->dest_len = dest_len; + memcpy(bundle->dest, dest, dest_len); + return bundle; } -static void bundle_destroy(struct connectbundle *bundle) +static void cpool_bundle_destroy(struct cpool_bundle *bundle) { + DEBUGASSERT(!Curl_llist_count(&bundle->conns)); free(bundle); } /* Add a connection to a bundle */ -static void bundle_add_conn(struct connectbundle *bundle, - struct connectdata *conn) +static void cpool_bundle_add(struct cpool_bundle *bundle, + struct connectdata *conn) { - Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node); - conn->bundle = bundle; - bundle->num_connections++; + DEBUGASSERT(!Curl_node_llist(&conn->cpool_node)); + Curl_llist_append(&bundle->conns, conn, &conn->cpool_node); + conn->bits.in_cpool = TRUE; } /* Remove a connection from a bundle */ -static int bundle_remove_conn(struct connectbundle *bundle, - struct connectdata *conn) +static void cpool_bundle_remove(struct cpool_bundle *bundle, + struct connectdata *conn) { - struct Curl_llist_element *curr; - - curr = bundle->conn_list.head; - while(curr) { - if(curr->ptr == conn) { - Curl_llist_remove(&bundle->conn_list, curr, NULL); - bundle->num_connections--; - conn->bundle = NULL; - return 1; /* we removed a handle */ - } - curr = curr->next; - } - DEBUGASSERT(0); - return 0; + (void)bundle; + DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns); + Curl_node_remove(&conn->cpool_node); + conn->bits.in_cpool = FALSE; } -static void free_bundle_hash_entry(void *freethis) +static void cpool_bundle_free_entry(void *freethis) { - struct connectbundle *b = (struct connectbundle *) freethis; - - bundle_destroy(b); + cpool_bundle_destroy((struct cpool_bundle *)freethis); } -int Curl_conncache_init(struct conncache *connc, size_t size) +int Curl_cpool_init(struct cpool *cpool, + Curl_cpool_disconnect_cb *disconnect_cb, + struct Curl_multi *multi, + struct Curl_share *share, + size_t size) { + DEBUGASSERT(!!multi != !!share); /* either one */ + Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str, + Curl_str_key_compare, cpool_bundle_free_entry); + Curl_llist_init(&cpool->shutdowns, NULL); + + DEBUGASSERT(disconnect_cb); + if(!disconnect_cb) + return 1; + /* allocate a new easy handle to use when closing cached connections */ - connc->closure_handle = curl_easy_init(); - if(!connc->closure_handle) + cpool->idata = curl_easy_init(); + if(!cpool->idata) return 1; /* bad */ - connc->closure_handle->state.internal = true; + cpool->idata->state.internal = true; + /* TODO: this is quirky. We need an internal handle for certain + * operations, but we do not add it to the multi (if there is one). + * But we give it the multi so that socket event operations can work. + * Probably better to have an internal handle owned by the multi that + * can be used for cpool operations. */ + cpool->idata->multi = multi; + #ifdef DEBUGBUILD + if(getenv("CURL_DEBUG")) + cpool->idata->set.verbose = true; +#endif - Curl_hash_init(&connc->hash, size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); - connc->closure_handle->state.conn_cache = connc; + cpool->disconnect_cb = disconnect_cb; + cpool->idata->multi = cpool->multi = multi; + cpool->idata->share = cpool->share = share; return 0; /* good */ } -void Curl_conncache_destroy(struct conncache *connc) +void Curl_cpool_destroy(struct cpool *cpool) { - if(connc) - Curl_hash_destroy(&connc->hash); + if(cpool) { + if(cpool->idata) { + cpool_close_and_destroy_all(cpool); + /* The internal closure handle is special and we need to + * disconnect it from multi/share before closing it down. */ + cpool->idata->multi = NULL; + cpool->idata->share = NULL; + Curl_close(&cpool->idata); + } + Curl_hash_destroy(&cpool->dest2bundle); + cpool->multi = NULL; + } } -/* creates a key to find a bundle for this connection */ -static void hashkey(struct connectdata *conn, char *buf, size_t len) +static struct cpool *cpool_get_instance(struct Curl_easy *data) { - const char *hostname; - long port = conn->remote_port; - DEBUGASSERT(len >= HASHKEY_SIZE); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - hostname = conn->http_proxy.host.name; - port = conn->primary.remote_port; + if(data) { + if(CURL_SHARE_KEEP_CONNECT(data->share)) + return &data->share->cpool; + else if(data->multi_easy) + return &data->multi_easy->cpool; + else if(data->multi) + return &data->multi->cpool; } - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - /* put the numbers first so that the hostname gets cut off if too long */ -#ifdef USE_IPV6 - msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); -#else - msnprintf(buf, len, "%ld/%s", port, hostname); -#endif - Curl_strntolower(buf, buf, len); + return NULL; } -/* Returns number of connections currently held in the connection cache. - Locks/unlocks the cache itself! -*/ -size_t Curl_conncache_size(struct Curl_easy *data) +void Curl_cpool_xfer_init(struct Curl_easy *data) { - size_t num; - CONNCACHE_LOCK(data); - num = data->state.conn_cache->num_conn; - CONNCACHE_UNLOCK(data); - return num; + struct cpool *cpool = cpool_get_instance(data); + + DEBUGASSERT(cpool); + if(cpool) { + CPOOL_LOCK(cpool); + /* the identifier inside the connection cache */ + data->id = cpool->next_easy_id++; + if(cpool->next_easy_id <= 0) + cpool->next_easy_id = 0; + data->state.lastconnect_id = -1; + + /* The closure handle only ever has default timeouts set. To improve the + state somewhat we clone the timeouts from each added handle so that the + closure handle always has the same timeouts as the most recently added + easy handle. */ + cpool->idata->set.timeout = data->set.timeout; + cpool->idata->set.server_response_timeout = + data->set.server_response_timeout; + cpool->idata->set.no_signal = data->set.no_signal; + + CPOOL_UNLOCK(cpool); + } + else { + /* We should not get here, but in a non-debug build, do something */ + data->id = 0; + data->state.lastconnect_id = -1; + } } -/* Look up the bundle with all the connections to the same host this - connectdata struct is setup to use. - - **NOTE**: When it returns, it holds the connection cache lock! */ -struct connectbundle * -Curl_conncache_find_bundle(struct Curl_easy *data, - struct connectdata *conn, - struct conncache *connc) +static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool, + struct connectdata *conn) { - struct connectbundle *bundle = NULL; - CONNCACHE_LOCK(data); - if(connc) { - char key[HASHKEY_SIZE]; - hashkey(conn, key, sizeof(key)); - bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); - } - - return bundle; + return Curl_hash_pick(&cpool->dest2bundle, + conn->destination, conn->destination_len); } -static void *conncache_add_bundle(struct conncache *connc, - char *key, - struct connectbundle *bundle) +static struct cpool_bundle * +cpool_add_bundle(struct cpool *cpool, struct connectdata *conn) { - return Curl_hash_add(&connc->hash, key, strlen(key), bundle); + struct cpool_bundle *bundle; + + bundle = cpool_bundle_create(conn->destination, conn->destination_len); + if(!bundle) + return NULL; + + if(!Curl_hash_add(&cpool->dest2bundle, + bundle->dest, bundle->dest_len, bundle)) { + cpool_bundle_destroy(bundle); + return NULL; + } + return bundle; } -static void conncache_remove_bundle(struct conncache *connc, - struct connectbundle *bundle) +static void cpool_remove_bundle(struct cpool *cpool, + struct cpool_bundle *bundle) { struct Curl_hash_iterator iter; struct Curl_hash_element *he; - if(!connc) + if(!cpool) return; - Curl_hash_start_iterate(&connc->hash, &iter); + Curl_hash_start_iterate(&cpool->dest2bundle, &iter); he = Curl_hash_next_element(&iter); while(he) { if(he->ptr == bundle) { /* The bundle is destroyed by the hash destructor function, free_bundle_hash_entry() */ - Curl_hash_delete(&connc->hash, he->key, he->key_len); + Curl_hash_delete(&cpool->dest2bundle, he->key, he->key_len); return; } @@ -211,225 +290,252 @@ static void conncache_remove_bundle(struct conncache *connc, } } -CURLcode Curl_conncache_add_conn(struct Curl_easy *data) +static struct connectdata * +cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle); + +int Curl_cpool_check_limits(struct Curl_easy *data, + struct connectdata *conn) { - CURLcode result = CURLE_OK; - struct connectbundle *bundle = NULL; - struct connectdata *conn = data->conn; - struct conncache *connc = data->state.conn_cache; - DEBUGASSERT(conn); + struct cpool *cpool = cpool_get_instance(data); + struct cpool_bundle *bundle; + size_t dest_limit = 0; + size_t total_limit = 0; + int result = CPOOL_LIMIT_OK; + + if(!cpool) + return CPOOL_LIMIT_OK; + + if(data && data->multi) { + dest_limit = data->multi->max_host_connections; + total_limit = data->multi->max_total_connections; + } - /* *find_bundle() locks the connection cache */ - bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache); - if(!bundle) { - char key[HASHKEY_SIZE]; + if(!dest_limit && !total_limit) + return CPOOL_LIMIT_OK; + + CPOOL_LOCK(cpool); + if(dest_limit) { + bundle = cpool_find_bundle(cpool, conn); + while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) { + struct connectdata *oldest_idle = NULL; + /* The bundle is full. Extract the oldest connection that may + * be removed now, if there is one. */ + oldest_idle = cpool_bundle_get_oldest_idle(bundle); + if(!oldest_idle) + break; + /* disconnect the old conn and continue */ + DEBUGF(infof(data, "Discarding connection #%" + FMT_OFF_T " from %zu to reach destination " + "limit of %zu", oldest_idle->connection_id, + Curl_llist_count(&bundle->conns), dest_limit)); + Curl_cpool_disconnect(data, oldest_idle, FALSE); + } + if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) { + result = CPOOL_LIMIT_DEST; + goto out; + } + } - result = bundle_create(&bundle); - if(result) { - goto unlock; + if(total_limit) { + while(cpool->num_conn >= total_limit) { + struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool); + if(!oldest_idle) + break; + /* disconnect the old conn and continue */ + DEBUGF(infof(data, "Discarding connection #%" + FMT_OFF_T " from %zu to reach total " + "limit of %zu", + oldest_idle->connection_id, cpool->num_conn, total_limit)); + Curl_cpool_disconnect(data, oldest_idle, FALSE); + } + if(cpool->num_conn >= total_limit) { + result = CPOOL_LIMIT_TOTAL; + goto out; } + } - hashkey(conn, key, sizeof(key)); +out: + CPOOL_UNLOCK(cpool); + return result; +} - if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) { - bundle_destroy(bundle); +CURLcode Curl_cpool_add_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct cpool_bundle *bundle = NULL; + struct cpool *cpool = cpool_get_instance(data); + DEBUGASSERT(conn); + + DEBUGASSERT(cpool); + if(!cpool) + return CURLE_FAILED_INIT; + + CPOOL_LOCK(cpool); + bundle = cpool_find_bundle(cpool, conn); + if(!bundle) { + bundle = cpool_add_bundle(cpool, conn); + if(!bundle) { result = CURLE_OUT_OF_MEMORY; - goto unlock; + goto out; } } - bundle_add_conn(bundle, conn); - conn->connection_id = connc->next_connection_id++; - connc->num_conn++; - - DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". " + cpool_bundle_add(bundle, conn); + conn->connection_id = cpool->next_connection_id++; + cpool->num_conn++; + DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". " "The cache now contains %zu members", - conn->connection_id, connc->num_conn)); - -unlock: - CONNCACHE_UNLOCK(data); + conn->connection_id, cpool->num_conn)); +out: + CPOOL_UNLOCK(cpool); return result; } -/* - * Removes the connectdata object from the connection cache, but the transfer - * still owns this connection. - * - * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function - * already holds the lock or not. - */ -void Curl_conncache_remove_conn(struct Curl_easy *data, - struct connectdata *conn, bool lock) +static void cpool_remove_conn(struct cpool *cpool, + struct connectdata *conn) { - struct connectbundle *bundle = conn->bundle; - struct conncache *connc = data->state.conn_cache; - - /* The bundle pointer can be NULL, since this function can be called - due to a failed connection attempt, before being added to a bundle */ - if(bundle) { - if(lock) { - CONNCACHE_LOCK(data); - } - bundle_remove_conn(bundle, conn); - if(bundle->num_connections == 0) - conncache_remove_bundle(connc, bundle); - conn->bundle = NULL; /* removed from it */ - if(connc) { - connc->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - connc->num_conn)); + struct Curl_llist *list = Curl_node_llist(&conn->cpool_node); + DEBUGASSERT(cpool); + if(list) { + /* The connection is certainly in the pool, but where? */ + struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn); + if(bundle && (list == &bundle->conns)) { + cpool_bundle_remove(bundle, conn); + if(!Curl_llist_count(&bundle->conns)) + cpool_remove_bundle(cpool, bundle); + conn->bits.in_cpool = FALSE; + cpool->num_conn--; } - if(lock) { - CONNCACHE_UNLOCK(data); + else { + /* Not in a bundle, already in the shutdown list? */ + DEBUGASSERT(list == &cpool->shutdowns); } } } -/* This function iterates the entire connection cache and calls the function +/* This function iterates the entire connection pool and calls the function func() with the connection pointer as the first argument and the supplied 'param' argument as the other. - The conncache lock is still held when the callback is called. It needs it, + The cpool lock is still held when the callback is called. It needs it, so that it can safely continue traversing the lists once the callback returns. - Returns 1 if the loop was aborted due to the callback's return code. + Returns TRUE if the loop was aborted due to the callback's return code. Return 0 from func() to continue the loop, return 1 to abort it. */ -bool Curl_conncache_foreach(struct Curl_easy *data, - struct conncache *connc, - void *param, - int (*func)(struct Curl_easy *data, - struct connectdata *conn, void *param)) +static bool cpool_foreach(struct Curl_easy *data, + struct cpool *cpool, + void *param, + int (*func)(struct Curl_easy *data, + struct connectdata *conn, void *param)) { struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; struct Curl_hash_element *he; - if(!connc) + if(!cpool) return FALSE; - CONNCACHE_LOCK(data); - Curl_hash_start_iterate(&connc->hash, &iter); + Curl_hash_start_iterate(&cpool->dest2bundle, &iter); he = Curl_hash_next_element(&iter); while(he) { - struct connectbundle *bundle; - - bundle = he->ptr; + struct Curl_llist_node *curr; + struct cpool_bundle *bundle = he->ptr; he = Curl_hash_next_element(&iter); - curr = bundle->conn_list.head; + curr = Curl_llist_head(&bundle->conns); while(curr) { /* Yes, we need to update curr before calling func(), because func() might decide to remove the connection */ - struct connectdata *conn = curr->ptr; - curr = curr->next; + struct connectdata *conn = Curl_node_elem(curr); + curr = Curl_node_next(curr); if(1 == func(data, conn, param)) { - CONNCACHE_UNLOCK(data); return TRUE; } } } - CONNCACHE_UNLOCK(data); return FALSE; } -/* Return the first connection found in the cache. Used when closing all - connections. - - NOTE: no locking is done here as this is presumably only done when cleaning - up a cache! -*/ -static struct connectdata * -conncache_find_first_connection(struct conncache *connc) +/* Return a live connection in the pool or NULL. */ +static struct connectdata *cpool_get_live_conn(struct cpool *cpool) { struct Curl_hash_iterator iter; struct Curl_hash_element *he; - struct connectbundle *bundle; - - Curl_hash_start_iterate(&connc->hash, &iter); + struct cpool_bundle *bundle; + struct Curl_llist_node *conn_node; - he = Curl_hash_next_element(&iter); - while(he) { - struct Curl_llist_element *curr; + Curl_hash_start_iterate(&cpool->dest2bundle, &iter); + for(he = Curl_hash_next_element(&iter); he; + he = Curl_hash_next_element(&iter)) { bundle = he->ptr; - - curr = bundle->conn_list.head; - if(curr) { - return curr->ptr; - } - - he = Curl_hash_next_element(&iter); + conn_node = Curl_llist_head(&bundle->conns); + if(conn_node) + return Curl_node_elem(conn_node); } - return NULL; } /* - * Give ownership of a connection back to the connection cache. Might - * disconnect the oldest existing in there to make space. + * A connection (already in the pool) has become idle. Do any + * cleanups in regard to the pool's limits. * - * Return TRUE if stored, FALSE if closed. + * Return TRUE if idle connection kept in pool, FALSE if closed. */ -bool Curl_conncache_return_conn(struct Curl_easy *data, - struct connectdata *conn) +bool Curl_cpool_conn_now_idle(struct Curl_easy *data, + struct connectdata *conn) { unsigned int maxconnects = !data->multi->maxconnects ? data->multi->num_easy * 4: data->multi->maxconnects; - struct connectdata *conn_candidate = NULL; + struct connectdata *oldest_idle = NULL; + struct cpool *cpool = cpool_get_instance(data); + bool kept = TRUE; conn->lastused = Curl_now(); /* it was used up until now */ - if(maxconnects && Curl_conncache_size(data) > maxconnects) { - infof(data, "Connection cache is full, closing the oldest one"); - - conn_candidate = Curl_conncache_extract_oldest(data); - if(conn_candidate) { - /* Use the closure handle for this disconnect so that anything that - happens during the disconnect is not stored and associated with the - 'data' handle which already just finished a transfer and it is - important that details from this (unrelated) disconnect does not - taint meta-data in the data handle. */ - struct conncache *connc = data->state.conn_cache; - Curl_disconnect(connc->closure_handle, conn_candidate, - /* dead_connection */ FALSE); + if(cpool && maxconnects) { + /* may be called form a callback already under lock */ + bool do_lock = !CPOOL_IS_LOCKED(cpool); + if(do_lock) + CPOOL_LOCK(cpool); + if(cpool->num_conn > maxconnects) { + infof(data, "Connection pool is full, closing the oldest one"); + + oldest_idle = cpool_get_oldest_idle(cpool); + kept = (oldest_idle != conn); + if(oldest_idle) { + Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE); + } } + if(do_lock) + CPOOL_UNLOCK(cpool); } - return (conn_candidate == conn) ? FALSE : TRUE; - + return kept; } /* * This function finds the connection in the connection bundle that has been * unused for the longest time. - * - * Does not lock the connection cache! - * - * Returns the pointer to the oldest idle connection, or NULL if none was - * found. */ -struct connectdata * -Curl_conncache_extract_bundle(struct Curl_easy *data, - struct connectbundle *bundle) +static struct connectdata * +cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle) { - struct Curl_llist_element *curr; + struct Curl_llist_node *curr; timediff_t highscore = -1; timediff_t score; struct curltime now; - struct connectdata *conn_candidate = NULL; + struct connectdata *oldest_idle = NULL; struct connectdata *conn; - (void)data; - now = Curl_now(); - - curr = bundle->conn_list.head; + curr = Curl_llist_head(&bundle->conns); while(curr) { - conn = curr->ptr; + conn = Curl_node_elem(curr); if(!CONN_INUSE(conn)) { /* Set higher score for the age passed since the connection was used */ @@ -437,141 +543,836 @@ Curl_conncache_extract_bundle(struct Curl_easy *data, if(score > highscore) { highscore = score; - conn_candidate = conn; + oldest_idle = conn; } } - curr = curr->next; - } - if(conn_candidate) { - /* remove it to prevent another thread from nicking it */ - bundle_remove_conn(bundle, conn_candidate); - data->state.conn_cache->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - data->state.conn_cache->num_conn)); + curr = Curl_node_next(curr); } - - return conn_candidate; + return oldest_idle; } -/* - * This function finds the connection in the connection cache that has been - * unused for the longest time and extracts that from the bundle. - * - * Returns the pointer to the connection, or NULL if none was found. - */ -struct connectdata * -Curl_conncache_extract_oldest(struct Curl_easy *data) +static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool) { - struct conncache *connc = data->state.conn_cache; struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; + struct Curl_llist_node *curr; struct Curl_hash_element *he; + struct connectdata *oldest_idle = NULL; + struct cpool_bundle *bundle; + struct curltime now; timediff_t highscore =- 1; timediff_t score; - struct curltime now; - struct connectdata *conn_candidate = NULL; - struct connectbundle *bundle; - struct connectbundle *bundle_candidate = NULL; now = Curl_now(); + Curl_hash_start_iterate(&cpool->dest2bundle, &iter); - CONNCACHE_LOCK(data); - Curl_hash_start_iterate(&connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { + for(he = Curl_hash_next_element(&iter); he; + he = Curl_hash_next_element(&iter)) { struct connectdata *conn; - bundle = he->ptr; - curr = bundle->conn_list.head; - while(curr) { - conn = curr->ptr; - - if(!CONN_INUSE(conn) && !conn->bits.close && - !conn->connect_only) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_timediff(now, conn->lastused); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - bundle_candidate = bundle; - } + for(curr = Curl_llist_head(&bundle->conns); curr; + curr = Curl_node_next(curr)) { + conn = Curl_node_elem(curr); + if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only) + continue; + /* Set higher score for the age passed since the connection was used */ + score = Curl_timediff(now, conn->lastused); + if(score > highscore) { + highscore = score; + oldest_idle = conn; } - curr = curr->next; } + } + return oldest_idle; +} - he = Curl_hash_next_element(&iter); +bool Curl_cpool_find(struct Curl_easy *data, + const char *destination, size_t dest_len, + Curl_cpool_conn_match_cb *conn_cb, + Curl_cpool_done_match_cb *done_cb, + void *userdata) +{ + struct cpool *cpool = cpool_get_instance(data); + struct cpool_bundle *bundle; + bool result = FALSE; + + DEBUGASSERT(cpool); + DEBUGASSERT(conn_cb); + if(!cpool) + return FALSE; + + CPOOL_LOCK(cpool); + bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len); + if(bundle) { + struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns); + while(curr) { + struct connectdata *conn = Curl_node_elem(curr); + /* Get next node now. callback might discard current */ + curr = Curl_node_next(curr); + + if(conn_cb(conn, userdata)) { + result = TRUE; + break; + } + } } - if(conn_candidate) { - /* remove it to prevent another thread from nicking it */ - bundle_remove_conn(bundle_candidate, conn_candidate); - connc->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - connc->num_conn)); + + if(done_cb) { + result = done_cb(result, userdata); } - CONNCACHE_UNLOCK(data); + CPOOL_UNLOCK(cpool); + return result; +} + +static void cpool_shutdown_discard_all(struct cpool *cpool) +{ + struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns); + struct connectdata *conn; - return conn_candidate; + if(!e) + return; + + DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all")); + while(e) { + conn = Curl_node_elem(e); + Curl_node_remove(e); + DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T, + conn->connection_id)); + cpool_close_and_destroy(cpool, conn, NULL, FALSE); + e = Curl_llist_head(&cpool->shutdowns); + } } -void Curl_conncache_close_all_connections(struct conncache *connc) +static void cpool_close_and_destroy_all(struct cpool *cpool) { struct connectdata *conn; + int timeout_ms = 0; SIGPIPE_VARIABLE(pipe_st); - if(!connc->closure_handle) - return; - conn = conncache_find_first_connection(connc); + DEBUGASSERT(cpool); + /* Move all connections to the shutdown list */ + sigpipe_init(&pipe_st); + CPOOL_LOCK(cpool); + conn = cpool_get_live_conn(cpool); while(conn) { - sigpipe_ignore(connc->closure_handle, &pipe_st); - /* This will remove the connection from the cache */ + cpool_remove_conn(cpool, conn); + sigpipe_apply(cpool->idata, &pipe_st); connclose(conn, "kill all"); - Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE); - Curl_disconnect(connc->closure_handle, conn, FALSE); - sigpipe_restore(&pipe_st); + cpool_discard_conn(cpool, cpool->idata, conn, FALSE); - conn = conncache_find_first_connection(connc); + conn = cpool_get_live_conn(cpool); } + CPOOL_UNLOCK(cpool); + + /* Just for testing, run graceful shutdown */ +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_GRACEFUL_SHUTDOWN"); + if(p) { + long l = strtol(p, NULL, 10); + if(l > 0 && l < INT_MAX) + timeout_ms = (int)l; + } + } +#endif + sigpipe_apply(cpool->idata, &pipe_st); + cpool_shutdown_all(cpool, cpool->idata, timeout_ms); - sigpipe_ignore(connc->closure_handle, &pipe_st); + /* discard all connections in the shutdown list */ + cpool_shutdown_discard_all(cpool); - Curl_hostcache_clean(connc->closure_handle, - connc->closure_handle->dns.hostcache); - Curl_close(&connc->closure_handle); + Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache); sigpipe_restore(&pipe_st); } + +static void cpool_shutdown_destroy_oldest(struct cpool *cpool) +{ + struct Curl_llist_node *e; + struct connectdata *conn; + + e = Curl_llist_head(&cpool->shutdowns); + if(e) { + SIGPIPE_VARIABLE(pipe_st); + conn = Curl_node_elem(e); + Curl_node_remove(e); + sigpipe_init(&pipe_st); + sigpipe_apply(cpool->idata, &pipe_st); + cpool_close_and_destroy(cpool, conn, NULL, FALSE); + sigpipe_restore(&pipe_st); + } +} + +static void cpool_discard_conn(struct cpool *cpool, + struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + bool done = FALSE; + + DEBUGASSERT(data); + DEBUGASSERT(cpool); + DEBUGASSERT(!conn->bits.in_cpool); + + /* + * If this connection is not marked to force-close, leave it open if there + * are other users of it + */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T + " still in use by %zu transfers", conn->connection_id, + CONN_INUSE(conn))); + return; + } + + /* treat the connection as aborted in CONNECT_ONLY situations, we do + * not know what the APP did with it. */ + if(conn->connect_only) + aborted = TRUE; + conn->bits.aborted = aborted; + + /* We do not shutdown dead connections. The term 'dead' can be misleading + * here, as we also mark errored connections/transfers as 'dead'. + * If we do a shutdown for an aborted transfer, the server might think + * it was successful otherwise (for example an ftps: upload). This is + * not what we want. */ + if(aborted) + done = TRUE; + if(!done) { + /* Attempt to shutdown the connection right away. */ + Curl_attach_connection(data, conn); + cpool_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d", + conn->connection_id, done)); + Curl_detach_connection(data); + } + + if(done) { + cpool_close_and_destroy(cpool, conn, data, FALSE); + return; + } + + /* Add the connection to our shutdown list for non-blocking shutdown + * during multi processing. */ + if(data->multi && data->multi->max_shutdown_connections > 0 && + (data->multi->max_shutdown_connections >= + (long)Curl_llist_count(&cpool->shutdowns))) { + DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection " + "due to limit of %ld", + data->multi->max_shutdown_connections)); + cpool_shutdown_destroy_oldest(cpool); + } + + if(data->multi && data->multi->socket_cb) { + DEBUGASSERT(cpool == &data->multi->cpool); + /* Start with an empty shutdown pollset, so out internal closure handle + * is added to the sockets. */ + memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll)); + if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) { + DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, " + "discarding #%" FMT_OFF_T, + conn->connection_id)); + cpool_close_and_destroy(cpool, conn, data, FALSE); + return; + } + } + + Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node); + DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T + " to shutdown list of length %zu", conn->connection_id, + Curl_llist_count(&cpool->shutdowns))); +} + +void Curl_cpool_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool aborted) +{ + struct cpool *cpool = cpool_get_instance(data); + bool do_lock; + + DEBUGASSERT(cpool); + DEBUGASSERT(data && !data->conn); + if(!cpool) + return; + + /* If this connection is not marked to force-close, leave it open if there + * are other users of it */ + if(CONN_INUSE(conn) && !aborted) { + DEBUGASSERT(0); /* does this ever happen? */ + DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); + return; + } + + /* This method may be called while we are under lock, e.g. from a + * user callback in find. */ + do_lock = !CPOOL_IS_LOCKED(cpool); + if(do_lock) + CPOOL_LOCK(cpool); + + if(conn->bits.in_cpool) { + cpool_remove_conn(cpool, conn); + DEBUGASSERT(!conn->bits.in_cpool); + } + + /* Run the callback to let it clean up anything it wants to. */ + aborted = cpool->disconnect_cb(data, conn, aborted); + + if(data->multi) { + /* Add it to the multi's cpool for shutdown handling */ + infof(data, "%s connection #%" FMT_OFF_T, + aborted? "closing" : "shutting down", conn->connection_id); + cpool_discard_conn(&data->multi->cpool, data, conn, aborted); + } + else { + /* No multi available. Make a best-effort shutdown + close */ + infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id); + cpool_close_and_destroy(NULL, conn, data, !aborted); + } + + if(do_lock) + CPOOL_UNLOCK(cpool); +} + +static void cpool_run_conn_shutdown_handler(struct Curl_easy *data, + struct connectdata *conn) +{ + if(!conn->bits.shutdown_handler) { + if(conn->dns_entry) + Curl_resolv_unlink(data, &conn->dns_entry); + + /* Cleanup NTLM connection-related data */ + Curl_http_auth_cleanup_ntlm(conn); + + /* Cleanup NEGOTIATE connection-related data */ + Curl_http_auth_cleanup_negotiate(conn); + + if(conn->handler && conn->handler->disconnect) { + /* This is set if protocol-specific cleanups should be made */ + DEBUGF(infof(data, "connection #%" FMT_OFF_T + ", shutdown protocol handler (aborted=%d)", + conn->connection_id, conn->bits.aborted)); + + conn->handler->disconnect(data, conn, conn->bits.aborted); + } + + /* possible left-overs from the async name resolvers */ + Curl_resolver_cancel(data); + + conn->bits.shutdown_handler = TRUE; + } +} + +static void cpool_run_conn_shutdown(struct Curl_easy *data, + struct connectdata *conn, + bool *done) +{ + CURLcode r1, r2; + bool done1, done2; + + /* We expect to be attached when called */ + DEBUGASSERT(data->conn == conn); + + cpool_run_conn_shutdown_handler(data, conn); + + if(conn->bits.shutdown_filters) { + *done = TRUE; + return; + } + + if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) + r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1); + else { + r1 = CURLE_OK; + done1 = TRUE; + } + + if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) + r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2); + else { + r2 = CURLE_OK; + done2 = TRUE; + } + + /* we are done when any failed or both report success */ + *done = (r1 || r2 || (done1 && done2)); + if(*done) + conn->bits.shutdown_filters = TRUE; +} + +static CURLcode cpool_add_pollfds(struct cpool *cpool, + struct curl_pollfds *cpfds) +{ + CURLcode result = CURLE_OK; + + if(Curl_llist_head(&cpool->shutdowns)) { + struct Curl_llist_node *e; + struct easy_pollset ps; + struct connectdata *conn; + + for(e = Curl_llist_head(&cpool->shutdowns); e; + e = Curl_node_next(e)) { + conn = Curl_node_elem(e); + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(cpool->idata, conn); + Curl_conn_adjust_pollset(cpool->idata, &ps); + Curl_detach_connection(cpool->idata); + + result = Curl_pollfds_add_ps(cpfds, &ps); + if(result) { + Curl_pollfds_cleanup(cpfds); + goto out; + } + } + } +out: + return result; +} + +CURLcode Curl_cpool_add_pollfds(struct cpool *cpool, + struct curl_pollfds *cpfds) +{ + CURLcode result; + CPOOL_LOCK(cpool); + result = cpool_add_pollfds(cpool, cpfds); + CPOOL_UNLOCK(cpool); + return result; +} + +CURLcode Curl_cpool_add_waitfds(struct cpool *cpool, + struct curl_waitfds *cwfds) +{ + CURLcode result = CURLE_OK; + + CPOOL_LOCK(cpool); + if(Curl_llist_head(&cpool->shutdowns)) { + struct Curl_llist_node *e; + struct easy_pollset ps; + struct connectdata *conn; + + for(e = Curl_llist_head(&cpool->shutdowns); e; + e = Curl_node_next(e)) { + conn = Curl_node_elem(e); + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(cpool->idata, conn); + Curl_conn_adjust_pollset(cpool->idata, &ps); + Curl_detach_connection(cpool->idata); + + result = Curl_waitfds_add_ps(cwfds, &ps); + if(result) + goto out; + } + } +out: + CPOOL_UNLOCK(cpool); + return result; +} + +static void cpool_perform(struct cpool *cpool) +{ + struct Curl_easy *data = cpool->idata; + struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns); + struct Curl_llist_node *enext; + struct connectdata *conn; + struct curltime *nowp = NULL; + struct curltime now; + timediff_t next_from_now_ms = 0, ms; + bool done; + + if(!e) + return; + + DEBUGASSERT(data); + DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown", + Curl_llist_count(&cpool->shutdowns))); + while(e) { + enext = Curl_node_next(e); + conn = Curl_node_elem(e); + Curl_attach_connection(data, conn); + cpool_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d", + conn->connection_id, done)); + Curl_detach_connection(data); + if(done) { + Curl_node_remove(e); + cpool_close_and_destroy(cpool, conn, NULL, FALSE); + } + else { + /* Not done, when does this connection time out? */ + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + ms = Curl_conn_shutdown_timeleft(conn, nowp); + if(ms && ms < next_from_now_ms) + next_from_now_ms = ms; + } + e = enext; + } + + if(next_from_now_ms) + Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW); +} + +void Curl_cpool_multi_perform(struct Curl_multi *multi) +{ + CPOOL_LOCK(&multi->cpool); + cpool_perform(&multi->cpool); + CPOOL_UNLOCK(&multi->cpool); +} + + +/* + * Close and destroy the connection. Run the shutdown sequence once, + * of so requested. + */ +static void cpool_close_and_destroy(struct cpool *cpool, + struct connectdata *conn, + struct Curl_easy *data, + bool do_shutdown) +{ + bool done; + + /* there must be a connection to close */ + DEBUGASSERT(conn); + /* it must be removed from the connection pool */ + DEBUGASSERT(!conn->bits.in_cpool); + /* there must be an associated transfer */ + DEBUGASSERT(data || cpool); + if(!data) + data = cpool->idata; + + /* the transfer must be detached from the connection */ + DEBUGASSERT(data && !data->conn); + + Curl_attach_connection(data, conn); + + cpool_run_conn_shutdown_handler(data, conn); + if(do_shutdown) { + /* Make a last attempt to shutdown handlers and filters, if + * not done so already. */ + cpool_run_conn_shutdown(data, conn, &done); + } + + if(cpool) + DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T, + conn->connection_id)); + else + DEBUGF(infof(data, "closing connection #%" FMT_OFF_T, + conn->connection_id)); + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_close(data, FIRSTSOCKET); + Curl_detach_connection(data); + + Curl_conn_free(data, conn); +} + + +static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct easy_pollset ps; + CURLMcode mresult; + + DEBUGASSERT(data); + DEBUGASSERT(multi); + DEBUGASSERT(multi->socket_cb); + + memset(&ps, 0, sizeof(ps)); + Curl_attach_connection(data, conn); + Curl_conn_adjust_pollset(data, &ps); + Curl_detach_connection(data); + + mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll); + + if(!mresult) /* Remember for next time */ + memcpy(&conn->shutdown_poll, &ps, sizeof(ps)); + return mresult; +} + +void Curl_cpool_multi_socket(struct Curl_multi *multi, + curl_socket_t s, int ev_bitmask) +{ + struct cpool *cpool = &multi->cpool; + struct Curl_easy *data = cpool->idata; + struct Curl_llist_node *e; + struct connectdata *conn; + bool done; + + (void)ev_bitmask; + DEBUGASSERT(multi->socket_cb); + CPOOL_LOCK(cpool); + e = Curl_llist_head(&cpool->shutdowns); + while(e) { + conn = Curl_node_elem(e); + if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) { + Curl_attach_connection(data, conn); + cpool_run_conn_shutdown(data, conn, &done); + DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d", + conn->connection_id, done)); + Curl_detach_connection(data); + if(done || cpool_update_shutdown_ev(multi, data, conn)) { + Curl_node_remove(e); + cpool_close_and_destroy(cpool, conn, NULL, FALSE); + } + break; + } + e = Curl_node_next(e); + } + CPOOL_UNLOCK(cpool); +} + +#define NUM_POLLS_ON_STACK 10 + +static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms) +{ + struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; + struct curl_pollfds cpfds; + CURLcode result; + + Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); + + result = cpool_add_pollfds(cpool, &cpfds); + if(result) + goto out; + + Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000)); + +out: + Curl_pollfds_cleanup(&cpfds); + return result; +} + +static void cpool_shutdown_all(struct cpool *cpool, + struct Curl_easy *data, int timeout_ms) +{ + struct connectdata *conn; + struct curltime started = Curl_now(); + + if(!data) + return; + (void)data; + + DEBUGF(infof(data, "cpool shutdown all")); + + /* Move all connections into the shutdown queue */ + for(conn = cpool_get_live_conn(cpool); conn; + conn = cpool_get_live_conn(cpool)) { + /* Move conn from live set to shutdown or destroy right away */ + DEBUGF(infof(data, "moving connection #%" FMT_OFF_T + " to shutdown queue", conn->connection_id)); + cpool_remove_conn(cpool, conn); + cpool_discard_conn(cpool, data, conn, FALSE); + } + + while(Curl_llist_head(&cpool->shutdowns)) { + timediff_t timespent; + int remain_ms; + + cpool_perform(cpool); + + if(!Curl_llist_head(&cpool->shutdowns)) { + DEBUGF(infof(data, "cpool shutdown ok")); + break; + } + + /* wait for activity, timeout or "nothing" */ + timespent = Curl_timediff(Curl_now(), started); + if(timespent >= (timediff_t)timeout_ms) { + DEBUGF(infof(data, "cpool shutdown %s", + (timeout_ms > 0)? "timeout" : "best effort done")); + break; + } + + remain_ms = timeout_ms - (int)timespent; + if(cpool_shutdown_wait(cpool, remain_ms)) { + DEBUGF(infof(data, "cpool shutdown all, abort")); + break; + } + } + + /* Due to errors/timeout, we might come here without being done. */ + cpool_shutdown_discard_all(cpool); +} + +struct cpool_reaper_ctx { + struct curltime now; +}; + +static int cpool_reap_dead_cb(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + struct cpool_reaper_ctx *rctx = param; + if(Curl_conn_seems_dead(conn, data, &rctx->now)) { + /* stop the iteration here, pass back the connection that was pruned */ + Curl_cpool_disconnect(data, conn, FALSE); + return 1; + } + return 0; /* continue iteration */ +} + +/* + * This function scans the data's connection pool for half-open/dead + * connections, closes and removes them. + * The cleanup is done at most once per second. + * + * When called, this transfer has no connection attached. + */ +void Curl_cpool_prune_dead(struct Curl_easy *data) +{ + struct cpool *cpool = cpool_get_instance(data); + struct cpool_reaper_ctx rctx; + timediff_t elapsed; + + if(!cpool) + return; + + rctx.now = Curl_now(); + CPOOL_LOCK(cpool); + elapsed = Curl_timediff(rctx.now, cpool->last_cleanup); + + if(elapsed >= 1000L) { + while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb)) + ; + cpool->last_cleanup = rctx.now; + } + CPOOL_UNLOCK(cpool); +} + +static int conn_upkeep(struct Curl_easy *data, + struct connectdata *conn, + void *param) +{ + struct curltime *now = param; + /* TODO, shall we reap connections that return an error here? */ + Curl_conn_upkeep(data, conn, now); + return 0; /* continue iteration */ +} + +CURLcode Curl_cpool_upkeep(void *data) +{ + struct cpool *cpool = cpool_get_instance(data); + struct curltime now = Curl_now(); + + if(!cpool) + return CURLE_OK; + + CPOOL_LOCK(cpool); + cpool_foreach(data, cpool, &now, conn_upkeep); + CPOOL_UNLOCK(cpool); + return CURLE_OK; +} + +struct cpool_find_ctx { + curl_off_t id; + struct connectdata *conn; +}; + +static int cpool_find_conn(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + struct cpool_find_ctx *fctx = param; + (void)data; + if(conn->connection_id == fctx->id) { + fctx->conn = conn; + return 1; + } + return 0; +} + +struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, + curl_off_t conn_id) +{ + struct cpool *cpool = cpool_get_instance(data); + struct cpool_find_ctx fctx; + + if(!cpool) + return NULL; + fctx.id = conn_id; + fctx.conn = NULL; + CPOOL_LOCK(cpool); + cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn); + CPOOL_UNLOCK(cpool); + return fctx.conn; +} + +struct cpool_do_conn_ctx { + curl_off_t id; + Curl_cpool_conn_do_cb *cb; + void *cbdata; +}; + +static int cpool_do_conn(struct Curl_easy *data, + struct connectdata *conn, void *param) +{ + struct cpool_do_conn_ctx *dctx = param; + (void)data; + if(conn->connection_id == dctx->id) { + dctx->cb(conn, data, dctx->cbdata); + return 1; + } + return 0; +} + +void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id, + Curl_cpool_conn_do_cb *cb, void *cbdata) +{ + struct cpool *cpool = cpool_get_instance(data); + struct cpool_do_conn_ctx dctx; + + if(!cpool) + return; + dctx.id = conn_id; + dctx.cb = cb; + dctx.cbdata = cbdata; + CPOOL_LOCK(cpool); + cpool_foreach(data, cpool, &dctx, cpool_do_conn); + CPOOL_UNLOCK(cpool); +} + +void Curl_cpool_do_locked(struct Curl_easy *data, + struct connectdata *conn, + Curl_cpool_conn_do_cb *cb, void *cbdata) +{ + struct cpool *cpool = cpool_get_instance(data); + if(cpool) { + CPOOL_LOCK(cpool); + cb(conn, data, cbdata); + CPOOL_UNLOCK(cpool); + } + else + cb(conn, data, cbdata); +} + #if 0 -/* Useful for debugging the connection cache */ -void Curl_conncache_print(struct conncache *connc) +/* Useful for debugging the connection pool */ +void Curl_cpool_print(struct cpool *cpool) { struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; + struct Curl_llist_node *curr; struct Curl_hash_element *he; - if(!connc) + if(!cpool) return; fprintf(stderr, "=Bundle cache=\n"); - Curl_hash_start_iterate(connc->hash, &iter); + Curl_hash_start_iterate(cpool->dest2bundle, &iter); he = Curl_hash_next_element(&iter); while(he) { - struct connectbundle *bundle; + struct cpool_bundle *bundle; struct connectdata *conn; bundle = he->ptr; fprintf(stderr, "%s -", he->key); - curr = bundle->conn_list->head; + curr = Curl_llist_head(bundle->conns); while(curr) { - conn = curr->ptr; + conn = Curl_node_elem(curr); - fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); - curr = curr->next; + fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount); + curr = Curl_node_next(curr); } fprintf(stderr, "\n"); diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index e68512394..a379ee747 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h @@ -25,98 +25,177 @@ * ***************************************************************************/ -/* - * All accesses to struct fields and changing of data in the connection cache - * and connectbundles must be done with the conncache LOCKED. The cache might - * be shared. - */ - #include #include "timeval.h" struct connectdata; +struct Curl_easy; +struct curl_pollfds; +struct curl_waitfds; +struct Curl_multi; +struct Curl_share; -struct conncache { - struct Curl_hash hash; +/** + * Callback invoked when disconnecting connections. + * @param data transfer last handling the connection, not attached + * @param conn the connection to discard + * @param aborted if the connection is being aborted + * @return if the connection is being aborted, e.g. should NOT perform + * a shutdown and just close. + **/ +typedef bool Curl_cpool_disconnect_cb(struct Curl_easy *data, + struct connectdata *conn, + bool aborted); + +struct cpool { + /* the pooled connections, bundled per destination */ + struct Curl_hash dest2bundle; size_t num_conn; curl_off_t next_connection_id; curl_off_t next_easy_id; struct curltime last_cleanup; - /* handle used for closing cached connections */ - struct Curl_easy *closure_handle; + struct Curl_llist shutdowns; /* The connections being shut down */ + struct Curl_easy *idata; /* internal handle used for discard */ + struct Curl_multi *multi; /* != NULL iff pool belongs to multi */ + struct Curl_share *share; /* != NULL iff pool belongs to share */ + Curl_cpool_disconnect_cb *disconnect_cb; + BIT(locked); }; -#define BUNDLE_NO_MULTIUSE -1 -#define BUNDLE_UNKNOWN 0 /* initial value */ -#define BUNDLE_MULTIPLEX 2 - -#ifdef CURLDEBUG -/* the debug versions of these macros make extra certain that the lock is - never doubly locked or unlocked */ -#define CONNCACHE_LOCK(x) \ - do { \ - if((x)->share) { \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, \ - CURL_LOCK_ACCESS_SINGLE); \ - DEBUGASSERT(!(x)->state.conncache_lock); \ - (x)->state.conncache_lock = TRUE; \ - } \ - } while(0) - -#define CONNCACHE_UNLOCK(x) \ - do { \ - if((x)->share) { \ - DEBUGASSERT((x)->state.conncache_lock); \ - (x)->state.conncache_lock = FALSE; \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ - } \ - } while(0) -#else -#define CONNCACHE_LOCK(x) if((x)->share) \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) -#define CONNCACHE_UNLOCK(x) if((x)->share) \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) -#endif - -struct connectbundle { - int multiuse; /* supports multi-use */ - size_t num_connections; /* Number of connections in the bundle */ - struct Curl_llist conn_list; /* The connectdata members of the bundle */ -}; +/* Init the pool, pass multi only if pool is owned by it. + * returns 1 on error, 0 is fine. + */ +int Curl_cpool_init(struct cpool *cpool, + Curl_cpool_disconnect_cb *disconnect_cb, + struct Curl_multi *multi, + struct Curl_share *share, + size_t size); + +/* Destroy all connections and free all members */ +void Curl_cpool_destroy(struct cpool *connc); + +/* Init the transfer to be used within its connection pool. + * Assigns `data->id`. */ +void Curl_cpool_xfer_init(struct Curl_easy *data); + +/** + * Get the connection with the given id from the transfer's pool. + */ +struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data, + curl_off_t conn_id); + +CURLcode Curl_cpool_add_conn(struct Curl_easy *data, + struct connectdata *conn) WARN_UNUSED_RESULT; + +/** + * Return if the pool has reached its configured limits for adding + * the given connection. Will try to discard the oldest, idle + * connections to make space. + */ +#define CPOOL_LIMIT_OK 0 +#define CPOOL_LIMIT_DEST 1 +#define CPOOL_LIMIT_TOTAL 2 +int Curl_cpool_check_limits(struct Curl_easy *data, + struct connectdata *conn); + +/* Return of conn is suitable. If so, stops iteration. */ +typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn, + void *userdata); + +/* Act on the result of the find, may override it. */ +typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); + +/** + * Find a connection in the pool matching `destination`. + * All callbacks are invoked while the pool's lock is held. + * @param data current transfer + * @param destination match agaonst `conn->destination` in pool + * @param dest_len destination length, including terminating NUL + * @param conn_cb must be present, called for each connection in the + * bundle until it returns TRUE + * @param result_cb if not NULL, is called at the end with the result + * of the `conn_cb` or FALSE if never called. + * @return combined result of last conn_db and result_cb or FALSE if no + connections were present. + */ +bool Curl_cpool_find(struct Curl_easy *data, + const char *destination, size_t dest_len, + Curl_cpool_conn_match_cb *conn_cb, + Curl_cpool_done_match_cb *done_cb, + void *userdata); + +/* + * A connection (already in the pool) is now idle. Do any + * cleanups in regard to the pool's limits. + * + * Return TRUE if idle connection kept in pool, FALSE if closed. + */ +bool Curl_cpool_conn_now_idle(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Remove the connection from the pool and tear it down. + * If `aborted` is FALSE, the connection will be shut down first + * before closing and destroying it. + * If the shutdown is not immediately complete, the connection + * will be placed into the pool's shutdown queue. + */ +void Curl_cpool_disconnect(struct Curl_easy *data, + struct connectdata *conn, + bool aborted); + +/** + * This function scans the data's connection pool for half-open/dead + * connections, closes and removes them. + * The cleanup is done at most once per second. + * + * When called, this transfer has no connection attached. + */ +void Curl_cpool_prune_dead(struct Curl_easy *data); + +/** + * Perform upkeep actions on connections in the transfer's pool. + */ +CURLcode Curl_cpool_upkeep(void *data); + +typedef void Curl_cpool_conn_do_cb(struct connectdata *conn, + struct Curl_easy *data, + void *cbdata); + +/** + * Invoke the callback on the pool's connection with the + * given connection id (if it exists). + */ +void Curl_cpool_do_by_id(struct Curl_easy *data, + curl_off_t conn_id, + Curl_cpool_conn_do_cb *cb, void *cbdata); + +/** + * Invoked the callback for the given data + connection under the + * connection pool's lock. + * The callback is always invoked, even if the transfer has no connection + * pool associated. + */ +void Curl_cpool_do_locked(struct Curl_easy *data, + struct connectdata *conn, + Curl_cpool_conn_do_cb *cb, void *cbdata); + +/** + * Add sockets and POLLIN/OUT flags for connections handled by the pool. + */ +CURLcode Curl_cpool_add_pollfds(struct cpool *connc, + struct curl_pollfds *cpfds); +CURLcode Curl_cpool_add_waitfds(struct cpool *connc, + struct curl_waitfds *cwfds); + +/** + * Perform maintenance on connections in the pool. Specifically, + * progress the shutdown of connections in the queue. + */ +void Curl_cpool_multi_perform(struct Curl_multi *multi); + +void Curl_cpool_multi_socket(struct Curl_multi *multi, + curl_socket_t s, int ev_bitmask); -/* returns 1 on error, 0 is fine */ -int Curl_conncache_init(struct conncache *, size_t size); -void Curl_conncache_destroy(struct conncache *connc); - -/* return the correct bundle, to a host or a proxy */ -struct connectbundle *Curl_conncache_find_bundle(struct Curl_easy *data, - struct connectdata *conn, - struct conncache *connc); -/* returns number of connections currently held in the connection cache */ -size_t Curl_conncache_size(struct Curl_easy *data); - -bool Curl_conncache_return_conn(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_conncache_add_conn(struct Curl_easy *data) WARN_UNUSED_RESULT; -void Curl_conncache_remove_conn(struct Curl_easy *data, - struct connectdata *conn, - bool lock); -bool Curl_conncache_foreach(struct Curl_easy *data, - struct conncache *connc, - void *param, - int (*func)(struct Curl_easy *data, - struct connectdata *conn, - void *param)); - -struct connectdata * -Curl_conncache_find_first_connection(struct conncache *connc); - -struct connectdata * -Curl_conncache_extract_bundle(struct Curl_easy *data, - struct connectbundle *bundle); -struct connectdata * -Curl_conncache_extract_oldest(struct Curl_easy *data); -void Curl_conncache_close_all_connections(struct conncache *connc); -void Curl_conncache_print(struct conncache *connc); #endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index bf85e640f..923f37ac3 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -90,7 +90,7 @@ /* * Curl_timeleft() returns the amount of milliseconds left allowed for the - * transfer/connection. If the value is 0, there's no timeout (ie there's + * transfer/connection. If the value is 0, there is no timeout (ie there is * infinite time left). If the value is negative, the timeout time has already * elapsed. * @param data the transfer to check on @@ -142,29 +142,70 @@ timediff_t Curl_timeleft(struct Curl_easy *data, return (ctimeleft_ms < timeleft_ms)? ctimeleft_ms : timeleft_ms; } -/* Copies connection info into the transfer handle to make it available when - the transfer handle is no longer associated with the connection. */ -void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - struct ip_quadruple *ip) +void Curl_shutdown_start(struct Curl_easy *data, int sockindex, + struct curltime *nowp) { - if(ip) - data->info.primary = *ip; - else { - memset(&data->info.primary, 0, sizeof(data->info.primary)); - data->info.primary.remote_port = -1; - data->info.primary.local_port = -1; + struct curltime now; + + DEBUGASSERT(data->conn); + if(!nowp) { + now = Curl_now(); + nowp = &now; } - data->info.conn_scheme = conn->handler->scheme; - /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; - data->info.conn_remote_port = conn->remote_port; - data->info.used_proxy = -#ifdef CURL_DISABLE_PROXY - 0 -#else - conn->bits.proxy -#endif - ; + data->conn->shutdown.start[sockindex] = *nowp; + data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ? + data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS; +} + +timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, + struct curltime *nowp) +{ + struct curltime now; + timediff_t left_ms; + + if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms) + return 0; /* not started or no limits */ + + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + left_ms = conn->shutdown.timeout_ms - + Curl_timediff(*nowp, conn->shutdown.start[sockindex]); + return left_ms? left_ms : -1; +} + +timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, + struct curltime *nowp) +{ + timediff_t left_ms = 0, ms; + struct curltime now; + int i; + + for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) { + if(!conn->shutdown.start[i].tv_sec) + continue; + if(!nowp) { + now = Curl_now(); + nowp = &now; + } + ms = Curl_shutdown_timeleft(conn, i, nowp); + if(ms && (!left_ms || ms < left_ms)) + left_ms = ms; + } + return left_ms; +} + +void Curl_shutdown_clear(struct Curl_easy *data, int sockindex) +{ + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + memset(pt, 0, sizeof(*pt)); +} + +bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) +{ + struct curltime *pt = &data->conn->shutdown.start[sockindex]; + return (pt->tv_sec > 0) || (pt->tv_usec > 0); } static const struct Curl_addrinfo * @@ -246,23 +287,6 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, return FALSE; } -struct connfind { - curl_off_t id_tofind; - struct connectdata *found; -}; - -static int conn_is_conn(struct Curl_easy *data, - struct connectdata *conn, void *param) -{ - struct connfind *f = (struct connfind *)param; - (void)data; - if(conn->connection_id == f->id_tofind) { - f->found = conn; - return 1; - } - return 0; -} - /* * Used to extract socket and connectdata struct for the most recent * transfer on the given Curl_easy. @@ -279,30 +303,19 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, * - that is associated with a multi handle, and whose connection * was detached with CURLOPT_CONNECT_ONLY */ - if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { - struct connectdata *c; - struct connfind find; - find.id_tofind = data->state.lastconnect_id; - find.found = NULL; - - Curl_conncache_foreach(data, - data->share && (data->share->specifier - & (1<< CURL_LOCK_DATA_CONNECT))? - &data->share->conn_cache: - data->multi_easy? - &data->multi_easy->conn_cache: - &data->multi->conn_cache, &find, conn_is_conn); - - if(!find.found) { + if(data->state.lastconnect_id != -1) { + struct connectdata *conn; + + conn = Curl_cpool_get_conn(data, data->state.lastconnect_id); + if(!conn) { data->state.lastconnect_id = -1; return CURL_SOCKET_BAD; } - c = find.found; if(connp) /* only store this if the caller cares for it */ - *connp = c; - return c->sock[FIRSTSOCKET]; + *connp = conn; + return conn->sock[FIRSTSOCKET]; } return CURL_SOCKET_BAD; } @@ -317,7 +330,7 @@ void Curl_conncontrol(struct connectdata *conn, #endif ) { - /* close if a connection, or a stream that isn't multiplexed. */ + /* close if a connection, or a stream that is not multiplexed. */ /* This function will be called both before and after this connection is associated with a transfer. */ bool closeit, is_multiplex; @@ -358,6 +371,7 @@ struct eyeballer { BIT(has_started); /* attempts have started */ BIT(is_done); /* out of addresses/time */ BIT(connected); /* cf has connected */ + BIT(shutdown); /* cf has shutdown */ BIT(inconclusive); /* connect was not a hard failure, we * might talk to a restarting server */ }; @@ -464,7 +478,7 @@ static void baller_initiate(struct Curl_cfilter *cf, CURLcode result; - /* Don't close a previous cfilter yet to ensure that the next IP's + /* Do not close a previous cfilter yet to ensure that the next IP's socket gets a different file descriptor, which can prevent bugs when the curl_multi_socket_action interface is used with certain select() replacements such as kqueue. */ @@ -533,9 +547,11 @@ static CURLcode baller_start_next(struct Curl_cfilter *cf, { if(cf->sockindex == FIRSTSOCKET) { baller_next_addr(baller); - /* If we get inconclusive answers from the server(s), we make - * a second iteration over the address list */ - if(!baller->addr && baller->inconclusive && !baller->rewinded) + /* If we get inconclusive answers from the server(s), we start + * again until this whole thing times out. This allows us to + * connect to servers that are gracefully restarting and the + * packet routing to the new instance has not happened yet (e.g. QUIC). */ + if(!baller->addr && baller->inconclusive) baller_rewind(baller); baller_start(cf, data, baller, timeoutms); } @@ -567,7 +583,7 @@ static CURLcode baller_connect(struct Curl_cfilter *cf, baller->is_done = TRUE; } else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) { - infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T + infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T "ms, move on!", baller->name, baller->timeoutms); #if defined(ETIMEDOUT) baller->error = ETIMEDOUT; @@ -658,7 +674,7 @@ evaluate: /* Nothing connected, check the time before we might * start new ballers or return ok. */ if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) { - failf(data, "Connection timeout after %" CURL_FORMAT_CURL_OFF_T " ms", + failf(data, "Connection timeout after %" FMT_OFF_T " ms", Curl_timediff(now, data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } @@ -681,8 +697,7 @@ evaluate: CURL_TRC_CF(data, cf, "%s done", baller->name); } else { - CURL_TRC_CF(data, cf, "%s starting (timeout=%" - CURL_FORMAT_TIMEDIFF_T "ms)", + CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)", baller->name, baller->timeoutms); ++ongoing; ++added; @@ -727,7 +742,7 @@ evaluate: hostname = conn->host.name; failf(data, "Failed to connect to %s port %u after " - "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", + "%" FMT_TIMEDIFF_T " ms: %s", hostname, conn->primary.remote_port, Curl_timediff(now, data->progress.t_startsingle), curl_easy_strerror(result)); @@ -744,7 +759,7 @@ evaluate: } /* - * Connect to the given host with timeout, proxy or remote doesn't matter. + * Connect to the given host with timeout, proxy or remote does not matter. * There might be more than one IP address to try out. */ static CURLcode start_connect(struct Curl_cfilter *cf, @@ -754,9 +769,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf, struct cf_he_ctx *ctx = cf->ctx; struct connectdata *conn = cf->conn; CURLcode result = CURLE_COULDNT_CONNECT; - int ai_family0, ai_family1; + int ai_family0 = 0, ai_family1 = 0; timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - const struct Curl_addrinfo *addr0, *addr1; + const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL; if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ @@ -775,33 +790,33 @@ static CURLcode start_connect(struct Curl_cfilter *cf, * the 2 connect attempt ballers to try different families, if possible. * */ - if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { - /* any IP version is allowed */ - ai_family0 = remotehost->addr? - remotehost->addr->ai_family : 0; + if(conn->ip_version == CURL_IPRESOLVE_V6) { #ifdef USE_IPV6 - ai_family1 = ai_family0 == AF_INET6 ? - AF_INET : AF_INET6; -#else - ai_family1 = AF_UNSPEC; + ai_family0 = AF_INET6; + addr0 = addr_first_match(remotehost->addr, ai_family0); #endif } + else if(conn->ip_version == CURL_IPRESOLVE_V4) { + ai_family0 = AF_INET; + addr0 = addr_first_match(remotehost->addr, ai_family0); + } else { - /* only one IP version is allowed */ - ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? - AF_INET : + /* no user preference, we try ipv6 always first when available */ #ifdef USE_IPV6 - AF_INET6; -#else - AF_UNSPEC; + ai_family0 = AF_INET6; + addr0 = addr_first_match(remotehost->addr, ai_family0); #endif - ai_family1 = AF_UNSPEC; + /* next candidate is ipv4 */ + ai_family1 = AF_INET; + addr1 = addr_first_match(remotehost->addr, ai_family1); + /* no ip address families, probably AF_UNIX or something, use the + * address family given to us */ + if(!addr1 && !addr0 && remotehost->addr) { + ai_family0 = remotehost->addr->ai_family; + addr0 = addr_first_match(remotehost->addr, ai_family0); + } } - /* Get the first address in the list that matches the family, - * this might give NULL, if we do not have any matches. */ - addr0 = addr_first_match(remotehost->addr, ai_family0); - addr1 = addr_first_match(remotehost->addr, ai_family1); if(!addr0 && addr1) { /* switch around, so a single baller always uses addr0 */ addr0 = addr1; @@ -820,8 +835,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME); if(result) return result; - CURL_TRC_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", + CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)", ctx->baller[0]->name, ctx->baller[0]->timeoutms); if(addr1) { /* second one gets a delayed start */ @@ -832,8 +846,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME2); if(result) return result; - CURL_TRC_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", + CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)", ctx->baller[1]->name, ctx->baller[1]->timeoutms); Curl_expire(data, data->set.happy_eyeballs_timeout, EXPIRE_HAPPY_EYEBALLS); @@ -857,6 +870,46 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) ctx->winner = NULL; } +static CURLcode cf_he_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_he_ctx *ctx = cf->ctx; + size_t i; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* shutdown all ballers that have not done so already. If one fails, + * continue shutting down others until all are shutdown. */ + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + struct eyeballer *baller = ctx->baller[i]; + bool bdone = FALSE; + if(!baller || !baller->cf || baller->shutdown) + continue; + baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone); + if(baller->result || bdone) + baller->shutdown = TRUE; /* treat a failed shutdown as done */ + } + + *done = TRUE; + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + if(ctx->baller[i] && !ctx->baller[i]->shutdown) + *done = FALSE; + } + if(*done) { + for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { + if(ctx->baller[i] && ctx->baller[i]->result) + result = ctx->baller[i]->result; + } + } + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + return result; +} + static void cf_he_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -913,12 +966,20 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf, cf->next = ctx->winner->cf; ctx->winner->cf = NULL; cf_he_ctx_clear(cf, data); - Curl_conn_cf_cntrl(cf->next, data, TRUE, - CF_CTRL_CONN_INFO_UPDATE, 0, NULL); if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - Curl_verboseconnect(data, cf->conn, cf->sockindex); + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ + if(Curl_trc_cf_is_verbose(cf, data)) { + struct ip_quadruple ipquad; + int is_ipv6; + if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { + const char *host, *disphost; + int port; + cf->next->cft->get_host(cf->next, data, &host, &disphost, &port); + CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", + disphost, ipquad.remote_ip, ipquad.remote_port); + } + } data->info.numconnects++; /* to track the # of connections made */ } break; @@ -1052,6 +1113,7 @@ struct Curl_cftype Curl_cft_happy_eyeballs = { cf_he_destroy, cf_he_connect, cf_he_close, + cf_he_shutdown, Curl_cf_def_get_host, cf_he_adjust_pollset, cf_he_data_pending, @@ -1112,7 +1174,7 @@ struct transport_provider { }; static -#ifndef DEBUGBUILD +#ifndef UNITTESTS const #endif struct transport_provider transport_providers[] = { @@ -1316,6 +1378,7 @@ struct Curl_cftype Curl_cft_setup = { cf_setup_destroy, cf_setup_connect, cf_setup_close, + Curl_cf_def_shutdown, Curl_cf_def_get_host, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, @@ -1378,7 +1441,7 @@ out: return result; } -#ifdef DEBUGBUILD +#ifdef UNITTESTS /* used by unit2600.c */ void Curl_debug_set_transport_provider(int transport, cf_ip_connect_create *cf_create) @@ -1391,7 +1454,7 @@ void Curl_debug_set_transport_provider(int transport, } } } -#endif /* DEBUGBUILD */ +#endif /* UNITTESTS */ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 00efe6f34..160db9420 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h @@ -32,7 +32,7 @@ struct Curl_dns_entry; struct ip_quadruple; -/* generic function that returns how much time there's left to run, according +/* generic function that returns how much time there is left to run, according to the timeouts set */ timediff_t Curl_timeleft(struct Curl_easy *data, struct curltime *nowp, @@ -40,6 +40,26 @@ timediff_t Curl_timeleft(struct Curl_easy *data, #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ +#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000) + +void Curl_shutdown_start(struct Curl_easy *data, int sockindex, + struct curltime *nowp); + +/* return how much time there is left to shutdown the connection at + * sockindex. Returns 0 if there is no limit or shutdown has not started. */ +timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, + struct curltime *nowp); + +/* return how much time there is left to shutdown the connection. + * Returns 0 if there is no limit or shutdown has not started. */ +timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, + struct curltime *nowp); + +void Curl_shutdown_clear(struct Curl_easy *data, int sockindex); + +/* TRUE iff shutdown has been started */ +bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); + /* * Used to extract socket and connectdata struct for the most recent * transfer on the given Curl_easy. @@ -52,9 +72,6 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, char *addr, int *port); -void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - struct ip_quadruple *ip); - /* * Curl_conncontrol() marks the end of a connection/stream. The 'closeit' * argument specifies if it is the end of a connection or a stream. @@ -125,7 +142,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, extern struct Curl_cftype Curl_cft_happy_eyeballs; extern struct Curl_cftype Curl_cft_setup; -#ifdef DEBUGBUILD +#ifdef UNITTESTS void Curl_debug_set_transport_provider(int transport, cf_ip_connect_create *cf_create); #endif diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index 50338c7e0..9552bb0c7 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c @@ -79,10 +79,10 @@ #define GZIP_MAGIC_1 0x8b /* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define ORIG_NAME 0x08 /* bit 3 set: original filename present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ @@ -192,7 +192,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, zp->zlib_init != ZLIB_GZIP_INFLATING) return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - /* Dynamically allocate a buffer for decompression because it's uncommonly + /* Dynamically allocate a buffer for decompression because it is uncommonly large to hold on the stack */ decomp = malloc(DSIZ); if(!decomp) @@ -246,7 +246,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, to fix and continue anyway */ if(zp->zlib_init == ZLIB_INIT) { /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ - (void) inflateEnd(z); /* don't care about the return code */ + (void) inflateEnd(z); /* do not care about the return code */ if(inflateInit2(z, -MAX_WBITS) == Z_OK) { z->next_in = orig_in; z->avail_in = nread; @@ -266,7 +266,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, } free(decomp); - /* We're about to leave this call so the `nread' data bytes won't be seen + /* We are about to leave this call so the `nread' data bytes will not be seen again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) @@ -388,7 +388,7 @@ static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, flags = data[3]; if(method != Z_DEFLATED || (flags & RESERVED) != 0) { - /* Can't handle this compression method or unknown flag */ + /* cannot handle this compression method or unknown flag */ return GZIP_BAD; } @@ -412,7 +412,7 @@ static gzip_status check_gzip_header(unsigned char const *data, ssize_t len, } if(flags & ORIG_NAME) { - /* Skip over NUL-terminated file name */ + /* Skip over NUL-terminated filename */ while(len && *data) { --len; ++data; @@ -474,10 +474,10 @@ static CURLcode gzip_do_write(struct Curl_easy *data, return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); #else - /* This next mess is to get around the potential case where there isn't - * enough data passed in to skip over the gzip header. If that happens, we - * malloc a block and copy what we have then wait for the next call. If - * there still isn't enough (this is definitely a worst-case scenario), we + /* This next mess is to get around the potential case where there is not + * enough data passed in to skip over the gzip header. If that happens, we + * malloc a block and copy what we have then wait for the next call. If + * there still is not enough (this is definitely a worst-case scenario), we * make the block bigger, copy the next part in and keep waiting. * * This is only required with zlib versions < 1.2.0.4 as newer versions @@ -499,11 +499,11 @@ static CURLcode gzip_do_write(struct Curl_easy *data, break; case GZIP_UNDERFLOW: - /* We need more data so we can find the end of the gzip header. It's + /* We need more data so we can find the end of the gzip header. it is * possible that the memory block we malloc here will never be freed if - * the transfer abruptly aborts after this point. Since it's unlikely + * the transfer abruptly aborts after this point. Since it is unlikely * that circumstances will be right for this code path to be followed in - * the first place, and it's even more unlikely for a transfer to fail + * the first place, and it is even more unlikely for a transfer to fail * immediately afterwards, it should seldom be a problem. */ z->avail_in = (uInt) nbytes; @@ -513,7 +513,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, } memcpy(z->next_in, buf, z->avail_in); zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ - /* We don't have any data to inflate yet */ + /* We do not have any data to inflate yet */ return CURLE_OK; case GZIP_BAD: @@ -536,18 +536,18 @@ static CURLcode gzip_do_write(struct Curl_easy *data, /* Append the new block of data to the previous one */ memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); - switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { + switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) { case GZIP_OK: /* This is the zlib stream data */ free(z->next_in); - /* Don't point into the malloced block since we just freed it */ + /* Do not point into the malloced block since we just freed it */ z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; - z->avail_in = (uInt) (z->avail_in - hlen); + z->avail_in = z->avail_in - (uInt)hlen; zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ break; case GZIP_UNDERFLOW: - /* We still don't have any data to inflate! */ + /* We still do not have any data to inflate! */ return CURLE_OK; case GZIP_BAD: @@ -572,11 +572,11 @@ static CURLcode gzip_do_write(struct Curl_easy *data, } if(z->avail_in == 0) { - /* We don't have any data to inflate; wait until next time */ + /* We do not have any data to inflate; wait until next time */ return CURLE_OK; } - /* We've parsed the header, now uncompress the data */ + /* We have parsed the header, now uncompress the data */ return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING); #endif } @@ -909,18 +909,18 @@ static CURLcode error_do_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - char all[256]; - (void)Curl_all_content_encodings(all, sizeof(all)); - (void) writer; (void) buf; (void) nbytes; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); - - failf(data, "Unrecognized content encoding type. " - "libcurl understands %s content encodings.", all); + else { + char all[256]; + (void)Curl_all_content_encodings(all, sizeof(all)); + failf(data, "Unrecognized content encoding type. " + "libcurl understands %s content encodings.", all); + } return CURLE_BAD_CONTENT_ENCODING; } @@ -966,7 +966,7 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name, return NULL; } -/* Set-up the unencoding stack from the Content-Encoding header value. +/* Setup 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 is_transfer) @@ -994,6 +994,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const struct Curl_cwtype *cwt; struct Curl_cwriter *writer; + CURL_TRC_WRITE(data, "looking for %s decoder: %.*s", + is_transfer? "transfer" : "content", (int)namelen, name); is_chunked = (is_transfer && (namelen == 7) && strncasecompare(name, "chunked", 7)); /* if we skip the decoding in this phase, do not look further. @@ -1001,6 +1003,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) || (!is_transfer && data->set.http_ce_skip)) { /* not requested, ignore */ + CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s", + (int)namelen, name); return CURLE_OK; } @@ -1018,6 +1022,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, * "A sender MUST NOT apply the chunked transfer coding more than * once to a message body." */ + CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); return CURLE_OK; } @@ -1040,6 +1045,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, cwt = &error_writer; /* Defer error at use. */ result = Curl_cwriter_create(&writer, data, cwt, phase); + CURL_TRC_WRITE(data, "added %s decoder %s -> %d", + is_transfer? "transfer" : "content", cwt->name, result); if(result) return result; diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index 837caaab3..95ca4a100 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c @@ -61,7 +61,7 @@ struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, boolean informs the cookie if a secure connection is achieved or not. - It shall only return cookies that haven't expired. + It shall only return cookies that have not expired. Example set of cookies: @@ -150,7 +150,7 @@ static bool cookie_tailmatch(const char *cookie_domain, } /* - * matching cookie path and url path + * matching cookie path and URL path * RFC6265 5.1.4 Paths and Path-Match */ static bool pathmatch(const char *cookie_path, const char *request_uri) @@ -262,8 +262,9 @@ static size_t cookie_hash_domain(const char *domain, const size_t len) size_t h = 5381; while(domain < end) { + size_t j = (size_t)Curl_raw_toupper(*domain++); h += h << 5; - h ^= Curl_raw_toupper(*domain++); + h ^= j; } return (h % COOKIE_HASH_SIZE); @@ -373,7 +374,7 @@ static void strstore(char **str, const char *newstr, size_t len) * * Remove expired cookies from the hash by inspecting the expires timestamp on * each cookie in the hash, freeing and deleting any where the timestamp is in - * the past. If the cookiejar has recorded the next timestamp at which one or + * the past. If the cookiejar has recorded the next timestamp at which one or * more cookies expire, then processing will exit early in case this timestamp * is in the future. */ @@ -385,11 +386,11 @@ static void remove_expired(struct CookieInfo *cookies) /* * If the earliest expiration timestamp in the jar is in the future we can - * skip scanning the whole jar and instead exit early as there won't be any - * cookies to evict. If we need to evict however, reset the next_expiration - * counter in order to track the next one. In case the recorded first - * expiration is the max offset, then perform the safe fallback of checking - * all cookies. + * skip scanning the whole jar and instead exit early as there will not be + * any cookies to evict. If we need to evict however, reset the + * next_expiration counter in order to track the next one. In case the + * recorded first expiration is the max offset, then perform the safe + * fallback of checking all cookies. */ if(now < cookies->next_expiration && cookies->next_expiration != CURL_OFF_T_MAX) @@ -414,7 +415,7 @@ static void remove_expired(struct CookieInfo *cookies) } else { /* - * If this cookie has an expiration timestamp earlier than what we've + * If this cookie has an expiration timestamp earlier than what we have * seen so far then record it for the next round of expirations. */ if(co->expires && co->expires < cookies->next_expiration) @@ -473,7 +474,7 @@ static int invalid_octets(const char *p) * Curl_cookie_add * * Add a single cookie line to the cookie keeping object. Be aware that - * sometimes we get an IP-only host name, and that might also be a numerical + * sometimes we get an IP-only hostname, and that might also be a numerical * IPv6 address. * * Returns NULL on out of memory or invalid cookie. This is suboptimal, @@ -509,7 +510,7 @@ Curl_cookie_add(struct Curl_easy *data, /* First, alloc and init a new struct for it */ co = calloc(1, sizeof(struct Cookie)); if(!co) - return NULL; /* bail out if we're this low on memory */ + return NULL; /* bail out if we are this low on memory */ if(httpheader) { /* This line was read off an HTTP-header */ @@ -647,7 +648,7 @@ Curl_cookie_add(struct Curl_easy *data, else if((nlen == 8) && strncasecompare("httponly", namep, 8)) co->httponly = TRUE; else if(sep) - /* there was a '=' so we're not done parsing this field */ + /* there was a '=' so we are not done parsing this field */ done = FALSE; } if(done) @@ -681,9 +682,9 @@ Curl_cookie_add(struct Curl_easy *data, #ifndef USE_LIBPSL /* - * Without PSL we don't know when the incoming cookie is set on a + * Without PSL we do not know when the incoming cookie is set on a * TLD or otherwise "protected" suffix. To reduce risk, we require a - * dot OR the exact host name being "localhost". + * dot OR the exact hostname being "localhost". */ if(bad_domain(valuep, vlen)) domain = ":"; @@ -721,10 +722,10 @@ Curl_cookie_add(struct Curl_easy *data, /* * Defined in RFC2109: * - * Optional. The Max-Age attribute defines the lifetime of the - * cookie, in seconds. The delta-seconds value is a decimal non- - * negative integer. After delta-seconds seconds elapse, the - * client should discard the cookie. A value of zero means the + * Optional. The Max-Age attribute defines the lifetime of the + * cookie, in seconds. The delta-seconds value is a decimal non- + * negative integer. After delta-seconds seconds elapse, the + * client should discard the cookie. A value of zero means the * cookie should be discarded immediately. */ CURLofft offt; @@ -780,7 +781,7 @@ Curl_cookie_add(struct Curl_easy *data, } /* - * Else, this is the second (or more) name we don't know about! + * Else, this is the second (or more) name we do not know about! */ } else { @@ -806,7 +807,7 @@ Curl_cookie_add(struct Curl_easy *data, if(!badcookie && !co->path && path) { /* - * No path was given in the header line, set the default. Note that the + * No path was given in the header line, set the default. Note that the * passed-in path to this function MAY have a '?' and following part that * MUST NOT be stored as part of the path. */ @@ -835,7 +836,7 @@ Curl_cookie_add(struct Curl_easy *data, } /* - * If we didn't get a cookie name, or a bad one, the this is an illegal + * If we did not get a cookie name, or a bad one, the this is an illegal * line so bail out. */ if(badcookie || !co->name) { @@ -868,7 +869,7 @@ Curl_cookie_add(struct Curl_easy *data, } if(lineptr[0]=='#') { - /* don't even try the comments */ + /* do not even try the comments */ free(co); return NULL; } @@ -908,7 +909,7 @@ Curl_cookie_add(struct Curl_easy *data, case 2: /* The file format allows the path field to remain not filled in */ if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { - /* only if the path doesn't look like a boolean option! */ + /* only if the path does not look like a boolean option! */ co->path = strdup(ptr); if(!co->path) badcookie = TRUE; @@ -920,7 +921,7 @@ Curl_cookie_add(struct Curl_easy *data, } break; } - /* this doesn't look like a path, make one up! */ + /* this does not look like a path, make one up! */ co->path = strdup("/"); if(!co->path) badcookie = TRUE; @@ -1003,7 +1004,7 @@ Curl_cookie_add(struct Curl_easy *data, if(!c->running && /* read from a file */ c->newsession && /* clean session cookies */ - !co->expires) { /* this is a session cookie since it doesn't expire! */ + !co->expires) { /* this is a session cookie since it does not expire! */ freecookie(co); return NULL; } @@ -1024,9 +1025,11 @@ Curl_cookie_add(struct Curl_easy *data, #ifdef USE_LIBPSL /* * Check if the domain is a Public Suffix and if yes, ignore the cookie. We - * must also check that the data handle isn't NULL since the psl code will + * must also check that the data handle is not NULL since the psl code will * dereference it. */ + DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s", + co->name, co->domain, domain)); if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { bool acceptable = FALSE; char lcase[256]; @@ -1053,6 +1056,9 @@ Curl_cookie_add(struct Curl_easy *data, return NULL; } } +#else + DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s", + co->name, co->domain, domain)); #endif /* A non-secure cookie may not overlay an existing secure cookie. */ @@ -1124,10 +1130,10 @@ Curl_cookie_add(struct Curl_easy *data, if(replace_old && !co->livecookie && clist->livecookie) { /* - * Both cookies matched fine, except that the already present cookie is - * "live", which means it was set from a header, while the new one was - * read from a file and thus isn't "live". "live" cookies are preferred - * so the new cookie is freed. + * Both cookies matched fine, except that the already present cookie + * is "live", which means it was set from a header, while the new one + * was read from a file and thus is not "live". "live" cookies are + * preferred so the new cookie is freed. */ freecookie(co); return NULL; @@ -1164,7 +1170,7 @@ Curl_cookie_add(struct Curl_easy *data, if(c->running) /* Only show this when NOT reading the cookies from a file */ infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " - "expire %" CURL_FORMAT_CURL_OFF_T, + "expire %" FMT_OFF_T, replace_old?"Replaced":"Added", co->name, co->value, co->domain, co->path, co->expires); @@ -1178,7 +1184,7 @@ Curl_cookie_add(struct Curl_easy *data, } /* - * Now that we've added a new cookie to the jar, update the expiration + * Now that we have added a new cookie to the jar, update the expiration * tracker in case it is the next one to expire. */ if(co->expires && (co->expires < c->next_expiration)) @@ -1211,12 +1217,12 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, FILE *handle = NULL; if(!inc) { - /* we didn't get a struct, create one */ + /* we did not get a struct, create one */ c = calloc(1, sizeof(struct CookieInfo)); if(!c) return NULL; /* failed to get memory */ /* - * Initialize the next_expiration time to signal that we don't have enough + * Initialize the next_expiration time to signal that we do not have enough * information yet. */ c->next_expiration = CURL_OFF_T_MAX; @@ -1271,7 +1277,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, } data->state.cookie_engine = TRUE; } - c->running = TRUE; /* now, we're running */ + c->running = TRUE; /* now, we are running */ return c; } @@ -1367,7 +1373,7 @@ fail: * should send to the server if used now. The secure boolean informs the cookie * if a secure connection is achieved or not. * - * It shall only return cookies that haven't expired. + * It shall only return cookies that have not expired. */ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, struct CookieInfo *c, @@ -1393,7 +1399,7 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, co = c->cookies[myhash]; while(co) { - /* if the cookie requires we're secure we must only continue if we are! */ + /* if the cookie requires we are secure we must only continue if we are! */ if(co->secure?secure:TRUE) { /* now check if the domain is correct */ @@ -1583,7 +1589,7 @@ static char *get_netscape_format(const struct Cookie *co) "%s\t" /* tailmatch */ "%s\t" /* path */ "%s\t" /* secure */ - "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ + "%" FMT_OFF_T "\t" /* expires */ "%s\t" /* name */ "%s", /* value */ co->httponly?"#HttpOnly_":"", @@ -1605,7 +1611,7 @@ static char *get_netscape_format(const struct Cookie *co) * cookie_output() * * Writes all internally known cookies to the specified file. Specify - * "-" as file name to write to stdout. + * "-" as filename to write to stdout. * * The function returns non-zero on write failure. */ diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h index 012dd892c..838d74d82 100644 --- a/Utilities/cmcurl/lib/cookie.h +++ b/Utilities/cmcurl/lib/cookie.h @@ -75,7 +75,7 @@ struct CookieInfo { /** Limits for INCOMING cookies **/ -/* The longest we allow a line to be when reading a cookie from a HTTP header +/* The longest we allow a line to be when reading a cookie from an HTTP header or from a cookie jar */ #define MAX_COOKIE_LINE 5000 diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index c32f24d01..44e10e9c9 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c @@ -95,7 +95,7 @@ Curl_freeaddrinfo(struct Curl_addrinfo *cahead) * the only difference that instead of returning a linked list of * addrinfo structs this one returns a linked list of Curl_addrinfo * ones. The memory allocated by this function *MUST* be free'd with - * Curl_freeaddrinfo(). For each successful call to this function + * Curl_freeaddrinfo(). For each successful call to this function * there must be an associated call later to Curl_freeaddrinfo(). * * There should be no single call to system's getaddrinfo() in the @@ -221,7 +221,7 @@ Curl_getaddrinfo_ex(const char *nodename, * stack, but usable also for IPv4, all hosts and environments. * * The memory allocated by this function *MUST* be free'd later on calling - * Curl_freeaddrinfo(). For each successful call to this function there + * Curl_freeaddrinfo(). For each successful call to this function there * must be an associated call later to Curl_freeaddrinfo(). * * Curl_addrinfo defined in "lib/curl_addrinfo.h" @@ -317,7 +317,11 @@ Curl_he2ai(const struct hostent *he, int port) addr = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr->sin_addr, curr, sizeof(struct in_addr)); +#ifdef __MINGW32__ + addr->sin_family = (short)(he->h_addrtype); +#else addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype); +#endif addr->sin_port = htons((unsigned short)port); break; @@ -326,7 +330,11 @@ Curl_he2ai(const struct hostent *he, int port) addr6 = (void *)ai->ai_addr; /* storage area for this info */ memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr)); +#ifdef __MINGW32__ + addr6->sin6_family = (short)(he->h_addrtype); +#else addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype); +#endif addr6->sin6_port = htons((unsigned short)port); break; #endif @@ -359,7 +367,7 @@ struct namebuff { /* * Curl_ip2addr() * - * This function takes an internet address, in binary form, as input parameter + * This function takes an Internet address, in binary form, as input parameter * along with its address family and the string version of the address, and it * returns a Curl_addrinfo chain filled in correctly with information for the * given address/host @@ -511,7 +519,7 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, * * This is strictly for memory tracing and are using the same style as the * family otherwise present in memdebug.c. I put these ones here since they - * require a bunch of structs I didn't want to include in memdebug.c + * require a bunch of structs I did not want to include in memdebug.c */ void @@ -535,7 +543,7 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis, * * This is strictly for memory tracing and are using the same style as the * family otherwise present in memdebug.c. I put these ones here since they - * require a bunch of structs I didn't want to include in memdebug.c + * require a bunch of structs I did not want to include in memdebug.c */ int @@ -563,7 +571,7 @@ curl_dbg_getaddrinfo(const char *hostname, #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /* - * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and Mac OS X + * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS * 10.11.5. */ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h index c757c49c5..9ceac997d 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.h +++ b/Utilities/cmcurl/lib/curl_addrinfo.h @@ -44,9 +44,9 @@ /* * Curl_addrinfo is our internal struct definition that we use to allow - * consistent internal handling of this data. We use this even when the - * system provides an addrinfo structure definition. And we use this for - * all sorts of IPv4 and IPV6 builds. + * consistent internal handling of this data. We use this even when the system + * provides an addrinfo structure definition. We use this for all sorts of + * IPv4 and IPV6 builds. */ struct Curl_addrinfo { diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index 8d4883769..02b2949da 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -/* lib/curl_config.h.in. Generated somehow by cmake. */ +/* lib/curl_config.h.in. Generated somehow by cmake. */ #include @@ -82,6 +82,9 @@ /* disables HTTP */ #cmakedefine CURL_DISABLE_HTTP 1 +/* disabled all HTTP authentication methods */ +#cmakedefine CURL_DISABLE_HTTP_AUTH 1 + /* disables IMAP */ #cmakedefine CURL_DISABLE_IMAP 1 @@ -124,6 +127,12 @@ /* disables RTSP */ #cmakedefine CURL_DISABLE_RTSP 1 +/* disables SHA-512/256 hash algorithm */ +#cmakedefine CURL_DISABLE_SHA512_256 1 + +/* disabled shuffle DNS feature */ +#cmakedefine CURL_DISABLE_SHUFFLE_DNS 1 + /* disables SMB */ #cmakedefine CURL_DISABLE_SMB 1 @@ -297,9 +306,6 @@ /* if you have the GNU gssapi libraries */ #cmakedefine HAVE_GSSGNU 1 -/* Define to 1 if you have the `idna_strerror' function. */ -#cmakedefine HAVE_IDNA_STRERROR 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IFADDRS_H 1 @@ -361,12 +367,6 @@ /* Define to 1 if you have the idn2.h header file. */ #cmakedefine HAVE_IDN2_H 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 - /* if zlib is available */ #cmakedefine HAVE_LIBZ 1 @@ -414,6 +414,9 @@ /* Define to 1 if you have the `pipe' function. */ #cmakedefine HAVE_PIPE 1 +/* Define to 1 if you have the `eventfd' function. */ +#cmakedefine HAVE_EVENTFD 1 + /* If you have a fine poll */ #cmakedefine HAVE_POLL_FINE 1 @@ -534,6 +537,9 @@ /* Define to 1 if you have the timeval struct. */ #cmakedefine HAVE_STRUCT_TIMEVAL 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_EVENTFD_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_FILIO_H 1 @@ -627,9 +633,6 @@ /* Define to the version of this package. */ #cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION} -/* a suitable file to read random data from */ -#cmakedefine RANDOM_FILE "${RANDOM_FILE}" - /* Note: SIZEOF_* variables are fetched with CMake through check_type_size(). As per CMake documentation on CheckTypeSize, C preprocessor code is @@ -686,7 +689,7 @@ ${SIZEOF_TIME_T_CODE} /* Define if you want to enable POSIX threaded DNS lookup */ #cmakedefine USE_THREADS_POSIX 1 -/* Define if you want to enable WIN32 threaded DNS lookup */ +/* Define if you want to enable Win32 threaded DNS lookup */ #cmakedefine USE_THREADS_WIN32 1 /* if GnuTLS is enabled */ @@ -701,16 +704,28 @@ ${SIZEOF_TIME_T_CODE} /* if BearSSL is enabled */ #cmakedefine USE_BEARSSL 1 -/* if WolfSSL is enabled */ +/* if Rustls is enabled */ +#cmakedefine USE_RUSTLS 1 + +/* if wolfSSL is enabled */ #cmakedefine USE_WOLFSSL 1 -/* if libSSH is in use */ +/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ +#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 + +/* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ +#cmakedefine HAVE_WOLFSSL_FULL_BIO 1 + +/* if libssh is in use */ #cmakedefine USE_LIBSSH 1 -/* if libSSH2 is in use */ +/* if libssh2 is in use */ #cmakedefine USE_LIBSSH2 1 -/* if libPSL is in use */ +/* if wolfssh is in use */ +#cmakedefine USE_WOLFSSH 1 + +/* if libpsl is in use */ #cmakedefine USE_LIBPSL 1 /* if you want to use OpenLDAP code instead of legacy ldap implementation */ @@ -722,7 +737,16 @@ ${SIZEOF_TIME_T_CODE} /* if librtmp/rtmpdump is in use */ #cmakedefine USE_LIBRTMP 1 -/* Define to 1 if you don't want the OpenSSL configuration to be loaded +/* if GSASL is in use */ +#cmakedefine USE_GSASL 1 + +/* if libuv is in use */ +#cmakedefine USE_LIBUV 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UV_H 1 + +/* Define to 1 if you do not want the OpenSSL configuration to be loaded automatically */ #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1 @@ -762,11 +786,6 @@ ${SIZEOF_TIME_T_CODE} /* Version number of package */ #cmakedefine VERSION ${VERSION} -/* Define to 1 if OS is AIX. */ -#ifndef _ALL_SOURCE -# undef _ALL_SOURCE -#endif - /* Number of bits in a file offset, on hosts where this is settable. */ #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index f8d2b2cc6..15836f58b 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c @@ -24,10 +24,10 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ +#if defined(USE_CURL_NTLM_CORE) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO)) #include "curl_des.h" @@ -36,7 +36,7 @@ * Curl_des_set_odd_parity() * * This is used to apply odd parity to the given byte array. It is typically - * used by when a cryptography engine doesn't have its own version. + * used by when a cryptography engine does not have its own version. * * The function is a port of the Java based oddParity() function over at: * diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h index 66525ab43..2dd498da2 100644 --- a/Utilities/cmcurl/lib/curl_des.h +++ b/Utilities/cmcurl/lib/curl_des.h @@ -26,10 +26,10 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ +#if defined(USE_CURL_NTLM_CORE) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO)) /* Applies odd parity to the given byte array */ diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c index 11c662a4c..d982e3126 100644 --- a/Utilities/cmcurl/lib/curl_endian.c +++ b/Utilities/cmcurl/lib/curl_endian.c @@ -30,7 +30,7 @@ * Curl_read16_le() * * This function converts a 16-bit integer from the little endian format, as - * used in the incoming package to whatever endian format we're using + * used in the incoming package to whatever endian format we are using * natively. * * Parameters: @@ -49,7 +49,7 @@ unsigned short Curl_read16_le(const unsigned char *buf) * Curl_read32_le() * * This function converts a 32-bit integer from the little endian format, as - * used in the incoming package to whatever endian format we're using + * used in the incoming package to whatever endian format we are using * natively. * * Parameters: @@ -68,7 +68,7 @@ unsigned int Curl_read32_le(const unsigned char *buf) * Curl_read16_be() * * This function converts a 16-bit integer from the big endian format, as - * used in the incoming package to whatever endian format we're using + * used in the incoming package to whatever endian format we are using * natively. * * Parameters: diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c index 5f9ca4f1b..ab848e8ff 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.c +++ b/Utilities/cmcurl/lib/curl_fnmatch.c @@ -80,7 +80,7 @@ static int parsekeyword(unsigned char **pattern, unsigned char *charset) unsigned char *p = *pattern; bool found = FALSE; for(i = 0; !found; i++) { - char c = *p++; + char c = (char)*p++; if(i >= KEYLEN) return SETCHARSET_FAIL; switch(state) { diff --git a/Utilities/cmcurl/lib/curl_fnmatch.h b/Utilities/cmcurl/lib/curl_fnmatch.h index 595646ff0..b8c2a4353 100644 --- a/Utilities/cmcurl/lib/curl_fnmatch.h +++ b/Utilities/cmcurl/lib/curl_fnmatch.h @@ -31,7 +31,7 @@ /* default pattern matching function * ================================= * Implemented with recursive backtracking, if you want to use Curl_fnmatch, - * please note that there is not implemented UTF/UNICODE support. + * please note that there is not implemented UTF/Unicode support. * * Implemented features: * '?' notation, does not match UTF characters diff --git a/Utilities/cmcurl/lib/curl_gethostname.c b/Utilities/cmcurl/lib/curl_gethostname.c index bd9b220d4..617a8ad52 100644 --- a/Utilities/cmcurl/lib/curl_gethostname.c +++ b/Utilities/cmcurl/lib/curl_gethostname.c @@ -28,26 +28,17 @@ /* * Curl_gethostname() is a wrapper around gethostname() which allows - * overriding the host name that the function would normally return. + * overriding the hostname that the function would normally return. * This capability is used by the test suite to verify exact matching * of NTLM authentication, which exercises libcurl's MD4 and DES code * as well as by the SMTP module when a hostname is not provided. * - * For libcurl debug enabled builds host name overriding takes place + * For libcurl debug enabled builds hostname overriding takes place * when environment variable CURL_GETHOSTNAME is set, using the value - * held by the variable to override returned host name. + * held by the variable to override returned hostname. * * Note: The function always returns the un-qualified hostname rather * than being provider dependent. - * - * For libcurl shared library release builds the test suite preloads - * another shared library named libhostname using the LD_PRELOAD - * mechanism which intercepts, and might override, the gethostname() - * function call. In this case a given platform must support the - * LD_PRELOAD mechanism and additionally have environment variable - * CURL_GETHOSTNAME set in order to override the returned host name. - * - * For libcurl static library release builds no overriding takes place. */ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) @@ -65,10 +56,13 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) #ifdef DEBUGBUILD - /* Override host name when environment variable CURL_GETHOSTNAME is set */ + /* Override hostname when environment variable CURL_GETHOSTNAME is set */ const char *force_hostname = getenv("CURL_GETHOSTNAME"); if(force_hostname) { - strncpy(name, force_hostname, namelen - 1); + if(strlen(force_hostname) < (size_t)namelen) + strcpy(name, force_hostname); + else + return 1; /* can't do it */ err = 0; } else { @@ -78,9 +72,6 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) #else /* DEBUGBUILD */ - /* The call to system's gethostname() might get intercepted by the - libhostname library when libcurl is built as a non-debug shared - library when running the test suite. */ name[0] = '\0'; err = gethostname(name, namelen); diff --git a/Utilities/cmcurl/lib/curl_memrchr.c b/Utilities/cmcurl/lib/curl_memrchr.c index 3f3dc6de1..c6d55f104 100644 --- a/Utilities/cmcurl/lib/curl_memrchr.c +++ b/Utilities/cmcurl/lib/curl_memrchr.c @@ -33,6 +33,9 @@ #include "memdebug.h" #ifndef HAVE_MEMRCHR +#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \ + defined(USE_OPENSSL) || \ + defined(USE_SCHANNEL) /* * Curl_memrchr() @@ -61,4 +64,5 @@ Curl_memrchr(const void *s, int c, size_t n) return NULL; } +#endif #endif /* HAVE_MEMRCHR */ diff --git a/Utilities/cmcurl/lib/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h index 45bb38c68..67a21ef36 100644 --- a/Utilities/cmcurl/lib/curl_memrchr.h +++ b/Utilities/cmcurl/lib/curl_memrchr.h @@ -34,11 +34,15 @@ #endif #else /* HAVE_MEMRCHR */ +#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \ + defined(USE_OPENSSL) || \ + defined(USE_SCHANNEL) void *Curl_memrchr(const void *s, int c, size_t n); #define memrchr(x,y,z) Curl_memrchr((x),(y),(z)) +#endif #endif /* HAVE_MEMRCHR */ #endif /* HEADER_CURL_MEMRCHR_H */ diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h index 8b9ac719e..dec384e2f 100644 --- a/Utilities/cmcurl/lib/curl_multibyte.h +++ b/Utilities/cmcurl/lib/curl_multibyte.h @@ -39,19 +39,20 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() * and curlx_unicodefree() main purpose is to minimize the number of * preprocessor conditional directives needed by code using these - * to differentiate UNICODE from non-UNICODE builds. + * to differentiate Unicode from non-Unicode builds. * - * In the case of a non-UNICODE build the tchar strings are char strings that + * In the case of a non-Unicode build the tchar strings are char strings that * are duplicated via strdup and remain in whatever the passed in encoding is, * which is assumed to be UTF-8 but may be other encoding. Therefore the - * significance of the conversion functions is primarily for UNICODE builds. + * significance of the conversion functions is primarily for Unicode builds. * * Allocated memory should be free'd with curlx_unicodefree(). * * Note: Because these are curlx functions their memory usage is not tracked - * by the curl memory tracker memdebug. You'll notice that curlx function-like - * macros call free and strdup in parentheses, eg (strdup)(ptr), and that's to - * ensure that the curl memdebug override macros do not replace them. + * by the curl memory tracker memdebug. you will notice that curlx + * function-like macros call free and strdup in parentheses, eg (strdup)(ptr), + * and that is to ensure that the curl memdebug override macros do not replace + * them. */ #if defined(UNICODE) && defined(_WIN32) diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 6f6d75c03..eee33afe5 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -57,9 +57,14 @@ #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0) #define USE_OPENSSL_DES #endif +#elif defined(USE_WOLFSSL) + #include + #if !defined(NO_DES3) + #define USE_OPENSSL_DES + #endif #endif -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) #if defined(USE_OPENSSL) # include @@ -67,7 +72,6 @@ # include # include #else -# include # include # include # include @@ -110,7 +114,7 @@ #elif defined(USE_WIN32_CRYPTO) # include #else -# error "Can't compile NTLM support without a crypto library with DES." +# error "cannot compile NTLM support without a crypto library with DES." # define CURL_NTLM_NOT_SUPPORTED #endif @@ -137,20 +141,20 @@ */ static void extend_key_56_to_64(const unsigned char *key_56, char *key) { - key[0] = key_56[0]; - key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); - key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); - key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); - key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); - key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); - key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); + key[0] = (char)key_56[0]; + key[1] = (char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); + key[2] = (char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); + key[3] = (char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); + key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); + key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); + key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); + key[7] = (char) ((key_56[6] << 1) & 0xFF); } #endif -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) /* - * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The + * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The * key schedule ks is also set. */ static void setup_des_key(const unsigned char *key_56, @@ -158,7 +162,7 @@ static void setup_des_key(const unsigned char *key_56, { DES_cblock key; - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, (char *) &key); /* Set the key parity to odd */ @@ -175,7 +179,7 @@ static void setup_des_key(const unsigned char *key_56, { char key[8]; - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, key); /* Set the key parity to odd */ @@ -193,7 +197,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, mbedtls_des_context ctx; char key[8]; - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, key); /* Set the key parity to odd */ @@ -214,7 +218,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, size_t out_len; CCCryptorStatus err; - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, key); /* Set the key parity to odd */ @@ -240,7 +244,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, ctl.Func_ID = ENCRYPT_ONLY; ctl.Data_Len = sizeof(key); - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, ctl.Crypto_Key); /* Set the key parity to odd */ @@ -278,7 +282,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, blob.hdr.aiKeyAlg = CALG_DES; blob.len = sizeof(blob.key); - /* Expand the 56-bit key to 64-bits */ + /* Expand the 56-bit key to 64 bits */ extend_key_56_to_64(key_56, blob.key); /* Set the key parity to odd */ @@ -313,7 +317,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); @@ -367,7 +371,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, { /* Create LanManager hashed password. */ -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) +#if defined(USE_OPENSSL_DES) DES_key_schedule ks; setup_des_key(pw, DESKEY(ks)); @@ -466,13 +470,13 @@ static void time2filetime(struct ms_filetime *ft, time_t t) unsigned int r, s; unsigned int i; - ft->dwLowDateTime = t & 0xFFFFFFFF; + ft->dwLowDateTime = (unsigned int)t & 0xFFFFFFFF; ft->dwHighDateTime = 0; # ifndef HAVE_TIME_T_UNSIGNED /* Extend sign if needed. */ if(ft->dwLowDateTime & 0x80000000) - ft->dwHighDateTime = ~0; + ft->dwHighDateTime = ~(unsigned int)0; # endif /* Bias seconds to Jan 1, 1601. @@ -534,13 +538,13 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, /* * Curl_ntlm_core_mk_ntlmv2_resp() * - * This creates the NTLMv2 response as set in the ntlm type-3 message. + * This creates the NTLMv2 response as set in the NTLM type-3 message. * * Parameters: * - * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * ntlmv2hash [in] - The NTLMv2 hash (16 bytes) * challenge_client [in] - The client nonce (8 bytes) - * ntlm [in] - The ntlm data struct being used to read TargetInfo + * ntlm [in] - The NTLM data struct being used to read TargetInfo and Server challenge received in the type-2 message * ntresp [out] - The address where a pointer to newly allocated * memory holding the NTLMv2 response. @@ -629,11 +633,11 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, /* * Curl_ntlm_core_mk_lmv2_resp() * - * This creates the LMv2 response as used in the ntlm type-3 message. + * This creates the LMv2 response as used in the NTLM type-3 message. * * Parameters: * - * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * ntlmv2hash [in] - The NTLMv2 hash (16 bytes) * challenge_client [in] - The client nonce (8 bytes) * challenge_client [in] - The server challenge (8 bytes) * lmresp [out] - The LMv2 response (24 bytes) @@ -657,7 +661,7 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, if(result) return result; - /* Concatenate the HMAC MD5 output with the client nonce */ + /* Concatenate the HMAC MD5 output with the client nonce */ memcpy(lmresp, hmac_output, 16); memcpy(lmresp + 16, challenge_client, 8); diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h index 0c62ee052..e2e4b1bd4 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.h +++ b/Utilities/cmcurl/lib/curl_ntlm_core.h @@ -28,13 +28,6 @@ #if defined(USE_CURL_NTLM_CORE) -#if defined(USE_OPENSSL) -# include -#elif defined(USE_WOLFSSL) -# include -# include -#endif - /* Helpers to generate function byte arguments in little endian order */ #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h index c2457d2a6..e851b14a5 100644 --- a/Utilities/cmcurl/lib/curl_printf.h +++ b/Utilities/cmcurl/lib/curl_printf.h @@ -29,6 +29,10 @@ * *rintf() functions. */ +#ifndef CURL_TEMP_PRINTF +#error "CURL_TEMP_PRINTF must be set before including curl/mprintf.h" +#endif + #include #define MERR_OK 0 @@ -40,7 +44,6 @@ # undef msnprintf # undef vprintf # undef vfprintf -# undef vsnprintf # undef mvsnprintf # undef aprintf # undef vaprintf diff --git a/Utilities/cmcurl/lib/curl_range.c b/Utilities/cmcurl/lib/curl_range.c index d499953c9..49fb5f077 100644 --- a/Utilities/cmcurl/lib/curl_range.c +++ b/Utilities/cmcurl/lib/curl_range.c @@ -55,15 +55,13 @@ CURLcode Curl_range(struct Curl_easy *data) if((to_t == CURL_OFFT_INVAL) && !from_t) { /* X - */ data->state.resume_from = from; - DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file", - from)); + DEBUGF(infof(data, "RANGE %" FMT_OFF_T " to end of file", from)); } else if((from_t == CURL_OFFT_INVAL) && !to_t) { /* -Y */ data->req.maxdownload = to; data->state.resume_from = -to; - DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes", - to)); + DEBUGF(infof(data, "RANGE the last %" FMT_OFF_T " bytes", to)); } else { /* X-Y */ @@ -79,13 +77,12 @@ CURLcode Curl_range(struct Curl_easy *data) data->req.maxdownload = totalsize + 1; /* include last byte */ data->state.resume_from = from; - DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T - " getting %" CURL_FORMAT_CURL_OFF_T " bytes", + DEBUGF(infof(data, "RANGE from %" FMT_OFF_T + " getting %" FMT_OFF_T " bytes", from, data->req.maxdownload)); } - DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T - " to %" CURL_FORMAT_CURL_OFF_T ", totally %" - CURL_FORMAT_CURL_OFF_T " bytes", + DEBUGF(infof(data, "range-download from %" FMT_OFF_T + " to %" FMT_OFF_T ", totally %" FMT_OFF_T " bytes", from, to, data->req.maxdownload)); } else diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c index 76eff787b..49f59e3a7 100644 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ b/Utilities/cmcurl/lib/curl_rtmp.c @@ -236,7 +236,7 @@ static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; - /* We have to know if it's a write before we send the + /* We have to know if it is a write before we send the * connect request packet */ if(data->state.upload) @@ -273,10 +273,10 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done) if(data->state.upload) { Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); } else - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); *done = TRUE; return CURLE_OK; } @@ -329,13 +329,14 @@ static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, } static ssize_t rtmp_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, CURLcode *err) { struct connectdata *conn = data->conn; RTMP *r = conn->proto.rtmp; ssize_t num; (void)sockindex; /* unused */ + (void)eos; /* unused */ num = RTMP_Write(r, (char *)buf, curlx_uztosi(len)); if(num < 0) diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index ba8911b72..24f8c8c53 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -328,7 +328,7 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) if(data->state.aptr.user) return TRUE; - /* EXTERNAL can authenticate without a user name and/or password */ + /* EXTERNAL can authenticate without a username and/or password */ if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) return TRUE; diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index f7ba7e92a..01b9015fc 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -28,6 +28,9 @@ #define CURL_NO_OLDIES #endif +/* Tell "curl/curl.h" not to include "curl/mprintf.h" */ +#define CURL_SKIP_INCLUDE_MPRINTF + /* FIXME: Delete this once the warnings have been fixed. */ #if !defined(CURL_WARN_SIGN_CONVERSION) #ifdef __GNUC__ @@ -40,6 +43,45 @@ #include <_mingw.h> #endif +/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0 and newer (as of 14.1.0) + that started advertising the `availability` attribute, which then gets used + by Apple SDK, but, in a way incompatible with gcc, resulting in a misc + errors inside SDK headers, e.g.: + error: attributes should be specified before the declarator in a function + definition + error: expected ',' or '}' before + Followed by missing declarations. + Fix it by overriding the built-in feature-check macro used by the headers + to enable the problematic attributes. This makes the feature check fail. */ +#if defined(__APPLE__) && \ + !defined(__clang__) && \ + defined(__GNUC__) && __GNUC__ >= 12 && \ + defined(__has_attribute) +#define availability curl_pp_attribute_disabled +#endif + +#if defined(__APPLE__) +#include +#include +/* Fixup faulty target macro initialization in macOS SDK since v14.4 (as of + 15.0 beta). The SDK target detection in `TargetConditionals.h` correctly + detects macOS, but fails to set the macro's old name `TARGET_OS_OSX`, then + continues to set it to a default value of 0. Other parts of the SDK still + rely on the old name, and with this inconsistency our builds fail due to + missing declarations. It happens when using mainline llvm older than v18. + Later versions fixed it by predefining these target macros, avoiding the + faulty dynamic detection. gcc is not affected (for now) because it lacks + the necessary dynamic detection features, so the SDK falls back to + a codepath that sets both the old and new macro to 1. */ +#if defined(TARGET_OS_MAC) && TARGET_OS_MAC && \ + defined(TARGET_OS_OSX) && !TARGET_OS_OSX && \ + (!defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE) && \ + (!defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR) +#undef TARGET_OS_OSX +#define TARGET_OS_OSX TARGET_OS_MAC +#endif +#endif + /* * Disable Visual Studio warnings: * 4127 "conditional expression is constant" @@ -50,7 +92,7 @@ #ifdef _WIN32 /* - * Don't include unneeded stuff in Windows headers to avoid compiler + * Do not include unneeded stuff in Windows headers to avoid compiler * warnings and macro clashes. * Make sure to define this macro before including any Windows headers. */ @@ -284,7 +326,7 @@ /* curl uses its own printf() function internally. It understands the GNU * format. Use this format, so that is matches the GNU format attribute we - * use with the mingw compiler, allowing it to verify them at compile-time. + * use with the MinGW compiler, allowing it to verify them at compile-time. */ #ifdef __MINGW32__ # undef CURL_FORMAT_CURL_OFF_T @@ -310,13 +352,28 @@ #define CURL_PRINTF(fmt, arg) #endif +/* Override default printf mask check rules in "curl/mprintf.h" */ +#define CURL_TEMP_PRINTF CURL_PRINTF + +/* Workaround for mainline llvm v16 and earlier missing a built-in macro + expected by macOS SDK v14 / Xcode v15 (2023) and newer. + gcc (as of v14) is also missing it. */ +#if defined(__APPLE__) && \ + ((!defined(__apple_build_version__) && \ + defined(__clang__) && __clang_major__ < 17) || \ + (defined(__GNUC__) && __GNUC__ <= 14)) && \ + defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__) +#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif + /* * Use getaddrinfo to resolve the IPv4 address literal. If the current network - * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64, + * interface does not support IPv4, but supports IPv6, NAT64, and DNS64, * performing this task will result in a synthesized IPv6 address. */ #if defined(__APPLE__) && !defined(USE_ARES) -#include #define USE_RESOLVE_ON_IPS 1 # if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \ defined(USE_IPV6) @@ -408,7 +465,7 @@ #endif /* - * Large file (>2Gb) support using WIN32 functions. + * Large file (>2Gb) support using Win32 functions. */ #ifdef USE_WIN32_LARGE_FILES @@ -431,7 +488,7 @@ #endif /* - * Small file (<2Gb) support using WIN32 functions. + * Small file (<2Gb) support using Win32 functions. */ #ifdef USE_WIN32_SMALL_FILES @@ -462,7 +519,7 @@ #endif #ifndef SIZEOF_TIME_T -/* assume default size of time_t to be 32 bit */ +/* assume default size of time_t to be 32 bits */ #define SIZEOF_TIME_T 4 #endif @@ -477,15 +534,15 @@ #endif #if SIZEOF_CURL_SOCKET_T < 8 -# define CURL_FORMAT_SOCKET_T "d" +# define FMT_SOCKET_T "d" #elif defined(__MINGW32__) -# define CURL_FORMAT_SOCKET_T "zd" +# define FMT_SOCKET_T "zd" #else -# define CURL_FORMAT_SOCKET_T "qd" +# define FMT_SOCKET_T "qd" #endif /* - * Default sizeof(off_t) in case it hasn't been defined in config file. + * Default sizeof(off_t) in case it has not been defined in config file. */ #ifndef SIZEOF_OFF_T @@ -529,10 +586,13 @@ # endif # define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU # define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX) -# define CURL_PRId64 CURL_FORMAT_CURL_OFF_T -# define CURL_PRIu64 CURL_FORMAT_CURL_OFF_TU +# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T +# define FMT_PRIu64 CURL_FORMAT_CURL_OFF_TU #endif +#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T +#define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU + #if (SIZEOF_TIME_T == 4) # ifdef HAVE_TIME_T_UNSIGNED # define TIME_T_MAX UINT_MAX @@ -552,7 +612,7 @@ #endif #ifndef SIZE_T_MAX -/* some limits.h headers have this defined, some don't */ +/* some limits.h headers have this defined, some do not */ #if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) #define SIZE_T_MAX 18446744073709551615U #else @@ -561,7 +621,7 @@ #endif #ifndef SSIZE_T_MAX -/* some limits.h headers have this defined, some don't */ +/* some limits.h headers have this defined, some do not */ #if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) #define SSIZE_T_MAX 9223372036854775807 #else @@ -570,7 +630,7 @@ #endif /* - * Arg 2 type for gethostname in case it hasn't been defined in config file. + * Arg 2 type for gethostname in case it has not been defined in config file. */ #ifndef GETHOSTNAME_TYPE_ARG2 @@ -679,6 +739,11 @@ #define USE_SSL /* SSL support has been enabled */ #endif +#if defined(USE_WOLFSSL) && defined(USE_GNUTLS) +/* Avoid defining unprefixed wolfSSL SHA macros colliding with nettle ones */ +#define NO_OLD_WC_NAMES +#endif + /* Single point where USE_SPNEGO definition might be defined */ #if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) @@ -780,12 +845,12 @@ #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) # if defined(SOCKET) || defined(USE_WINSOCK) -# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!" +# error "Winsock and lwIP TCP/IP stack definitions shall not coexist!" # endif #endif /* - * shutdown() flags for systems that don't define them + * shutdown() flags for systems that do not define them */ #ifndef SHUT_RD @@ -818,7 +883,7 @@ Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 #define FOPEN_WRITETEXT "wt" #define FOPEN_APPENDTEXT "at" #elif defined(__CYGWIN__) -/* Cygwin has specific behavior we need to address when WIN32 is not defined. +/* Cygwin has specific behavior we need to address when _WIN32 is not defined. https://cygwin.com/cygwin-ug-net/using-textbinary.html For write we want our output to have line endings of LF and be compatible with other Cygwin utilities. For read we want to handle input that may have line @@ -833,7 +898,7 @@ endings either CRLF or LF so 't' is appropriate. #define FOPEN_APPENDTEXT "a" #endif -/* for systems that don't detect this in configure */ +/* for systems that do not detect this in configure */ #ifndef CURL_SA_FAMILY_T # if defined(HAVE_SA_FAMILY_T) # define CURL_SA_FAMILY_T sa_family_t @@ -862,7 +927,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); #endif -#ifdef DEBUGBUILD +#ifdef UNITTESTS #define UNITTEST #else #define UNITTEST static diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h index 40c7bcbff..1521e69f9 100644 --- a/Utilities/cmcurl/lib/curl_setup_once.h +++ b/Utilities/cmcurl/lib/curl_setup_once.h @@ -106,7 +106,7 @@ #endif /* - * Definition of timeval struct for platforms that don't have it. + * Definition of timeval struct for platforms that do not have it. */ #ifndef HAVE_STRUCT_TIMEVAL @@ -130,7 +130,7 @@ struct timeval { #if defined(__minix) -/* Minix doesn't support recv on TCP sockets */ +/* Minix does not support recv on TCP sockets */ #define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \ (RECV_TYPE_ARG2)(y), \ (RECV_TYPE_ARG3)(z)) @@ -143,7 +143,7 @@ struct timeval { * * HAVE_RECV is defined if you have a function named recv() * which is used to read incoming data from sockets. If your - * function has another name then don't define HAVE_RECV. + * function has another name then do not define HAVE_RECV. * * If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2, * RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also @@ -151,7 +151,7 @@ struct timeval { * * HAVE_SEND is defined if you have a function named send() * which is used to write outgoing data on a connected socket. - * If yours has another name then don't define HAVE_SEND. + * If yours has another name then do not define HAVE_SEND. * * If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2, * SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and @@ -170,7 +170,7 @@ struct timeval { #if defined(__minix) -/* Minix doesn't support send on TCP sockets */ +/* Minix does not support send on TCP sockets */ #define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ (SEND_TYPE_ARG2)(y), \ (SEND_TYPE_ARG3)(z)) @@ -226,7 +226,7 @@ struct timeval { /* * 'bool' exists on platforms with , i.e. C99 platforms. - * On non-C99 platforms there's no bool, so define an enum for that. + * On non-C99 platforms there is no bool, so define an enum for that. * On C99 platforms 'false' and 'true' also exist. Enum uses a * global namespace though, so use bool_false and bool_true. */ @@ -238,7 +238,7 @@ struct timeval { } bool; /* - * Use a define to let 'true' and 'false' use those enums. There + * Use a define to let 'true' and 'false' use those enums. There * are currently no use of true and false in libcurl proper, but * there are some in the examples. This will cater for any later * code happening to use true and false. diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index d99f958f9..c3cf00a21 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -33,13 +33,8 @@ extern const struct HMAC_params Curl_HMAC_SHA256[1]; -#ifdef USE_WOLFSSL -/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from - * sha.h */ -#include -#include -#else -#define SHA256_DIGEST_LENGTH 32 +#ifndef CURL_SHA256_DIGEST_LENGTH +#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ #endif CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, diff --git a/Utilities/cmcurl/lib/curl_sha512_256.c b/Utilities/cmcurl/lib/curl_sha512_256.c index 5f2e99517..576eda244 100644 --- a/Utilities/cmcurl/lib/curl_sha512_256.c +++ b/Utilities/cmcurl/lib/curl_sha512_256.c @@ -37,7 +37,7 @@ * * SecureTransport (Darwin) * * mbedTLS * * BearSSL - * * rustls + * * Rustls * Skip the backend if it does not support the required algorithm */ #if defined(USE_OPENSSL) @@ -93,13 +93,13 @@ /** * Size of the SHA-512/256 single processing block in bytes. */ -#define SHA512_256_BLOCK_SIZE 128 +#define CURL_SHA512_256_BLOCK_SIZE 128 /** * Size of the SHA-512/256 resulting digest in bytes. * This is the final digest size, not intermediate hash. */ -#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH +#define CURL_SHA512_256_DIGEST_SIZE CURL_SHA512_256_DIGEST_LENGTH /** * Context type used for SHA-512/256 calculations @@ -124,9 +124,9 @@ Curl_sha512_256_init(void *context) if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) { /* Check whether the header and this file use the same numbers */ - DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE); + DEBUGASSERT(EVP_MD_CTX_size(*ctx) == CURL_SHA512_256_DIGEST_SIZE); /* Check whether the block size is correct */ - DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE); + DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == CURL_SHA512_256_BLOCK_SIZE); return CURLE_OK; /* Success */ } @@ -163,7 +163,8 @@ Curl_sha512_256_update(void *context, * Finalise SHA-512/256 calculation, return digest. * * @param context the calculation context - * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE + # bytes * @return CURLE_OK if succeed, * error code otherwise */ @@ -177,11 +178,11 @@ Curl_sha512_256_finish(unsigned char *digest, #ifdef NEED_NETBSD_SHA512_256_WORKAROUND /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ - unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2]; + unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; ret = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; if(ret == CURLE_OK) - memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE); + memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); #else /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; @@ -195,6 +196,9 @@ Curl_sha512_256_finish(unsigned char *digest, #elif defined(USE_GNUTLS_SHA512_256) +#define CURL_SHA512_256_BLOCK_SIZE SHA512_256_BLOCK_SIZE +#define CURL_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE + /** * Context type used for SHA-512/256 calculations */ @@ -212,7 +216,7 @@ Curl_sha512_256_init(void *context) Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; /* Check whether the header and this file use the same numbers */ - DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE); sha512_256_init(ctx); @@ -247,7 +251,8 @@ Curl_sha512_256_update(void *context, * Finalise SHA-512/256 calculation, return digest. * * @param context the calculation context - * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE + # bytes * @return always CURLE_OK */ static CURLcode @@ -256,7 +261,8 @@ Curl_sha512_256_finish(unsigned char *digest, { Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; - sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest); + sha512_256_digest(ctx, + (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest); return CURLE_OK; } @@ -270,9 +276,9 @@ Curl_sha512_256_finish(unsigned char *digest, * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** * * ** The author ported the code to libcurl. The ported code is provided ** * * ** under curl license. ** * - * ** This is a minimal version with minimal optimisations. Performance ** * + * ** This is a minimal version with minimal optimizations. Performance ** * * ** can be significantly improved. Big-endian store and load macros ** * - * ** are obvious targets for optimisation. ** */ + * ** are obvious targets for optimization. ** */ #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) @@ -328,7 +334,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits) bits %= 64; if(0 == bits) return value; - /* Defined in a form which modern compiler could optimise. */ + /* Defined in a form which modern compiler could optimize. */ return (value >> bits) | (value << (64 - bits)); } @@ -360,7 +366,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits) * Size of the SHA-512/256 resulting digest in bytes * This is the final digest size, not intermediate hash. */ -#define SHA512_256_DIGEST_SIZE \ +#define CURL_SHA512_256_DIGEST_SIZE \ (SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD) /** @@ -371,7 +377,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits) /** * Size of the SHA-512/256 single processing block in bytes. */ -#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8) +#define CURL_SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8) /** * Size of the SHA-512/256 single processing block in words. @@ -425,7 +431,7 @@ MHDx_sha512_256_init(void *context) struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context; /* Check whether the header and this file use the same numbers */ - DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE); + DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE); DEBUGASSERT(sizeof(curl_uint64_t) == 8); @@ -453,7 +459,7 @@ MHDx_sha512_256_init(void *context) * Base of the SHA-512/256 transformation. * Gets a full 128 bytes block of data and updates hash values; * @param H hash values - * @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block + * @param data the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block */ static void MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], @@ -474,10 +480,10 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */ curl_uint64_t W[16]; - /* 'Ch' and 'Maj' macro functions are defined with widely-used optimisation. + /* 'Ch' and 'Maj' macro functions are defined with widely-used optimization. See FIPS PUB 180-4 formulae 4.8, 4.9. */ -#define Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) ) -#define Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) ) +#define Sha512_Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) ) +#define Sha512_Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) ) /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ @@ -547,9 +553,9 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], * Note: 'wt' must be used exactly one time in this macro as macro for 'wt' calculation may change other data as well every time when used. */ -#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ - (vD) += ((vH) += SIG1 ((vE)) + Ch ((vE),(vF),(vG)) + (kt) + (wt)); \ - (vH) += SIG0 ((vA)) + Maj ((vA),(vB),(vC)); } while (0) +#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ + (vD) += ((vH) += SIG1((vE)) + Sha512_Ch((vE),(vF),(vG)) + (kt) + (wt)); \ + (vH) += SIG0((vA)) + Sha512_Maj((vA),(vB),(vC)); } while (0) /* One step of SHA-512/256 computation with working variables rotation, see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns @@ -636,9 +642,9 @@ MHDx_sha512_256_update(void *context, if(0 == length) return CURLE_OK; /* Shortcut, do nothing */ - /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) - equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1)) + equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); ctx->count += length; if(length > ctx->count) ctx->count_bits_hi += 1U << 3; /* Value wrap */ @@ -646,7 +652,7 @@ MHDx_sha512_256_update(void *context, ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF); if(0 != bytes_have) { - unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have; + unsigned int bytes_left = CURL_SHA512_256_BLOCK_SIZE - bytes_have; if(length >= bytes_left) { /* Combine new data with data in the buffer and process the full block. */ @@ -660,12 +666,12 @@ MHDx_sha512_256_update(void *context, } } - while(SHA512_256_BLOCK_SIZE <= length) { + while(CURL_SHA512_256_BLOCK_SIZE <= length) { /* Process any full blocks of new data directly, without copying to the buffer. */ MHDx_sha512_256_transform(ctx->H, data); - data += SHA512_256_BLOCK_SIZE; - length -= SHA512_256_BLOCK_SIZE; + data += CURL_SHA512_256_BLOCK_SIZE; + length -= CURL_SHA512_256_BLOCK_SIZE; } if(0 != length) { @@ -694,7 +700,8 @@ MHDx_sha512_256_update(void *context, * Finalise SHA-512/256 calculation, return digest. * * @param context the calculation context - * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes + * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE + # bytes * @return always CURLE_OK */ static CURLcode @@ -712,9 +719,9 @@ MHDx_sha512_256_finish(unsigned char *digest, not change the amount of hashed data. */ num_bits = ctx->count << 3; - /* Note: (count & (SHA512_256_BLOCK_SIZE-1)) - equals (count % SHA512_256_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1)); + /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1)) + equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */ + bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); /* Input data must be padded with a single bit "1", then with zeros and the finally the length of data in bits must be added as the final bytes @@ -728,12 +735,12 @@ MHDx_sha512_256_finish(unsigned char *digest, processed when formed). */ ((unsigned char *) ctx_buf)[bytes_have++] = 0x80U; - if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) { + if(CURL_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) { /* No space in the current block to put the total length of message. Pad the current block with zeros and process it. */ - if(bytes_have < SHA512_256_BLOCK_SIZE) + if(bytes_have < CURL_SHA512_256_BLOCK_SIZE) memset(((unsigned char *) ctx_buf) + bytes_have, 0, - SHA512_256_BLOCK_SIZE - bytes_have); + CURL_SHA512_256_BLOCK_SIZE - bytes_have); /* Process the full block. */ MHDx_sha512_256_transform(ctx->H, ctx->buffer); /* Start the new block. */ @@ -742,17 +749,17 @@ MHDx_sha512_256_finish(unsigned char *digest, /* Pad the rest of the buffer with zeros. */ memset(((unsigned char *) ctx_buf) + bytes_have, 0, - SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have); + CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have); /* Put high part of number of bits in processed message and then lower part of number of bits as big-endian values. See FIPS PUB 180-4 section 5.1.2. */ /* Note: the target location is predefined and buffer is always aligned */ MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ - + SHA512_256_BLOCK_SIZE \ + + CURL_SHA512_256_BLOCK_SIZE \ - SHA512_256_SIZE_OF_LEN_ADD, \ ctx->count_bits_hi); MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ - + SHA512_256_BLOCK_SIZE \ + + CURL_SHA512_256_BLOCK_SIZE \ - SHA512_256_SIZE_OF_LEN_ADD \ + SHA512_256_BYTES_IN_WORD, \ num_bits); @@ -841,9 +848,9 @@ const struct HMAC_params Curl_HMAC_SHA512_256[] = { /* Context structure size. */ sizeof(Curl_sha512_256_ctx), /* Maximum key length (bytes). */ - SHA512_256_BLOCK_SIZE, + CURL_SHA512_256_BLOCK_SIZE, /* Result length (bytes). */ - SHA512_256_DIGEST_SIZE + CURL_SHA512_256_DIGEST_SIZE } }; diff --git a/Utilities/cmcurl/lib/curl_sha512_256.h b/Utilities/cmcurl/lib/curl_sha512_256.h index 30a9f140e..a84e77bc3 100644 --- a/Utilities/cmcurl/lib/curl_sha512_256.h +++ b/Utilities/cmcurl/lib/curl_sha512_256.h @@ -33,7 +33,7 @@ extern const struct HMAC_params Curl_HMAC_SHA512_256[1]; -#define SHA512_256_DIGEST_LENGTH 32 +#define CURL_SHA512_256_DIGEST_LENGTH 32 CURLcode Curl_sha512_256it(unsigned char *output, const unsigned char *input, diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index eb21e7e2b..680bb661b 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c @@ -52,10 +52,10 @@ typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID); #endif /* Handle of security.dll or secur32.dll, depending on Windows version */ -HMODULE s_hSecDll = NULL; +HMODULE Curl_hSecDll = NULL; /* Pointer to SSPI dispatch table */ -PSecurityFunctionTable s_pSecFn = NULL; +PSecurityFunctionTable Curl_pSecFn = NULL; /* * Curl_sspi_global_init() @@ -79,29 +79,29 @@ CURLcode Curl_sspi_global_init(void) INITSECURITYINTERFACE_FN pInitSecurityInterface; /* If security interface is not yet initialized try to do this */ - if(!s_hSecDll) { + if(!Curl_hSecDll) { /* Security Service Provider Interface (SSPI) functions are located in * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP * have both these DLLs (security.dll forwards calls to secur32.dll) */ /* Load SSPI dll into the address space of the calling process */ if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL)) - s_hSecDll = Curl_load_library(TEXT("security.dll")); + Curl_hSecDll = Curl_load_library(TEXT("security.dll")); else - s_hSecDll = Curl_load_library(TEXT("secur32.dll")); - if(!s_hSecDll) + Curl_hSecDll = Curl_load_library(TEXT("secur32.dll")); + if(!Curl_hSecDll) return CURLE_FAILED_INIT; /* Get address of the InitSecurityInterfaceA function from the SSPI dll */ pInitSecurityInterface = CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN, - (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT))); + (GetProcAddress(Curl_hSecDll, SECURITYENTRYPOINT))); if(!pInitSecurityInterface) return CURLE_FAILED_INIT; /* Get pointer to Security Service Provider Interface dispatch table */ - s_pSecFn = pInitSecurityInterface(); - if(!s_pSecFn) + Curl_pSecFn = pInitSecurityInterface(); + if(!Curl_pSecFn) return CURLE_FAILED_INIT; } @@ -119,10 +119,10 @@ CURLcode Curl_sspi_global_init(void) */ void Curl_sspi_global_cleanup(void) { - if(s_hSecDll) { - FreeLibrary(s_hSecDll); - s_hSecDll = NULL; - s_pSecFn = NULL; + if(Curl_hSecDll) { + FreeLibrary(Curl_hSecDll); + Curl_hSecDll = NULL; + Curl_pSecFn = NULL; } } @@ -134,7 +134,7 @@ void Curl_sspi_global_cleanup(void) * * Parameters: * - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * identity [in/out] - The identity structure. * diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h index b26c39156..535a1ff65 100644 --- a/Utilities/cmcurl/lib/curl_sspi.h +++ b/Utilities/cmcurl/lib/curl_sspi.h @@ -57,8 +57,8 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); /* Forward-declaration of global variables defined in curl_sspi.c */ -extern HMODULE s_hSecDll; -extern PSecurityFunctionTable s_pSecFn; +extern HMODULE Curl_hSecDll; +extern PSecurityFunctionTable Curl_pSecFn; /* Provide some definitions missing in old headers */ #define SP_NAME_DIGEST "WDigest" diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index 93fa2dafb..6d73273f7 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c @@ -35,7 +35,9 @@ #endif #include "curl_threads.h" +#ifdef BUILDING_LIBCURL #include "curl_memory.h" +#endif /* The last #include file should be: */ #include "memdebug.h" @@ -100,18 +102,23 @@ int Curl_thread_join(curl_thread_t *hnd) #elif defined(USE_THREADS_WIN32) -/* !checksrc! disable SPACEBEFOREPAREN 1 */ -curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *), +curl_thread_t Curl_thread_create( +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + DWORD +#else + unsigned int +#endif + (CURL_STDCALL *func) (void *), void *arg) { -#ifdef _WIN32_WCE +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) typedef HANDLE curl_win_thread_handle_t; #else typedef uintptr_t curl_win_thread_handle_t; #endif curl_thread_t t; curl_win_thread_handle_t thread_handle; -#ifdef _WIN32_WCE +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL); #else thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 27a478d4c..be22352dc 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h @@ -52,8 +52,13 @@ #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -/* !checksrc! disable SPACEBEFOREPAREN 1 */ -curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *), +curl_thread_t Curl_thread_create( +#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) + DWORD +#else + unsigned int +#endif + (CURL_STDCALL *func) (void *), void *arg); void Curl_thread_destroy(curl_thread_t hnd); diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index f017e21a5..58512d74d 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c @@ -53,6 +53,9 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif void Curl_debug(struct Curl_easy *data, curl_infotype type, char *ptr, size_t size) @@ -118,10 +121,16 @@ static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, const char * const fmt, va_list ap) { int len = 0; - char buffer[MAXINFO + 2]; + char buffer[MAXINFO + 5]; if(feat) - len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name); - len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap); + len = msnprintf(buffer, (MAXINFO + 1), "[%s] ", feat->name); + len += mvsnprintf(buffer + len, (MAXINFO + 1) - len, fmt, ap); + if(len >= MAXINFO) { /* too long, shorten with '...' */ + --len; + buffer[len++] = '.'; + buffer[len++] = '.'; + buffer[len++] = '.'; + } buffer[len++] = '\n'; buffer[len] = '\0'; Curl_debug(data, CURLINFO_TEXT, buffer, len); @@ -212,58 +221,144 @@ void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) } #endif /* !CURL_DISABLE_FTP */ -static struct curl_trc_feat *trc_feats[] = { - &Curl_trc_feat_read, - &Curl_trc_feat_write, +#ifndef CURL_DISABLE_SMTP +struct curl_trc_feat Curl_trc_feat_smtp = { + "SMTP", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_smtp, fmt, ap); + va_end(ap); + } +} +#endif /* !CURL_DISABLE_SMTP */ + +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +struct curl_trc_feat Curl_trc_feat_ws = { + "WS", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_ws, fmt, ap); + va_end(ap); + } +} +#endif /* USE_WEBSOCKETS && !CURL_DISABLE_HTTP */ + +#define TRC_CT_NONE (0) +#define TRC_CT_PROTOCOL (1<<(0)) +#define TRC_CT_NETWORK (1<<(1)) +#define TRC_CT_PROXY (1<<(2)) + +struct trc_feat_def { + struct curl_trc_feat *feat; + unsigned int category; +}; + +static struct trc_feat_def trc_feats[] = { + { &Curl_trc_feat_read, TRC_CT_NONE }, + { &Curl_trc_feat_write, TRC_CT_NONE }, #ifndef CURL_DISABLE_FTP - &Curl_trc_feat_ftp, + { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, #endif #ifndef CURL_DISABLE_DOH - &Curl_doh_trc, + { &Curl_doh_trc, TRC_CT_NETWORK }, +#endif +#ifndef CURL_DISABLE_SMTP + { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL }, #endif - NULL, +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) + { &Curl_trc_feat_ws, TRC_CT_PROTOCOL }, +#endif +}; + +struct trc_cft_def { + struct Curl_cftype *cft; + unsigned int category; }; -static struct Curl_cftype *cf_types[] = { - &Curl_cft_tcp, - &Curl_cft_udp, - &Curl_cft_unix, - &Curl_cft_tcp_accept, - &Curl_cft_happy_eyeballs, - &Curl_cft_setup, +static struct trc_cft_def trc_cfts[] = { + { &Curl_cft_tcp, TRC_CT_NETWORK }, + { &Curl_cft_udp, TRC_CT_NETWORK }, + { &Curl_cft_unix, TRC_CT_NETWORK }, + { &Curl_cft_tcp_accept, TRC_CT_NETWORK }, + { &Curl_cft_happy_eyeballs, TRC_CT_NETWORK }, + { &Curl_cft_setup, TRC_CT_PROTOCOL }, #ifdef USE_NGHTTP2 - &Curl_cft_nghttp2, + { &Curl_cft_nghttp2, TRC_CT_PROTOCOL }, #endif #ifdef USE_SSL - &Curl_cft_ssl, + { &Curl_cft_ssl, TRC_CT_NETWORK }, #ifndef CURL_DISABLE_PROXY - &Curl_cft_ssl_proxy, + { &Curl_cft_ssl_proxy, TRC_CT_PROXY }, #endif #endif #if !defined(CURL_DISABLE_PROXY) #if !defined(CURL_DISABLE_HTTP) - &Curl_cft_h1_proxy, + { &Curl_cft_h1_proxy, TRC_CT_PROXY }, #ifdef USE_NGHTTP2 - &Curl_cft_h2_proxy, + { &Curl_cft_h2_proxy, TRC_CT_PROXY }, #endif - &Curl_cft_http_proxy, + { &Curl_cft_http_proxy, TRC_CT_PROXY }, #endif /* !CURL_DISABLE_HTTP */ - &Curl_cft_haproxy, - &Curl_cft_socks_proxy, + { &Curl_cft_haproxy, TRC_CT_PROXY }, + { &Curl_cft_socks_proxy, TRC_CT_PROXY }, #endif /* !CURL_DISABLE_PROXY */ #ifdef USE_HTTP3 - &Curl_cft_http3, + { &Curl_cft_http3, TRC_CT_PROTOCOL }, #endif #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) - &Curl_cft_http_connect, + { &Curl_cft_http_connect, TRC_CT_PROTOCOL }, #endif - NULL, }; -CURLcode Curl_trc_opt(const char *config) +static void trc_apply_level_by_name(const char * const token, int lvl) +{ + size_t i; + + for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) { + if(strcasecompare(token, trc_cfts[i].cft->name)) { + trc_cfts[i].cft->log_level = lvl; + break; + } + } + for(i = 0; i < ARRAYSIZE(trc_feats); ++i) { + if(strcasecompare(token, trc_feats[i].feat->name)) { + trc_feats[i].feat->log_level = lvl; + break; + } + } +} + +static void trc_apply_level_by_category(int category, int lvl) { - char *token, *tok_buf, *tmp; size_t i; + + for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) { + if(!category || (trc_cfts[i].category & category)) + trc_cfts[i].cft->log_level = lvl; + } + for(i = 0; i < ARRAYSIZE(trc_feats); ++i) { + if(!category || (trc_feats[i].category & category)) + trc_feats[i].feat->log_level = lvl; + } +} + +static CURLcode trc_opt(const char *config) +{ + char *token, *tok_buf, *tmp; int lvl; tmp = strdup(config); @@ -285,42 +380,46 @@ CURLcode Curl_trc_opt(const char *config) lvl = CURL_LOG_LVL_INFO; break; } - for(i = 0; cf_types[i]; ++i) { - if(strcasecompare(token, "all")) { - cf_types[i]->log_level = lvl; - } - else if(strcasecompare(token, cf_types[i]->name)) { - cf_types[i]->log_level = lvl; - break; - } - } - for(i = 0; trc_feats[i]; ++i) { - if(strcasecompare(token, "all")) { - trc_feats[i]->log_level = lvl; - } - else if(strcasecompare(token, trc_feats[i]->name)) { - trc_feats[i]->log_level = lvl; - break; - } - } + if(strcasecompare(token, "all")) + trc_apply_level_by_category(TRC_CT_NONE, lvl); + else if(strcasecompare(token, "protocol")) + trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl); + else if(strcasecompare(token, "network")) + trc_apply_level_by_category(TRC_CT_NETWORK, lvl); + else if(strcasecompare(token, "proxy")) + trc_apply_level_by_category(TRC_CT_PROXY, lvl); + else + trc_apply_level_by_name(token, lvl); + token = strtok_r(NULL, ", ", &tok_buf); } free(tmp); return CURLE_OK; } -CURLcode Curl_trc_init(void) +CURLcode Curl_trc_opt(const char *config) { + CURLcode result = config? trc_opt(config) : CURLE_OK; #ifdef DEBUGBUILD - /* WIP: we use the auto-init from an env var only in DEBUG builds for - * convenience. */ - const char *config = getenv("CURL_DEBUG"); - if(config) { - return Curl_trc_opt(config); + /* CURL_DEBUG can override anything */ + if(!result) { + const char *dbg_config = getenv("CURL_DEBUG"); + if(dbg_config) + result = trc_opt(dbg_config); } #endif /* DEBUGBUILD */ + return result; +} + +CURLcode Curl_trc_init(void) +{ +#ifdef DEBUGBUILD + return Curl_trc_opt(NULL); +#else return CURLE_OK; +#endif } + #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ CURLcode Curl_trc_init(void) diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index 3d3801834..5f675b453 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h @@ -89,6 +89,16 @@ void Curl_failf(struct Curl_easy *data, do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \ Curl_trc_ftp(data, __VA_ARGS__); } while(0) #endif /* !CURL_DISABLE_FTP */ +#ifndef CURL_DISABLE_SMTP +#define CURL_TRC_SMTP(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \ + Curl_trc_smtp(data, __VA_ARGS__); } while(0) +#endif /* !CURL_DISABLE_SMTP */ +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#define CURL_TRC_WS(data, ...) \ + do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \ + Curl_trc_ws(data, __VA_ARGS__); } while(0) +#endif /* USE_WEBSOCKETS && !CURL_DISABLE_HTTP */ #else /* CURL_HAVE_C99 */ @@ -100,6 +110,12 @@ void Curl_failf(struct Curl_easy *data, #ifndef CURL_DISABLE_FTP #define CURL_TRC_FTP Curl_trc_ftp #endif +#ifndef CURL_DISABLE_SMTP +#define CURL_TRC_SMTP Curl_trc_smtp +#endif +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#define CURL_TRC_WS Curl_trc_ws +#endif #endif /* !CURL_HAVE_C99 */ @@ -148,6 +164,16 @@ extern struct curl_trc_feat Curl_trc_feat_ftp; void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); #endif +#ifndef CURL_DISABLE_SMTP +extern struct curl_trc_feat Curl_trc_feat_smtp; +void Curl_trc_smtp(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif +#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +extern struct curl_trc_feat Curl_trc_feat_ws; +void Curl_trc_ws(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */ @@ -194,6 +220,12 @@ static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) (void)data; (void)fmt; } #endif +#ifndef CURL_DISABLE_SMTP +static void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; (void)fmt; +} +#endif #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */ diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h index 54e427957..0391d7cd7 100644 --- a/Utilities/cmcurl/lib/curlx.h +++ b/Utilities/cmcurl/lib/curlx.h @@ -31,10 +31,8 @@ * be. */ -#include -/* this is still a public header file that provides the curl_mprintf() - functions while they still are offered publicly. They will be made library- - private one day */ +/* map standard printf functions to curl implementations */ +#include "curl_printf.h" #include "strcase.h" /* "strcase.h" provides the strcasecompare protos */ @@ -77,41 +75,4 @@ */ -#define curlx_mvsnprintf curl_mvsnprintf -#define curlx_msnprintf curl_msnprintf -#define curlx_maprintf curl_maprintf -#define curlx_mvaprintf curl_mvaprintf -#define curlx_msprintf curl_msprintf -#define curlx_mprintf curl_mprintf -#define curlx_mfprintf curl_mfprintf -#define curlx_mvsprintf curl_mvsprintf -#define curlx_mvprintf curl_mvprintf -#define curlx_mvfprintf curl_mvfprintf - -#ifdef ENABLE_CURLX_PRINTF -/* If this define is set, we define all "standard" printf() functions to use - the curlx_* version instead. It makes the source code transparent and - easier to understand/patch. Undefine them first. */ -# undef printf -# undef fprintf -# undef sprintf -# undef msnprintf -# undef vprintf -# undef vfprintf -# undef vsprintf -# undef mvsnprintf -# undef aprintf -# undef vaprintf - -# define printf curlx_mprintf -# define fprintf curlx_mfprintf -# define sprintf curlx_msprintf -# define msnprintf curlx_msnprintf -# define vprintf curlx_mvprintf -# define vfprintf curlx_mvfprintf -# define mvsnprintf curlx_mvsnprintf -# define aprintf curlx_maprintf -# define vaprintf curlx_mvaprintf -#endif /* ENABLE_CURLX_PRINTF */ - #endif /* HEADER_CURL_CURLX_H */ diff --git a/Utilities/cmcurl/lib/cw-out.c b/Utilities/cmcurl/lib/cw-out.c index 4e56c6a1b..56ec4162e 100644 --- a/Utilities/cmcurl/lib/cw-out.c +++ b/Utilities/cmcurl/lib/cw-out.c @@ -228,8 +228,8 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, if(CURL_WRITEFUNC_PAUSE == nwritten) { if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) { /* Protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the - transfer isn't done using the "normal" procedure. */ + actually only FILE:// just now, and it cannot pause since the + transfer is not done using the "normal" procedure. */ failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index a26a28b7b..7d9c18dc9 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c @@ -146,7 +146,7 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) for(;;) { /* Write the buffer to the socket */ - result = Curl_xfer_send(data, sptr, write_len, &bytes_written); + result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written); if(result) break; @@ -241,7 +241,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) failf(data, "Failed sending DICT request"); goto error; } - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */ } else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || @@ -287,7 +287,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) failf(data, "Failed sending DICT request"); goto error; } - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); } else { @@ -309,7 +309,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) goto error; } - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); } } diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index 03317ae27..52b357458 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c @@ -46,7 +46,7 @@ #define DNS_CLASS_IN 0x01 -/* local_print_buf truncates if the hex string will be more than this */ +/* doh_print_buf truncates if the hex string will be more than this */ #define LOCAL_PB_HEXMAX 400 #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -82,32 +82,32 @@ struct curl_trc_feat Curl_doh_trc = { /* @unittest 1655 */ -UNITTEST DOHcode doh_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen) /* output length */ +UNITTEST DOHcode doh_req_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen) /* output length */ { const size_t hostlen = strlen(host); unsigned char *orig = dnsp; const char *hostp = host; /* The expected output length is 16 bytes more than the length of - * the QNAME-encoding of the host name. + * the QNAME-encoding of the hostname. * * A valid DNS name may not contain a zero-length label, except at - * the end. For this reason, a name beginning with a dot, or + * the end. For this reason, a name beginning with a dot, or * containing a sequence of two or more consecutive dots, is invalid * and cannot be encoded as a QNAME. * - * If the host name ends with a trailing dot, the corresponding - * QNAME-encoding is one byte longer than the host name. If (as is + * If the hostname ends with a trailing dot, the corresponding + * QNAME-encoding is one byte longer than the hostname. If (as is * also valid) the hostname is shortened by the omission of the * trailing dot, then its QNAME-encoding will be two bytes longer - * than the host name. + * than the hostname. * * Each [ label, dot ] pair is encoded as [ length, label ], - * preserving overall length. A final [ label ] without a dot is + * preserving overall length. A final [ label ] without a dot is * also encoded as [ length, label ], increasing overall length * by one. The encoding is completed by appending a zero byte, * representing the zero-length root label, again increasing @@ -191,10 +191,10 @@ doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) return realsize; } -#if defined(USE_HTTPSRR) && defined(CURLDEBUG) -static void local_print_buf(struct Curl_easy *data, - const char *prefix, - unsigned char *buf, size_t len) +#if defined(USE_HTTPSRR) && defined(DEBUGBUILD) +static void doh_print_buf(struct Curl_easy *data, + const char *prefix, + unsigned char *buf, size_t len) { unsigned char hexstr[LOCAL_PB_HEXMAX]; size_t hlen = LOCAL_PB_HEXMAX; @@ -214,19 +214,26 @@ static void local_print_buf(struct Curl_easy *data, /* called from multi.c when this DoH transfer is complete */ static int doh_done(struct Curl_easy *doh, CURLcode result) { - struct Curl_easy *data = doh->set.dohfor; - struct dohdata *dohp = data->req.doh; - /* so one of the DoH request done for the 'data' transfer is now complete! */ - dohp->pending--; - infof(doh, "a DoH request is completed, %u to go", dohp->pending); - if(result) - infof(doh, "DoH request %s", curl_easy_strerror(result)); + struct Curl_easy *data; /* the transfer that asked for the DoH probe */ + + data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid); + if(!data) { + DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T + " not found", doh->set.dohfor_mid)); + DEBUGASSERT(0); + } + else { + struct doh_probes *dohp = data->req.doh; + /* one of the DoH request done for the 'data' transfer is now complete! */ + dohp->pending--; + infof(doh, "a DoH request is completed, %u to go", dohp->pending); + if(result) + infof(doh, "DoH request %s", curl_easy_strerror(result)); - if(!dohp->pending) { - /* DoH completed */ - curl_slist_free_all(dohp->headers); - dohp->headers = NULL; - Curl_expire(data, 0, EXPIRE_RUN_NOW); + if(!dohp->pending) { + /* DoH completed, run the transfer picking up the results */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } } return 0; } @@ -240,24 +247,24 @@ do { \ goto error; \ } while(0) -static CURLcode dohprobe(struct Curl_easy *data, - struct dnsprobe *p, DNStype dnstype, - const char *host, - const char *url, CURLM *multi, - struct curl_slist *headers) +static CURLcode doh_run_probe(struct Curl_easy *data, + struct doh_probe *p, DNStype dnstype, + const char *host, + const char *url, CURLM *multi, + struct curl_slist *headers) { struct Curl_easy *doh = NULL; CURLcode result = CURLE_OK; timediff_t timeout_ms; - DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), - &p->dohlen); + DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body), + &p->req_body_len); if(d) { failf(data, "Failed to encode DoH packet [%d]", d); return CURLE_OUT_OF_MEMORY; } p->dnstype = dnstype; - Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); + Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE); timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms <= 0) { @@ -266,126 +273,126 @@ static CURLcode dohprobe(struct Curl_easy *data, } /* Curl_open() is the internal version of curl_easy_init() */ result = Curl_open(&doh); - if(!result) { - /* pass in the struct pointer via a local variable to please coverity and - the gcc typecheck helpers */ - struct dynbuf *resp = &p->serverdoh; - doh->state.internal = true; + if(result) + goto error; + + /* pass in the struct pointer via a local variable to please coverity and + the gcc typecheck helpers */ + doh->state.internal = true; #ifndef CURL_DISABLE_VERBOSE_STRINGS - doh->state.feat = &Curl_doh_trc; + doh->state.feat = &Curl_doh_trc; #endif - 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); - ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); - ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); + 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, &p->resp_body); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body); + ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len); + ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); #ifdef USE_HTTP2 - ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); - ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); + ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L); #endif -#ifndef CURLDEBUG - /* enforce HTTPS if not debug */ - ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); +#ifndef DEBUGBUILD + /* enforce HTTPS if not debug */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); #else - /* in debug mode, also allow http */ - ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); + /* in debug mode, also allow http */ + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); #endif - ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); - ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); - if(data->set.err && data->set.err != stderr) - ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); - if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) - ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); - if(data->set.no_signal) - ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); - - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, - data->set.doh_verifyhost ? 2L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, - data->set.doh_verifypeer ? 1L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, - data->set.doh_verifystatus ? 1L : 0L); - - /* Inherit *some* SSL options from the user's transfer. This is a - best-guess as to which options are needed for compatibility. #3661 - - Note DoH does not inherit the user's proxy server so proxy SSL settings - have no effect and are not inherited. If that changes then two new - options should be added to check doh proxy insecure separately, - CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. - */ - if(data->set.ssl.falsestart) - ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); - if(data->set.str[STRING_SSL_CAFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO, - data->set.str[STRING_SSL_CAFILE]); - } - if(data->set.blobs[BLOB_CAINFO]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, - data->set.blobs[BLOB_CAINFO]); - } - if(data->set.str[STRING_SSL_CAPATH]) { - ERROR_CHECK_SETOPT(CURLOPT_CAPATH, - data->set.str[STRING_SSL_CAPATH]); - } - if(data->set.str[STRING_SSL_CRLFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, - data->set.str[STRING_SSL_CRLFILE]); - } - if(data->set.ssl.certinfo) - ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); - if(data->set.ssl.fsslctx) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); - if(data->set.ssl.fsslctxp) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); - if(data->set.fdebug) - ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug); - if(data->set.debugdata) - ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata); - if(data->set.str[STRING_SSL_EC_CURVES]) { - ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, - data->set.str[STRING_SSL_EC_CURVES]); - } + ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); + ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); + if(data->set.err && data->set.err != stderr) + ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); + if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) + ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); + if(data->set.no_signal) + ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); + + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, + data->set.doh_verifyhost ? 2L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, + data->set.doh_verifypeer ? 1L : 0L); + ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, + data->set.doh_verifystatus ? 1L : 0L); + + /* Inherit *some* SSL options from the user's transfer. This is a + best-guess as to which options are needed for compatibility. #3661 + + Note DoH does not inherit the user's proxy server so proxy SSL settings + have no effect and are not inherited. If that changes then two new + options should be added to check doh proxy insecure separately, + CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. + */ + if(data->set.ssl.falsestart) + ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); + if(data->set.str[STRING_SSL_CAFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO, + data->set.str[STRING_SSL_CAFILE]); + } + if(data->set.blobs[BLOB_CAINFO]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, + data->set.blobs[BLOB_CAINFO]); + } + if(data->set.str[STRING_SSL_CAPATH]) { + ERROR_CHECK_SETOPT(CURLOPT_CAPATH, + data->set.str[STRING_SSL_CAPATH]); + } + if(data->set.str[STRING_SSL_CRLFILE]) { + ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, + data->set.str[STRING_SSL_CRLFILE]); + } + if(data->set.ssl.certinfo) + ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); + if(data->set.ssl.fsslctx) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); + if(data->set.ssl.fsslctxp) + ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); + if(data->set.fdebug) + ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug); + if(data->set.debugdata) + ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata); + if(data->set.str[STRING_SSL_EC_CURVES]) { + ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, + data->set.str[STRING_SSL_EC_CURVES]); + } - { - long mask = - (data->set.ssl.enable_beast ? - CURLSSLOPT_ALLOW_BEAST : 0) | - (data->set.ssl.no_revoke ? - CURLSSLOPT_NO_REVOKE : 0) | - (data->set.ssl.no_partialchain ? - CURLSSLOPT_NO_PARTIALCHAIN : 0) | - (data->set.ssl.revoke_best_effort ? - CURLSSLOPT_REVOKE_BEST_EFFORT : 0) | - (data->set.ssl.native_ca_store ? - CURLSSLOPT_NATIVE_CA : 0) | - (data->set.ssl.auto_client_cert ? - CURLSSLOPT_AUTO_CLIENT_CERT : 0); - - (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask); - } + { + long mask = + (data->set.ssl.enable_beast ? + CURLSSLOPT_ALLOW_BEAST : 0) | + (data->set.ssl.no_revoke ? + CURLSSLOPT_NO_REVOKE : 0) | + (data->set.ssl.no_partialchain ? + CURLSSLOPT_NO_PARTIALCHAIN : 0) | + (data->set.ssl.revoke_best_effort ? + CURLSSLOPT_REVOKE_BEST_EFFORT : 0) | + (data->set.ssl.native_ca_store ? + CURLSSLOPT_NATIVE_CA : 0) | + (data->set.ssl.auto_client_cert ? + CURLSSLOPT_AUTO_CLIENT_CERT : 0); + + (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask); + } - doh->set.fmultidone = doh_done; - doh->set.dohfor = data; /* identify for which transfer this is done */ - p->easy = doh; + doh->set.fmultidone = doh_done; + doh->set.dohfor_mid = data->mid; /* for which transfer this is done */ - /* DoH handles must not inherit private_data. The handles may be passed to - the user via callbacks and the user will be able to identify them as - internal handles because private data is not set. The user can then set - private_data via CURLOPT_PRIVATE if they so choose. */ - DEBUGASSERT(!doh->set.private_data); + /* DoH handles must not inherit private_data. The handles may be passed to + the user via callbacks and the user will be able to identify them as + internal handles because private data is not set. The user can then set + private_data via CURLOPT_PRIVATE if they so choose. */ + DEBUGASSERT(!doh->set.private_data); - if(curl_multi_add_handle(multi, doh)) - goto error; - } - else + if(curl_multi_add_handle(multi, doh)) goto error; + + p->easy_mid = doh->mid; return CURLE_OK; error: Curl_close(&doh); + p->easy_mid = -1; return result; } @@ -400,9 +407,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, int *waitp) { CURLcode result = CURLE_OK; - int slot; - struct dohdata *dohp; + struct doh_probes *dohp; struct connectdata *conn = data->conn; + size_t i; #ifdef USE_HTTPSRR /* for now, this is only used when ECH is enabled */ # ifdef USE_ECH @@ -417,23 +424,27 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, DEBUGASSERT(conn); /* start clean, consider allocating this struct on demand */ - dohp = data->req.doh = calloc(1, sizeof(struct dohdata)); + dohp = data->req.doh = calloc(1, sizeof(struct doh_probes)); if(!dohp) return NULL; + for(i = 0; i < DOH_SLOT_COUNT; ++i) { + dohp->probe[i].easy_mid = -1; + } + conn->bits.doh = TRUE; dohp->host = hostname; dohp->port = port; - dohp->headers = + dohp->req_hds = curl_slist_append(NULL, "Content-Type: application/dns-message"); - if(!dohp->headers) + if(!dohp->req_hds) goto error; /* create IPv4 DoH request */ - result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4], - DNS_TYPE_A, hostname, data->set.str[STRING_DOH], - data->multi, dohp->headers); + result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4], + DNS_TYPE_A, hostname, data->set.str[STRING_DOH], + data->multi, dohp->req_hds); if(result) goto error; dohp->pending++; @@ -441,9 +452,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, #ifdef USE_IPV6 if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* create IPv6 DoH request */ - result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], - DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], - data->multi, dohp->headers); + result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6], + DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], + data->multi, dohp->req_hds); if(result) goto error; dohp->pending++; @@ -455,9 +466,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, * TODO: Figure out the conditions under which we want to make * a request for an HTTPS RR when we are not doing ECH. For now, * making this request breaks a bunch of DoH tests, e.g. test2100, - * where the additional request doesn't match the pre-cooked data - * files, so there's a bit of work attached to making the request - * in a non-ECH use-case. For the present, we'll only make the + * where the additional request does not match the pre-cooked data + * files, so there is a bit of work attached to making the request + * in a non-ECH use-case. For the present, we will only make the * request when ECH is enabled in the build and is being used for * the curl operation. */ @@ -470,10 +481,10 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, qname = aprintf("_%d._https.%s", port, hostname); if(!qname) goto error; - result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS], - DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH], - data->multi, dohp->headers); - free(qname); + result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR], + DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH], + data->multi, dohp->req_hds); + Curl_safefree(qname); if(result) goto error; dohp->pending++; @@ -484,18 +495,12 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, return NULL; error: - curl_slist_free_all(dohp->headers); - data->req.doh->headers = NULL; - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); - Curl_close(&dohp->probe[slot].easy); - } - Curl_safefree(data->req.doh); + Curl_doh_cleanup(data); return NULL; } -static DOHcode skipqname(const unsigned char *doh, size_t dohlen, - unsigned int *indexp) +static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen, + unsigned int *indexp) { unsigned char length; do { @@ -518,12 +523,13 @@ static DOHcode skipqname(const unsigned char *doh, size_t dohlen, return DOH_OK; } -static unsigned short get16bit(const unsigned char *doh, int index) +static unsigned short doh_get16bit(const unsigned char *doh, + unsigned int index) { return (unsigned short)((doh[index] << 8) | doh[index + 1]); } -static unsigned int get32bit(const unsigned char *doh, int index) +static unsigned int doh_get32bit(const unsigned char *doh, unsigned int index) { /* make clang and gcc optimize this to bswap by incrementing the pointer first. */ @@ -531,12 +537,13 @@ static unsigned int get32bit(const unsigned char *doh, int index) /* avoid undefined behavior by casting to unsigned before shifting 24 bits, possibly into the sign bit. codegen is same, but - ub sanitizer won't be upset */ + ub sanitizer will not be upset */ return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) | ((unsigned)doh[2] << 8) | doh[3]; } -static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) +static void doh_store_a(const unsigned char *doh, int index, + struct dohentry *d) { /* silently ignore addresses over the limit */ if(d->numaddr < DOH_MAX_ADDR) { @@ -545,12 +552,10 @@ static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) memcpy(&a->ip.v4, &doh[index], 4); d->numaddr++; } - return DOH_OK; } -static DOHcode store_aaaa(const unsigned char *doh, - int index, - struct dohentry *d) +static void doh_store_aaaa(const unsigned char *doh, int index, + struct dohentry *d) { /* silently ignore addresses over the limit */ if(d->numaddr < DOH_MAX_ADDR) { @@ -559,14 +564,11 @@ static DOHcode store_aaaa(const unsigned char *doh, memcpy(&a->ip.v6, &doh[index], 16); d->numaddr++; } - return DOH_OK; } #ifdef USE_HTTPSRR -static DOHcode store_https(const unsigned char *doh, - int index, - struct dohentry *d, - uint16_t len) +static DOHcode doh_store_https(const unsigned char *doh, int index, + struct dohentry *d, uint16_t len) { /* silently ignore RRs over the limit */ if(d->numhttps_rrs < DOH_MAX_HTTPS) { @@ -581,10 +583,8 @@ static DOHcode store_https(const unsigned char *doh, } #endif -static DOHcode store_cname(const unsigned char *doh, - size_t dohlen, - unsigned int index, - struct dohentry *d) +static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen, + unsigned int index, struct dohentry *d) { struct dynbuf *c; unsigned int loop = 128; /* a valid DNS name can never loop this much */ @@ -606,7 +606,7 @@ static DOHcode store_cname(const unsigned char *doh, /* move to the new index */ newpos = (length & 0x3f) << 8 | doh[index + 1]; - index = newpos; + index = (unsigned int)newpos; continue; } else if(length & 0xc0) @@ -633,12 +633,12 @@ static DOHcode store_cname(const unsigned char *doh, return DOH_OK; } -static DOHcode rdata(const unsigned char *doh, - size_t dohlen, - unsigned short rdlength, - unsigned short type, - int index, - struct dohentry *d) +static DOHcode doh_rdata(const unsigned char *doh, + size_t dohlen, + unsigned short rdlength, + unsigned short type, + int index, + struct dohentry *d) { /* RDATA - A (TYPE 1): 4 bytes @@ -651,26 +651,22 @@ static DOHcode rdata(const unsigned char *doh, case DNS_TYPE_A: if(rdlength != 4) return DOH_DNS_RDATA_LEN; - rc = store_a(doh, index, d); - if(rc) - return rc; + doh_store_a(doh, index, d); break; case DNS_TYPE_AAAA: if(rdlength != 16) return DOH_DNS_RDATA_LEN; - rc = store_aaaa(doh, index, d); - if(rc) - return rc; + doh_store_aaaa(doh, index, d); break; #ifdef USE_HTTPSRR case DNS_TYPE_HTTPS: - rc = store_https(doh, index, d, rdlength); + rc = doh_store_https(doh, index, d, rdlength); if(rc) return rc; break; #endif case DNS_TYPE_CNAME: - rc = store_cname(doh, dohlen, index, d); + rc = doh_store_cname(doh, dohlen, (unsigned int)index, d); if(rc) return rc; break; @@ -694,10 +690,10 @@ UNITTEST void de_init(struct dohentry *de) } -UNITTEST DOHcode doh_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d) +UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d) { unsigned char rcode; unsigned short qdcount; @@ -717,9 +713,9 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(rcode) return DOH_DNS_BAD_RCODE; /* bad rcode */ - qdcount = get16bit(doh, 4); + qdcount = doh_get16bit(doh, 4); while(qdcount) { - rc = skipqname(doh, dohlen, &index); + rc = doh_skipqname(doh, dohlen, &index); if(rc) return rc; /* bad qname */ if(dohlen < (index + 4)) @@ -728,19 +724,19 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, qdcount--; } - ancount = get16bit(doh, 6); + ancount = doh_get16bit(doh, 6); while(ancount) { unsigned short class; unsigned int ttl; - rc = skipqname(doh, dohlen, &index); + rc = doh_skipqname(doh, dohlen, &index); if(rc) return rc; /* bad qname */ if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - type = get16bit(doh, index); + type = doh_get16bit(doh, index); if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */ && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */ && (type != dnstype)) @@ -750,7 +746,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - class = get16bit(doh, index); + class = doh_get16bit(doh, index); if(DNS_CLASS_IN != class) return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ index += 2; @@ -758,7 +754,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(dohlen < (index + 4)) return DOH_DNS_OUT_OF_RANGE; - ttl = get32bit(doh, index); + ttl = doh_get32bit(doh, index); if(ttl < d->ttl) d->ttl = ttl; index += 4; @@ -766,21 +762,21 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - rdlength = get16bit(doh, index); + rdlength = doh_get16bit(doh, index); index += 2; if(dohlen < (index + rdlength)) return DOH_DNS_OUT_OF_RANGE; - rc = rdata(doh, dohlen, rdlength, type, index, d); + rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d); if(rc) - return rc; /* bad rdata */ + return rc; /* bad doh_rdata */ index += rdlength; ancount--; } - nscount = get16bit(doh, 8); + nscount = doh_get16bit(doh, 8); while(nscount) { - rc = skipqname(doh, dohlen, &index); + rc = doh_skipqname(doh, dohlen, &index); if(rc) return rc; /* bad qname */ @@ -792,7 +788,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - rdlength = get16bit(doh, index); + rdlength = doh_get16bit(doh, index); index += 2; if(dohlen < (index + rdlength)) return DOH_DNS_OUT_OF_RANGE; @@ -800,9 +796,9 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, nscount--; } - arcount = get16bit(doh, 10); + arcount = doh_get16bit(doh, 10); while(arcount) { - rc = skipqname(doh, dohlen, &index); + rc = doh_skipqname(doh, dohlen, &index); if(rc) return rc; /* bad qname */ @@ -814,7 +810,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - rdlength = get16bit(doh, index); + rdlength = doh_get16bit(doh, index); index += 2; if(dohlen < (index + rdlength)) return DOH_DNS_OUT_OF_RANGE; @@ -837,8 +833,8 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh, } #ifndef CURL_DISABLE_VERBOSE_STRINGS -static void showdoh(struct Curl_easy *data, - const struct dohentry *d) +static void doh_show(struct Curl_easy *data, + const struct dohentry *d) { int i; infof(data, "[DoH] TTL: %u seconds", d->ttl); @@ -870,9 +866,9 @@ static void showdoh(struct Curl_easy *data, } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) { -# ifdef CURLDEBUG - local_print_buf(data, "DoH HTTPS", - d->https_rrs[i].val, d->https_rrs[i].len); +# ifdef DEBUGBUILD + doh_print_buf(data, "DoH HTTPS", + d->https_rrs[i].val, d->https_rrs[i].len); # else infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); # endif @@ -883,7 +879,7 @@ static void showdoh(struct Curl_easy *data, } } #else -#define showdoh(x,y) +#define doh_show(x,y) #endif /* @@ -891,11 +887,11 @@ static void showdoh(struct Curl_easy *data, * * This function returns a pointer to the first element of a newly allocated * Curl_addrinfo struct linked list filled with the data from a set of DoH - * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for + * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for * a IPv6 stack, but usable also for IPv4, all hosts and environments. * * The memory allocated by this function *MUST* be free'd later on calling - * Curl_freeaddrinfo(). For each successful call to this function there + * Curl_freeaddrinfo(). For each successful call to this function there * must be an associated call later to Curl_freeaddrinfo(). */ @@ -923,7 +919,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, CURL_SA_FAMILY_T addrtype; if(de->addr[i].type == DNS_TYPE_AAAA) { #ifndef USE_IPV6 - /* we can't handle IPv6 addresses */ + /* we cannot handle IPv6 addresses */ continue; #else ss_size = sizeof(struct sockaddr_in6); @@ -967,7 +963,11 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); +#ifdef __MINGW32__ + addr->sin_family = (short)addrtype; +#else addr->sin_family = addrtype; +#endif addr->sin_port = htons((unsigned short)port); break; @@ -976,7 +976,11 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addr6 = (void *)ai->ai_addr; /* storage area for this info */ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); +#ifdef __MINGW32__ + addr6->sin6_family = (short)addrtype; +#else addr6->sin6_family = addrtype; +#endif addr6->sin6_port = htons((unsigned short)port); break; #endif @@ -995,7 +999,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, } #ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *type2name(DNStype dnstype) +static const char *doh_type2name(DNStype dnstype) { switch(dnstype) { case DNS_TYPE_A: @@ -1020,7 +1024,7 @@ UNITTEST void de_cleanup(struct dohentry *d) } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) - free(d->https_rrs[i].val); + Curl_safefree(d->https_rrs[i].val); #endif } @@ -1038,10 +1042,10 @@ UNITTEST void de_cleanup(struct dohentry *d) * * The input buffer pointer will be modified so it points to * just after the end of the DNS name encoding on output. (And - * that's why it's an "unsigned char **" :-) + * that is why it is an "unsigned char **" :-) */ -static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining, - char **dnsname) +static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining, + char **dnsname) { unsigned char *cp = NULL; int rem = 0; @@ -1087,8 +1091,8 @@ static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining, return CURLE_OK; } -static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len, - char **alpns) +static CURLcode doh_decode_rdata_alpn(unsigned char *rrval, size_t len, + char **alpns) { /* * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1 @@ -1097,7 +1101,7 @@ static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len, * output is comma-sep list of the strings * implementations may or may not handle quoting of comma within * string values, so we might see a comma within the wire format - * version of a string, in which case we'll precede that by a + * version of a string, in which case we will precede that by a * backslash - same goes for a backslash character, and of course * we need to use two backslashes in strings when we mean one;-) */ @@ -1143,10 +1147,10 @@ err: return CURLE_BAD_CONTENT_ENCODING; } -#ifdef CURLDEBUG -static CURLcode test_alpn_escapes(void) +#ifdef DEBUGBUILD +static CURLcode doh_test_alpn_escapes(void) { - /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */ + /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */ static unsigned char example[] = { 0x08, /* length 8 */ 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */ @@ -1157,7 +1161,7 @@ static CURLcode test_alpn_escapes(void) char *aval = NULL; static const char *expected = "f\\\\oo\\,bar,h2"; - if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK) + if(doh_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK) return CURLE_BAD_CONTENT_ENCODING; if(strlen(aval) != strlen(expected)) return CURLE_BAD_CONTENT_ENCODING; @@ -1167,7 +1171,7 @@ static CURLcode test_alpn_escapes(void) } #endif -static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, +static CURLcode doh_resp_decode_httpsrr(unsigned char *rrval, size_t len, struct Curl_https_rrinfo **hrr) { size_t remaining = len; @@ -1176,9 +1180,9 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, struct Curl_https_rrinfo *lhrr = NULL; char *dnsname = NULL; -#ifdef CURLDEBUG - /* a few tests of escaping, shouldn't be here but ok for now */ - if(test_alpn_escapes() != CURLE_OK) +#ifdef DEBUGBUILD + /* a few tests of escaping, should not be here but ok for now */ + if(doh_test_alpn_escapes() != CURLE_OK) return CURLE_OUT_OF_MEMORY; #endif lhrr = calloc(1, sizeof(struct Curl_https_rrinfo)); @@ -1193,7 +1197,7 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]); cp += 2; remaining -= (uint16_t)2; - if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK) + if(doh_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK) goto err; lhrr->target = dnsname; while(remaining >= 4) { @@ -1203,24 +1207,30 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, cp += 2; remaining -= 4; if(pcode == HTTPS_RR_CODE_ALPN) { - if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK) + if(doh_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK) goto err; } if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN) lhrr->no_def_alpn = TRUE; else if(pcode == HTTPS_RR_CODE_IPV4) { + if(!plen) + goto err; lhrr->ipv4hints = Curl_memdup(cp, plen); if(!lhrr->ipv4hints) goto err; lhrr->ipv4hints_len = (size_t)plen; } else if(pcode == HTTPS_RR_CODE_ECH) { + if(!plen) + goto err; lhrr->echconfiglist = Curl_memdup(cp, plen); if(!lhrr->echconfiglist) goto err; lhrr->echconfiglist_len = (size_t)plen; } else if(pcode == HTTPS_RR_CODE_IPV6) { + if(!plen) + goto err; lhrr->ipv6hints = Curl_memdup(cp, plen); if(!lhrr->ipv6hints) goto err; @@ -1236,17 +1246,18 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len, return CURLE_OK; err: if(lhrr) { - free(lhrr->target); - free(lhrr->echconfiglist); - free(lhrr->val); - free(lhrr); + Curl_safefree(lhrr->target); + Curl_safefree(lhrr->echconfiglist); + Curl_safefree(lhrr->val); + Curl_safefree(lhrr->alpns); + Curl_safefree(lhrr); } return CURLE_OUT_OF_MEMORY; } -# ifdef CURLDEBUG -static void local_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr) +# ifdef DEBUGBUILD +static void doh_print_httpsrr(struct Curl_easy *data, + struct Curl_https_rrinfo *hrr) { DEBUGASSERT(hrr); infof(data, "HTTPS RR: priority %d, target: %s", @@ -1260,20 +1271,20 @@ static void local_print_httpsrr(struct Curl_easy *data, else infof(data, "HTTPS RR: no_def_alpn not set"); if(hrr->ipv4hints) { - local_print_buf(data, "HTTPS RR: ipv4hints", - hrr->ipv4hints, hrr->ipv4hints_len); + doh_print_buf(data, "HTTPS RR: ipv4hints", + hrr->ipv4hints, hrr->ipv4hints_len); } else infof(data, "HTTPS RR: no ipv4hints"); if(hrr->echconfiglist) { - local_print_buf(data, "HTTPS RR: ECHConfigList", - hrr->echconfiglist, hrr->echconfiglist_len); + doh_print_buf(data, "HTTPS RR: ECHConfigList", + hrr->echconfiglist, hrr->echconfiglist_len); } else infof(data, "HTTPS RR: no ECHConfigList"); if(hrr->ipv6hints) { - local_print_buf(data, "HTTPS RR: ipv6hint", - hrr->ipv6hints, hrr->ipv6hints_len); + doh_print_buf(data, "HTTPS RR: ipv6hint", + hrr->ipv6hints, hrr->ipv6hints_len); } else infof(data, "HTTPS RR: no ipv6hints"); @@ -1286,63 +1297,53 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, struct Curl_dns_entry **dnsp) { CURLcode result; - struct dohdata *dohp = data->req.doh; + struct doh_probes *dohp = data->req.doh; *dnsp = NULL; /* defaults to no response */ if(!dohp) return CURLE_OUT_OF_MEMORY; - if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && - !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { + if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 && + dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) { failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { -#ifndef USE_HTTPSRR - DOHcode rc[DOH_PROBE_SLOTS] = { - DOH_OK, DOH_OK - }; -#else - DOHcode rc[DOH_PROBE_SLOTS] = { - DOH_OK, DOH_OK, DOH_OK - }; -#endif + DOHcode rc[DOH_SLOT_COUNT]; struct dohentry de; int slot; + + memset(rc, 0, sizeof(rc)); /* remove DoH handles from multi handle and close them */ - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); - Curl_close(&dohp->probe[slot].easy); - } + Curl_doh_close(data); /* parse the responses, create the struct and return it! */ de_init(&de); - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - struct dnsprobe *p = &dohp->probe[slot]; + for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { + struct doh_probe *p = &dohp->probe[slot]; if(!p->dnstype) continue; - rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh), - Curl_dyn_len(&p->serverdoh), - p->dnstype, - &de); - Curl_dyn_free(&p->serverdoh); + rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body), + Curl_dyn_len(&p->resp_body), + p->dnstype, &de); + Curl_dyn_free(&p->resp_body); #ifndef CURL_DISABLE_VERBOSE_STRINGS if(rc[slot]) { infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), - type2name(p->dnstype), dohp->host); + doh_type2name(p->dnstype), dohp->host); } #endif } /* next slot */ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ - if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) { + if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { /* we have an address, of one kind or other */ struct Curl_dns_entry *dns; struct Curl_addrinfo *ai; if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) { - infof(data, "[DoH] Host name: %s", dohp->host); - showdoh(data, &de); + infof(data, "[DoH] hostname: %s", dohp->host); + doh_show(data, &de); } result = doh2ai(&de, dohp->host, dohp->port, &ai); @@ -1355,7 +1356,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port); + dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -1375,15 +1376,15 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, #ifdef USE_HTTPSRR if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) { struct Curl_https_rrinfo *hrr = NULL; - result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len, + result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len, &hrr); if(result) { infof(data, "Failed to decode HTTPS RR"); return result; } infof(data, "Some HTTPS RR to process"); -# ifdef CURLDEBUG - local_print_httpsrr(data, hrr); +# ifdef DEBUGBUILD + doh_print_httpsrr(data, hrr); # endif (*dnsp)->hinfo = hrr; } @@ -1391,7 +1392,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, /* All done */ de_cleanup(&de); - Curl_safefree(data->req.doh); + Curl_doh_cleanup(data); return result; } /* !dohp->pending */ @@ -1400,4 +1401,43 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data, return CURLE_OK; } +void Curl_doh_close(struct Curl_easy *data) +{ + struct doh_probes *doh = data->req.doh; + if(doh && data->multi) { + struct Curl_easy *probe_data; + curl_off_t mid; + size_t slot; + for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { + mid = doh->probe[slot].easy_mid; + if(mid < 0) + continue; + doh->probe[slot].easy_mid = -1; + /* should have been called before data is removed from multi handle */ + DEBUGASSERT(data->multi); + probe_data = data->multi? Curl_multi_get_handle(data->multi, mid) : NULL; + if(!probe_data) { + DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%" + FMT_OFF_T " not found!", + doh->probe[slot].easy_mid)); + continue; + } + /* data->multi might already be reset at this time */ + curl_multi_remove_handle(data->multi, probe_data); + Curl_close(&probe_data); + } + } +} + +void Curl_doh_cleanup(struct Curl_easy *data) +{ + struct doh_probes *doh = data->req.doh; + if(doh) { + Curl_doh_close(data); + curl_slist_free_all(doh->req_hds); + data->req.doh->req_hds = NULL; + Curl_safefree(data->req.doh); + } +} + #endif /* CURL_DISABLE_DOH */ diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index bb881ecc5..aae32a654 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h @@ -59,18 +59,39 @@ typedef enum { } DNStype; /* one of these for each DoH request */ -struct dnsprobe { - CURL *easy; +struct doh_probe { + curl_off_t easy_mid; /* multi id of easy handle doing the lookup */ DNStype dnstype; - unsigned char dohbuffer[512]; - size_t dohlen; - struct dynbuf serverdoh; + unsigned char req_body[512]; + size_t req_body_len; + struct dynbuf resp_body; }; -struct dohdata { - struct curl_slist *headers; - struct dnsprobe probe[DOH_PROBE_SLOTS]; - unsigned int pending; /* still outstanding requests */ +enum doh_slot_num { + /* Explicit values for first two symbols so as to match hard-coded + * constants in existing code + */ + DOH_SLOT_IPV4 = 0, /* make 'V4' stand out for readability */ + DOH_SLOT_IPV6 = 1, /* 'V6' likewise */ + + /* Space here for (possibly build-specific) additional slot definitions */ +#ifdef USE_HTTPSRR + DOH_SLOT_HTTPS_RR = 2, /* for HTTPS RR */ +#endif + + /* for example */ + /* #ifdef WANT_DOH_FOOBAR_TXT */ + /* DOH_PROBE_SLOT_FOOBAR_TXT, */ + /* #endif */ + + /* AFTER all slot definitions, establish how many we have */ + DOH_SLOT_COUNT +}; + +struct doh_probes { + struct curl_slist *req_hds; + struct doh_probe probe[DOH_SLOT_COUNT]; + unsigned int pending; /* still outstanding probes */ int port; const char *host; }; @@ -116,7 +137,7 @@ struct dohaddr { #define HTTPS_RR_CODE_IPV6 0x06 /* - * These may need escaping when found within an alpn string + * These may need escaping when found within an ALPN string * value. */ #define COMMA_CHAR ',' @@ -140,19 +161,22 @@ struct dohentry { #endif }; - -#ifdef DEBUGBUILD -DOHcode doh_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen); /* output length */ -DOHcode doh_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d); -void de_init(struct dohentry *d); -void de_cleanup(struct dohentry *d); +void Curl_doh_close(struct Curl_easy *data); +void Curl_doh_cleanup(struct Curl_easy *data); + +#ifdef UNITTESTS +UNITTEST DOHcode doh_req_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen); /* output length */ +UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); + +UNITTEST void de_init(struct dohentry *d); +UNITTEST void de_cleanup(struct dohentry *d); #endif extern struct curl_trc_feat Curl_doh_trc; diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c index 3b62eaf8a..eab07efbf 100644 --- a/Utilities/cmcurl/lib/dynbuf.c +++ b/Utilities/cmcurl/lib/dynbuf.c @@ -51,7 +51,7 @@ void Curl_dyn_init(struct dynbuf *s, size_t toobig) } /* - * free the buffer and re-init the necessary fields. It doesn't touch the + * free the buffer and re-init the necessary fields. It does not touch the * 'init' field and thus this buffer can be reused to add data to again. */ void Curl_dyn_free(struct dynbuf *s) @@ -71,7 +71,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, size_t a = s->allc; size_t fit = len + indx + 1; /* new string + old string + zero byte */ - /* try to detect if there's rubbish in the struct */ + /* try to detect if there is rubbish in the struct */ DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(s->toobig); DEBUGASSERT(indx < s->toobig); diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c index d7548959b..9153838e3 100644 --- a/Utilities/cmcurl/lib/dynhds.c +++ b/Utilities/cmcurl/lib/dynhds.c @@ -275,7 +275,7 @@ CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) return Curl_dynhds_h1_add_line(dynhds, line, line? strlen(line) : 0); } -#ifdef DEBUGBUILD +#ifdef UNITTESTS /* used by unit2602.c */ bool Curl_dynhds_contains(struct dynhds *dynhds, diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h index 3b536000a..fb162a30d 100644 --- a/Utilities/cmcurl/lib/dynhds.h +++ b/Utilities/cmcurl/lib/dynhds.h @@ -95,6 +95,9 @@ struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, size_t namelen); struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); +#ifdef UNITTESTS +/* used by unit2602.c */ + /** * Return TRUE iff one or more headers with the given name exist. */ @@ -115,20 +118,6 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds, */ size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); -/** - * Add a header, name + value, to `dynhds` at the end. Does *not* - * check for duplicate names. - */ -CURLcode Curl_dynhds_add(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); - -/** - * Add a header, c-string name + value, to `dynhds` at the end. - */ -CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, - const char *name, const char *value); - /** * Remove all entries with the given name. * Returns number of entries removed. @@ -146,19 +135,34 @@ size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); CURLcode Curl_dynhds_set(struct dynhds *dynhds, const char *name, size_t namelen, const char *value, size_t valuelen); +#endif CURLcode Curl_dynhds_cset(struct dynhds *dynhds, const char *name, const char *value); /** - * Add a single header from a HTTP/1.1 formatted line at the end. Line + * Add a header, name + value, to `dynhds` at the end. Does *not* + * check for duplicate names. + */ +CURLcode Curl_dynhds_add(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); + +/** + * Add a header, c-string name + value, to `dynhds` at the end. + */ +CURLcode Curl_dynhds_cadd(struct dynhds *dynhds, + const char *name, const char *value); + +/** + * Add a single header from an HTTP/1.1 formatted line at the end. Line * may contain a delimiting \r\n or just \n. Any characters after * that will be ignored. */ CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line); /** - * Add a single header from a HTTP/1.1 formatted line at the end. Line + * Add a single header from an HTTP/1.1 formatted line at the end. Line * may contain a delimiting \r\n or just \n. Any characters after * that will be ignored. */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index a04dbedd8..261445aee 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -113,6 +113,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #endif #if defined(_MSC_VER) && defined(_DLL) +# pragma warning(push) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ #endif @@ -130,7 +131,7 @@ curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; #endif #if defined(_MSC_VER) && defined(_DLL) -# pragma warning(default:4232) /* MSVC extension, dllimport identity */ +# pragma warning(pop) #endif #ifdef DEBUGBUILD @@ -242,7 +243,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, global_init_lock(); if(initialized) { - /* Already initialized, don't do it again, but bump the variable anyway to + /* Already initialized, do not do it again, but bump the variable anyway to work like curl_global_init() and require the same amount of cleanup calls. */ initialized++; @@ -268,7 +269,8 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, /** * curl_global_cleanup() globally cleanups curl, uses the value of - * "easy_init_flags" to determine what needs to be cleaned up and what doesn't. + * "easy_init_flags" to determine what needs to be cleaned up and what does + * not. */ void curl_global_cleanup(void) { @@ -374,7 +376,7 @@ struct Curl_easy *curl_easy_init(void) return data; } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD struct socketmonitor { struct socketmonitor *next; /* the next node in the list or NULL */ @@ -389,25 +391,22 @@ struct events { int running_handles; /* store the returned number */ }; +#define DEBUG_EV_POLL 0 + /* events_timer * * Callback that gets called with a new value when the timeout should be * updated. */ - static int events_timer(struct Curl_multi *multi, /* multi handle */ long timeout_ms, /* see above */ void *userp) /* private callback pointer */ { struct events *ev = userp; (void)multi; - if(timeout_ms == -1) - /* timeout removed */ - timeout_ms = 0; - else if(timeout_ms == 0) - /* timeout is already reached! */ - timeout_ms = 1; /* trigger asap */ - +#if DEBUG_EV_POLL + fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms); +#endif ev->ms = timeout_ms; ev->msbump = TRUE; return 0; @@ -461,6 +460,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ struct events *ev = userp; struct socketmonitor *m; struct socketmonitor *prev = NULL; + bool found = FALSE; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) easy; @@ -470,7 +470,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ m = ev->list; while(m) { if(m->socket.fd == s) { - + found = TRUE; if(what == CURL_POLL_REMOVE) { struct socketmonitor *nxt = m->next; /* remove this node from the list of monitored sockets */ @@ -479,15 +479,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ else ev->list = nxt; free(m); - m = nxt; - infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T - " REMOVED", s); + infof(easy, "socket cb: socket %" FMT_SOCKET_T " REMOVED", s); } else { /* The socket 's' is already being monitored, update the activity mask. Convert from libcurl bitmask to the poll one. */ m->socket.events = socketcb2poll(what); - infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T + infof(easy, "socket cb: socket %" FMT_SOCKET_T " UPDATED as %s%s", s, (what&CURL_POLL_IN)?"IN":"", (what&CURL_POLL_OUT)?"OUT":""); @@ -497,12 +495,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ prev = m; m = m->next; /* move to next node */ } - if(!m) { + + if(!found) { if(what == CURL_POLL_REMOVE) { - /* this happens a bit too often, libcurl fix perhaps? */ - /* fprintf(stderr, - "%s: socket %d asked to be REMOVED but not present!\n", - __func__, s); */ + /* should not happen if our logic is correct, but is no drama. */ + DEBUGF(infof(easy, "socket cb: asked to REMOVE socket %" + FMT_SOCKET_T "but not present!", s)); + DEBUGASSERT(0); } else { m = malloc(sizeof(struct socketmonitor)); @@ -512,8 +511,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ m->socket.events = socketcb2poll(what); m->socket.revents = 0; ev->list = m; - infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T - " ADDED as %s%s", s, + infof(easy, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s, (what&CURL_POLL_IN)?"IN":"", (what&CURL_POLL_OUT)?"OUT":""); } @@ -563,14 +561,15 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) int pollrc; int i; struct curltime before; - struct curltime after; /* populate the fds[] array */ for(m = ev->list, f = &fds[0]; m; m = m->next) { f->fd = m->socket.fd; f->events = m->socket.events; f->revents = 0; - /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ +#if DEBUG_EV_POLL + fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); +#endif f++; numfds++; } @@ -578,12 +577,27 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) /* get the time stamp to use to figure out how long poll takes */ before = Curl_now(); - /* wait for activity or timeout */ - pollrc = Curl_poll(fds, numfds, ev->ms); - if(pollrc < 0) - return CURLE_UNRECOVERABLE_POLL; - - after = Curl_now(); + if(numfds) { + /* wait for activity or timeout */ +#if DEBUG_EV_POLL + fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms); +#endif + pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms); +#if DEBUG_EV_POLL + fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n", + numfds, ev->ms, pollrc); +#endif + if(pollrc < 0) + return CURLE_UNRECOVERABLE_POLL; + } + else { +#if DEBUG_EV_POLL + fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms); +#endif + pollrc = 0; + if(ev->ms > 0) + Curl_wait_ms(ev->ms); + } ev->msbump = FALSE; /* reset here */ @@ -596,26 +610,37 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) } else { /* here pollrc is > 0 */ + struct Curl_llist_node *e = Curl_llist_head(&multi->process); + struct Curl_easy *data; + DEBUGASSERT(e); + data = Curl_node_elem(e); + DEBUGASSERT(data); /* loop over the monitored sockets to see which ones had activity */ for(i = 0; i< numfds; i++) { if(fds[i].revents) { /* socket activity, tell libcurl */ int act = poll2cselect(fds[i].revents); /* convert */ - infof(multi->easyp, - "call curl_multi_socket_action(socket " - "%" CURL_FORMAT_SOCKET_T ")", fds[i].fd); + + /* sending infof "randomly" to the first easy handle */ + infof(data, "call curl_multi_socket_action(socket " + "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd); mcode = curl_multi_socket_action(multi, fds[i].fd, act, &ev->running_handles); } } - if(!ev->msbump) { + + if(!ev->msbump && ev->ms >= 0) { /* If nothing updated the timeout, we decrease it by the spent time. * If it was updated, it has the new timeout time stored already. */ - timediff_t timediff = Curl_timediff(after, before); + timediff_t timediff = Curl_timediff(Curl_now(), before); if(timediff > 0) { +#if DEBUG_EV_POLL + fprintf(stderr, "poll timeout %ldms not updated, decrease by " + "time spent %ldms\n", ev->ms, (long)timediff); +#endif if(timediff > ev->ms) ev->ms = 0; else @@ -627,7 +652,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) if(mcode) return CURLE_URL_MALFORMAT; - /* we don't really care about the "msgs_in_queue" value returned in the + /* we do not really care about the "msgs_in_queue" value returned in the second argument */ msg = curl_multi_info_read(multi, &pollrc); if(msg) { @@ -648,15 +673,15 @@ static CURLcode easy_events(struct Curl_multi *multi) { /* this struct is made static to allow it to be used after this function returns and curl_multi_remove_handle() is called */ - static struct events evs = {2, FALSE, 0, NULL, 0}; + static struct events evs = {-1, FALSE, 0, NULL, 0}; /* if running event-based, do some further multi inits */ events_setup(multi, &evs); return wait_or_timeout(multi, &evs); } -#else /* CURLDEBUG */ -/* when not built with debug, this function doesn't exist */ +#else /* DEBUGBUILD */ +/* when not built with debug, this function does not exist */ #define easy_events(x) CURLE_NOT_BUILT_IN #endif @@ -706,7 +731,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi) * easy handle, destroys the multi handle and returns the easy handle's return * code. * - * REALITY: it can't just create and destroy the multi handle that easily. It + * REALITY: it cannot just create and destroy the multi handle that easily. It * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be reused so that the same pools and * caches can be used. @@ -763,12 +788,13 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* assign this after curl_multi_add_handle() */ data->multi_easy = multi; - sigpipe_ignore(data, &pipe_st); + sigpipe_init(&pipe_st); + sigpipe_apply(data, &pipe_st); /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); - /* ignoring the return code isn't nice, but atm we can't really handle + /* ignoring the return code is not nice, but atm we cannot really handle a failure here, room for future improvement! */ (void)curl_multi_remove_handle(multi, data); @@ -788,7 +814,7 @@ CURLcode curl_easy_perform(struct Curl_easy *data) return easy_perform(data, FALSE); } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD /* * curl_easy_perform_ev() is the external interface that performs a blocking * transfer using the event-based API internally. @@ -912,8 +938,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); - /* the connection cache is setup on demand */ - outcurl->state.conn_cache = NULL; + /* the connection pool is setup on demand */ outcurl->state.lastconnect_id = -1; outcurl->state.recent_conn_id = -1; outcurl->id = -1; @@ -1010,7 +1035,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) goto fail; } #endif /* USE_ARES */ - +#ifndef CURL_DISABLE_HTTP + Curl_llist_init(&outcurl->state.httphdrs, NULL); +#endif Curl_initinfo(outcurl); outcurl->magic = CURLEASY_MAGIC_NUMBER; @@ -1090,7 +1117,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) bool keep_changed, unpause_read, not_all_paused; if(!GOOD_EASY_HANDLE(data) || !data->conn) - /* crazy input, don't continue */ + /* crazy input, do not continue */ return CURLE_BAD_FUNCTION_ARGUMENT; if(Curl_is_in_callback(data)) @@ -1110,7 +1137,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) (data->mstate == MSTATE_PERFORMING || data->mstate == MSTATE_RATELIMITING)); /* Unpausing writes is detected on the next run in - * transfer.c:Curl_readwrite(). This is because this may result + * transfer.c:Curl_sendrecv(). This is because this may result * in a transfer error if the application's callbacks fail */ /* Set the new keepon state, so it takes effect no matter what error @@ -1142,6 +1169,11 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) goto out; } + if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { + Curl_conn_ev_data_pause(data, FALSE); + result = Curl_cwriter_unpause(data); + } + out: if(!result && !data->state.done && keep_changed) /* This transfer may have been moved in or out of the bundle, update the @@ -1257,7 +1289,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, Curl_attach_connection(data, c); sigpipe_ignore(data, &pipe_st); - result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, n); + result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); sigpipe_restore(&pipe_st); if(result && result != CURLE_AGAIN) @@ -1282,47 +1314,6 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, return result; } -/* - * Wrapper to call functions in Curl_conncache_foreach() - * - * Returns always 0. - */ -static int conn_upkeep(struct Curl_easy *data, - struct connectdata *conn, - void *param) -{ - struct curltime *now = param; - - if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) - return 0; - - /* briefly attach for action */ - Curl_attach_connection(data, conn); - if(conn->handler->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); - } - else { - /* Do the generic action on the FIRSTSOCKET filter chain */ - Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - } - Curl_detach_connection(data); - - conn->keepalive = *now; - return 0; /* continue iteration */ -} - -static CURLcode upkeep(struct conncache *conn_cache, void *data) -{ - struct curltime now = Curl_now(); - /* Loop over every connection and make connection alive. */ - Curl_conncache_foreach(data, - conn_cache, - &now, - conn_upkeep); - return CURLE_OK; -} - /* * Performs connection upkeep for the given session handle. */ @@ -1332,12 +1323,9 @@ CURLcode curl_easy_upkeep(struct Curl_easy *data) if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; - if(data->multi_easy) { - /* Use the common function to keep connections alive. */ - return upkeep(&data->multi_easy->conn_cache, data); - } - else { - /* No connections, so just return success */ - return CURLE_OK; - } + if(Curl_is_in_callback(data)) + return CURLE_RECURSIVE_API_CALL; + + /* Use the common function to keep connections alive. */ + return Curl_cpool_upkeep(data); } diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c index a0239a89f..86833bf6b 100644 --- a/Utilities/cmcurl/lib/easygetopt.c +++ b/Utilities/cmcurl/lib/easygetopt.c @@ -42,7 +42,7 @@ static struct curl_easyoption *lookup(const char *name, CURLoption id) } else { if((o->id == id) && !(o->flags & CURLOT_FLAG_ALIAS)) - /* don't match alias options */ + /* do not match alias options */ return o; } o++; diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h index 6ce3483c6..d77bb98f9 100644 --- a/Utilities/cmcurl/lib/easyif.h +++ b/Utilities/cmcurl/lib/easyif.h @@ -34,7 +34,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, CURLcode Curl_connect_only_attach(struct Curl_easy *data); #endif -#ifdef CURLDEBUG +#ifdef DEBUGBUILD CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy); #endif diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c index c79d13670..81091c405 100644 --- a/Utilities/cmcurl/lib/easyoptions.c +++ b/Utilities/cmcurl/lib/easyoptions.c @@ -328,6 +328,7 @@ struct curl_easyoption Curl_easyopts[] = { CURLOT_LONG, 0}, {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, {"TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0}, + {"TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CURLOT_LONG, 0}, {"TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0}, {"TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0}, {"TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0}, @@ -376,6 +377,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (325 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (326 + 1)); } #endif diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c index 5af00c351..9b6edb443 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c @@ -60,17 +60,18 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string, struct dynbuf d; (void)data; - if(inlength < 0) + if(!string || (inlength < 0)) return NULL; - Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3); - length = (inlength?(size_t)inlength:strlen(string)); if(!length) return strdup(""); + Curl_dyn_init(&d, length * 3 + 1); + while(length--) { - unsigned char in = *string++; /* treat the characters unsigned */ + /* treat the characters unsigned */ + unsigned char in = (unsigned char)*string++; if(ISUNRESERVED(in)) { /* append this */ @@ -137,7 +138,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, *ostring = ns; while(alloc) { - unsigned char in = *string; + unsigned char in = (unsigned char)*string; if(('%' == in) && (alloc > 2) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ @@ -157,7 +158,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, return CURLE_URL_MALFORMAT; } - *ns++ = in; + *ns++ = (char)in; } *ns = 0; /* terminate it */ @@ -180,7 +181,7 @@ char *curl_easy_unescape(struct Curl_easy *data, const char *string, { char *str = NULL; (void)data; - if(length >= 0) { + if(string && (length >= 0)) { size_t inputlen = (size_t)length; size_t outputlen; CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, @@ -222,8 +223,8 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ while(len-- && (olen >= 3)) { /* clang-tidy warns on this line without this comment: */ /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ - *out++ = hex[(*src & 0xF0)>>4]; - *out++ = hex[*src & 0x0F]; + *out++ = (unsigned char)hex[(*src & 0xF0)>>4]; + *out++ = (unsigned char)hex[*src & 0x0F]; ++src; olen -= 2; } diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index db860225b..01af52e72 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c @@ -147,7 +147,7 @@ static CURLcode file_setup_connection(struct Curl_easy *data, /* * file_connect() gets called from Curl_protocol_connect() to allow us to - * do protocol-specific actions at connect-time. We emulate a + * do protocol-specific actions at connect-time. We emulate a * connect-then-transfer protocol and "connect" to the file here */ static CURLcode file_connect(struct Curl_easy *data, bool *done) @@ -177,18 +177,18 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) return result; #ifdef DOS_FILESYSTEM - /* If the first character is a slash, and there's + /* If the first character is a slash, and there is something that looks like a drive at the beginning of - the path, skip the slash. If we remove the initial + the path, skip the slash. If we remove the initial slash in all cases, paths without drive letters end up - relative to the current directory which isn't how + relative to the current directory which is not how browsers work. Some browsers accept | instead of : as the drive letter separator, so we do too. On other platforms, we need the slash to indicate an - absolute pathname. On Windows, absolute paths start + absolute pathname. On Windows, absolute paths start with a drive letter. */ actual_path = real_path; @@ -223,7 +223,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) * 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 + * this first. Failing that, we treat the path as a real Unix * path, but only if the application was compiled with -lunix. */ fd = -1; @@ -308,7 +308,7 @@ static CURLcode file_upload(struct Curl_easy *data) bool eos = FALSE; /* - * Since FILE: doesn't do the full init, we need to provide some extra + * Since FILE: does not do the full init, we need to provide some extra * assignments here. */ @@ -331,7 +331,7 @@ static CURLcode file_upload(struct Curl_easy *data) fd = open(file->path, mode, data->set.new_file_perms); if(fd < 0) { - failf(data, "Can't open %s for writing", file->path); + failf(data, "cannot open %s for writing", file->path); return CURLE_WRITE_ERROR; } @@ -343,7 +343,7 @@ static CURLcode file_upload(struct Curl_easy *data) if(data->state.resume_from < 0) { if(fstat(fd, &file_stat)) { close(fd); - failf(data, "Can't get the size of %s", file->path); + failf(data, "cannot get the size of %s", file->path); return CURLE_WRITE_ERROR; } data->state.resume_from = (curl_off_t)file_stat.st_size; @@ -413,13 +413,13 @@ out: * file_do() is the protocol-specific function for the do-phase, separated * from the connect-phase above. Other protocols merely setup the transfer in * the do-phase, to have it done in the main transfer loop but since some - * platforms we support don't allow select()ing etc on file handles (as + * platforms we support do not allow select()ing etc on file handles (as * opposed to sockets) we instead perform the whole do-operation in this * function. */ static CURLcode file_do(struct Curl_easy *data, bool *done) { - /* This implementation ignores the host name in conformance with + /* This implementation ignores the hostname in conformance with RFC 1738. Only local files (reachable via the standard file system) are supported. This means that files on remotely mounted directories (via NFS, Samba, NT sharing) can be accessed through a file:// URL @@ -465,17 +465,17 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) const struct tm *tm = &buffer; char header[80]; int headerlen; - char accept_ranges[24]= { "Accept-ranges: bytes\r\n" }; + static const char accept_ranges[]= { "Accept-ranges: bytes\r\n" }; if(expected_size >= 0) { - headerlen = msnprintf(header, sizeof(header), - "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", - expected_size); + headerlen = + msnprintf(header, sizeof(header), "Content-Length: %" FMT_OFF_T "\r\n", + expected_size); result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); if(result) return result; result = Curl_client_write(data, CLIENTWRITE_HEADER, - accept_ranges, strlen(accept_ranges)); + accept_ranges, sizeof(accept_ranges) - 1); if(result != CURLE_OK) return result; } @@ -486,23 +486,26 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) return result; /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - headerlen = msnprintf(header, sizeof(header), - "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s", - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec, - data->req.no_body ? "": "\r\n"); + headerlen = + msnprintf(header, sizeof(header), + "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); + if(!result) + /* end of headers */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2); if(result) return result; /* set the file size to make it available post transfer */ Curl_pgrsSetDownloadSize(data, expected_size); if(data->req.no_body) - return result; + return CURLE_OK; } /* Check whether file range has been specified */ @@ -514,7 +517,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) * of the stream if the filesize could be determined */ if(data->state.resume_from < 0) { if(!fstated) { - failf(data, "Can't get the size of file."); + failf(data, "cannot get the size of file."); return CURLE_READ_ERROR; } data->state.resume_from += (curl_off_t)statbuf.st_size; @@ -522,7 +525,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(data->state.resume_from > 0) { /* We check explicitly if we have a start offset, because - * expected_size may be -1 if we don't know how large the file is, + * expected_size may be -1 if we do not know how large the file is, * in which case we should not adjust it. */ if(data->state.resume_from <= expected_size) expected_size -= data->state.resume_from; @@ -566,7 +569,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(!S_ISDIR(statbuf.st_mode)) { while(!result) { ssize_t nread; - /* Don't fill a whole buffer if we want less than all data */ + /* Do not fill a whole buffer if we want less than all data */ size_t bytestoread; if(size_known) { diff --git a/Utilities/cmcurl/lib/fileinfo.h b/Utilities/cmcurl/lib/fileinfo.h index ce009da06..0b3f56d9d 100644 --- a/Utilities/cmcurl/lib/fileinfo.h +++ b/Utilities/cmcurl/lib/fileinfo.h @@ -30,7 +30,7 @@ struct fileinfo { struct curl_fileinfo info; - struct Curl_llist_element list; + struct Curl_llist_node list; struct dynbuf buf; }; diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c index 0bdf2e11b..7373e0883 100644 --- a/Utilities/cmcurl/lib/fopen.c +++ b/Utilities/cmcurl/lib/fopen.c @@ -42,12 +42,12 @@ /* The dirslash() function breaks a null-terminated pathname string into directory and filename components then returns the directory component up - to, *AND INCLUDING*, a final '/'. If there is no directory in the path, + to, *AND INCLUDING*, a final '/'. If there is no directory in the path, this instead returns a "" string. This function returns a pointer to malloc'ed memory. - The input path to this function is expected to have a file name part. + The input path to this function is expected to have a filename part. */ #ifdef _WIN32 @@ -88,7 +88,7 @@ static char *dirslash(const char *path) * 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 + * 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, @@ -117,7 +117,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, dir = dirslash(filename); if(dir) { - /* The temp file name should not end up too long for the target file + /* The temp filename should not end up too long for the target file system */ tempstore = aprintf("%s%s.tmp", dir, randbuf); free(dir); diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index d6a1697aa..c260d442b 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -216,8 +216,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, struct curl_forms *forms = NULL; char *array_value = NULL; /* value read from an array */ - /* This is a state variable, that if TRUE means that we're parsing an - array that we got passed to us. If FALSE we're parsing the input + /* This is a state variable, that if TRUE means that we are parsing an + array that we got passed to us. If FALSE we are parsing the input va_list arguments. */ bool array_state = FALSE; @@ -260,7 +260,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, switch(option) { case CURLFORM_ARRAY: if(array_state) - /* we don't support an array from within an array */ + /* we do not support an array from within an array */ return_value = CURL_FORMADD_ILLEGAL_ARRAY; else { forms = va_arg(params, struct curl_forms *); @@ -327,7 +327,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, array_state?(curl_off_t)(size_t)array_value:va_arg(params, curl_off_t); break; - /* Get contents from a given file name */ + /* Get contents from a given filename */ case CURLFORM_FILECONTENT: if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) return_value = CURL_FORMADD_OPTION_TWICE; @@ -429,7 +429,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, array_state?array_value:va_arg(params, char *); if(userp) { current_form->userp = userp; - current_form->value = userp; /* this isn't strictly true but we + current_form->value = userp; /* this is not strictly true but we derive a value from this later on and we need this non-NULL to be accepted as a fine form part */ @@ -599,7 +599,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } if(!(form->flags & HTTPPOST_PTRNAME) && (form == first_form) ) { - /* Note that there's small risk that form->name is NULL here if the + /* Note that there is small risk that form->name is NULL here if the app passed in a bad combo, so we better check for that first. */ if(form->name) { /* copy name (without strdup; possibly not null-terminated) */ @@ -764,7 +764,7 @@ void curl_formfree(struct curl_httppost *form) ) free(form->contents); /* free the contents */ free(form->contenttype); /* free the content type */ - free(form->showfilename); /* free the faked file name */ + free(form->showfilename); /* free the faked filename */ free(form); /* free the struct */ form = next; } while(form); /* continue */ @@ -790,10 +790,10 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) /* wrap call to fseeko so it matches the calling convention of callback */ static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) { -#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) - return fseeko(stream, (off_t)offset, whence); -#elif defined(HAVE__FSEEKI64) +#if defined(HAVE__FSEEKI64) return _fseeki64(stream, (__int64)offset, whence); +#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) + return fseeko(stream, (off_t)offset, whence); #else if(offset > LONG_MAX) return -1; @@ -880,10 +880,10 @@ CURLcode Curl_getformdata(struct Curl_easy *data, if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) { if(!strcmp(file->contents, "-")) { - /* There are a few cases where the code below won't work; in + /* There are a few cases where the code below will not work; in particular, freopen(stdin) by the caller is not guaranteed to result as expected. This feature has been kept for backward - compatibility: use of "-" pseudo file name should be avoided. */ + compatibility: use of "-" pseudo filename should be avoided. */ result = curl_mime_data_cb(part, (curl_off_t) -1, (curl_read_callback) fread, fseeko_wrapper, @@ -915,7 +915,7 @@ CURLcode Curl_getformdata(struct Curl_easy *data, } } - /* Set fake file name. */ + /* Set fake filename. */ if(!result && post->showfilename) if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER | HTTPPOST_CALLBACK))) diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h index af466249f..2ed96ffcf 100644 --- a/Utilities/cmcurl/lib/formdata.h +++ b/Utilities/cmcurl/lib/formdata.h @@ -38,8 +38,8 @@ struct FormInfo { long flags; char *buffer; /* pointer to existing buffer used for file upload */ size_t bufferlength; - char *showfilename; /* The file name to show. If not set, the actual - file name will be used */ + char *showfilename; /* The filename to show. If not set, the actual + filename will be used */ char *userp; /* pointer for the read callback */ struct curl_slist *contentheader; struct FormInfo *more; diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 81ebb78a3..dab6e3e07 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -188,7 +188,7 @@ static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done); #ifndef CURL_DISABLE_VERBOSE_STRINGS static void ftp_pasv_verbose(struct Curl_easy *data, struct Curl_addrinfo *ai, - char *newhost, /* ascii version */ + char *newhost, /* ASCII version */ int port); #endif static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data); @@ -290,12 +290,11 @@ const struct Curl_handler Curl_handler_ftps = { }; #endif -static void close_secondarysocket(struct Curl_easy *data, - struct connectdata *conn) +static void close_secondarysocket(struct Curl_easy *data) { CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data)); Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } /* @@ -328,7 +327,7 @@ static void freedirs(struct ftp_conn *ftpc) Curl_safefree(ftpc->newhost); } -#ifdef CURL_DO_LINEEND_CONV +#ifdef CURL_PREFER_LF_LINEENDS /*********************************************************************** * * Lineend Conversions @@ -370,7 +369,6 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data, } /* either we just wrote the newline or it is part of the next * chunk of bytes we write. */ - data->state.crlf_conversions++; ctx->newline_pending = FALSE; } @@ -401,7 +399,6 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data, /* EndOfStream, if we have a trailing cr, now is the time to write it */ if(ctx->newline_pending) { ctx->newline_pending = FALSE; - data->state.crlf_conversions++; return Curl_cwriter_write(data, writer->next, type, &nl, 1); } /* Always pass on the EOS type indicator */ @@ -419,7 +416,7 @@ static const struct Curl_cwtype ftp_cw_lc = { sizeof(struct ftp_cw_lc_ctx) }; -#endif /* CURL_DO_LINEEND_CONV */ +#endif /* CURL_PREFER_LF_LINEENDS */ /*********************************************************************** * * AcceptServerConnect() @@ -475,7 +472,7 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) Curl_set_in_callback(data, false); if(error) { - close_secondarysocket(data, conn); + close_secondarysocket(data); return CURLE_ABORTED_BY_CALLBACK; } } @@ -649,19 +646,19 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) return result; if(conn->proto.ftpc.state_saved == FTP_STOR) { - /* When we know we're uploading a specified file, we can get the file + /* When we know we are uploading a specified file, we can get the file size prior to the actual upload. */ Curl_pgrsSetUploadSize(data, data->state.infilesize); /* set the SO_SNDBUF for the secondary socket for those who need it */ - Curl_sndbufset(conn->sock[SECONDARYSOCKET]); + Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]); - Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET); + Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE); } else { /* FTP download: */ - Curl_xfer_setup(data, SECONDARYSOCKET, - conn->proto.ftpc.retr_size_saved, FALSE, -1); + Curl_xfer_setup2(data, CURL_XFER_RECV, + conn->proto.ftpc.retr_size_saved, TRUE); } conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ @@ -674,7 +671,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) * * AllowServerConnect() * - * When we've issue the PORT command, we have told the server to connect to + * When we have issue the PORT command, we have told the server to connect to * us. This function checks whether data connection is established if so it is * accepted. * @@ -806,7 +803,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, { /* * We cannot read just one byte per read() and then go back to select() as - * the OpenSSL read() doesn't grok that properly. + * the OpenSSL read() does not grok that properly. * * Alas, read as much as possible, split up into lines, use the ending * line in a response or continue reading. */ @@ -820,6 +817,8 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, int cache_skip = 0; int value_to_be_ignored = 0; + CURL_TRC_FTP(data, "getFTPResponse start"); + if(ftpcode) *ftpcode = 0; /* 0 for errors */ else @@ -849,37 +848,43 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, * * A caution here is that the ftp_readresp() function has a cache that may * contain pieces of a response from the previous invoke and we need to - * make sure we don't just wait for input while there is unhandled data in + * make sure we do not just wait for input while there is unhandled data in * that cache. But also, if the cache is there, we call ftp_readresp() and - * the cache wasn't good enough to continue we must not just busy-loop + * the cache was not good enough to continue we must not just busy-loop * around this function. * */ if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) { /* - * There's a cache left since before. We then skipping the wait for + * There is a cache left since before. We then skipping the wait for * socket action, unless this is the same cache like the previous round * as then the cache was deemed not enough to act on and we then need to * wait for more data anyway. */ } else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { - switch(SOCKET_READABLE(sockfd, interval_ms)) { - case -1: /* select() error, stop reading */ + curl_socket_t wsock = Curl_pp_needs_flush(data, pp)? + sockfd : CURL_SOCKET_BAD; + int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms); + if(ev < 0) { failf(data, "FTP response aborted due to select/poll error: %d", SOCKERRNO); return CURLE_RECV_ERROR; - - case 0: /* timeout */ + } + else if(ev == 0) { if(Curl_pgrsUpdate(data)) return CURLE_ABORTED_BY_CALLBACK; continue; /* just continue in our loop for the timeout duration */ + } + } - default: /* for clarity */ + if(Curl_pp_needs_flush(data, pp)) { + result = Curl_pp_flushsend(data, pp); + if(result) break; - } } + result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread); if(result) break; @@ -895,9 +900,11 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, *nreadp += nread; - } /* while there's buffer left and loop is requested */ + } /* while there is buffer left and loop is requested */ pp->pending_resp = FALSE; + CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d", + result, *nreadp, *ftpcode); return result; } @@ -948,7 +955,7 @@ static int ftp_domore_getsock(struct Curl_easy *data, CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data)); if(FTP_STOP == ftpc->state) { - /* if stopped and still in this state, then we're also waiting for a + /* if stopped and still in this state, then we are also waiting for a connect on the secondary connection */ DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD || (conn->cfilter[SECONDARYSOCKET] && @@ -1043,7 +1050,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, int error; char *host = NULL; char *string_ftpport = data->set.str[STRING_FTPPORT]; - struct Curl_dns_entry *h = NULL; + struct Curl_dns_entry *dns_entry = NULL; unsigned short port_min = 0; unsigned short port_max = 0; unsigned short port; @@ -1136,13 +1143,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, #endif ipstr, hbuf, sizeof(hbuf))) { case IF2IP_NOT_FOUND: - /* not an interface, use the given string as host name instead */ + /* not an interface, use the given string as hostname instead */ host = ipstr; break; case IF2IP_AF_NOT_SUPPORTED: goto out; case IF2IP_FOUND: - host = hbuf; /* use the hbuf for host name */ + host = hbuf; /* use the hbuf for hostname */ break; } } @@ -1153,7 +1160,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!host) { const char *r; - /* not an interface and not a host name, get default by extracting + /* not an interface and not a hostname, get default by extracting the IP from the control connection */ sslen = sizeof(ss); if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { @@ -1174,20 +1181,17 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!r) { goto out; } - host = hbuf; /* use this host name */ + host = hbuf; /* use this hostname */ possibly_non_local = FALSE; /* we know it is local now */ } /* resolv ip/host to ip */ - rc = Curl_resolv(data, host, 0, FALSE, &h); + rc = Curl_resolv(data, host, 0, FALSE, &dns_entry); if(rc == CURLRESOLV_PENDING) - (void)Curl_resolver_wait_resolv(data, &h); - if(h) { - res = h->addr; - /* when we return from this function, we can forget about this entry - to we can unlock it now already */ - Curl_resolv_unlock(data, h); - } /* (h) */ + (void)Curl_resolver_wait_resolv(data, &dns_entry); + if(dns_entry) { + res = dns_entry->addr; + } else res = NULL; /* failure! */ @@ -1232,7 +1236,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* It failed. */ error = SOCKERRNO; if(possibly_non_local && (error == EADDRNOTAVAIL)) { - /* The requested bind address is not local. Use the address used for + /* The requested bind address is not local. Use the address used for * the control connection instead and restart the port loop */ infof(data, "bind(port=%hu) on non-local address failed: %s", port, @@ -1245,7 +1249,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, goto out; } port = port_min; - possibly_non_local = FALSE; /* don't try this again */ + possibly_non_local = FALSE; /* do not try this again */ continue; } if(error != EADDRINUSE && error != EACCES) { @@ -1355,7 +1359,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, char *dest = target; /* translate x.x.x.x to x,x,x,x */ - while(source && *source) { + while(*source) { if(*source == '.') *dest = ','; else @@ -1382,6 +1386,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, ftp_state(data, FTP_PORT); out: + /* If we looked up a dns_entry, now is the time to safely release it */ + if(dns_entry) + Curl_resolv_unlink(data, &dns_entry); if(result) { ftp_state(data, FTP_STOP); } @@ -1444,7 +1451,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) struct connectdata *conn = data->conn; if(ftp->transfer != PPTRANSFER_BODY) { - /* doesn't transfer any data */ + /* does not transfer any data */ /* still possibly do PRE QUOTE jobs */ ftp_state(data, FTP_RETR_PREQUOTE); @@ -1512,7 +1519,7 @@ static CURLcode ftp_state_size(struct Curl_easy *data, if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) { /* if a "head"-like request is being made (on a file) */ - /* we know ftpc->file is a valid pointer to a file name */ + /* we know ftpc->file is a valid pointer to a filename */ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); if(!result) ftp_state(data, FTP_SIZE); @@ -1590,13 +1597,13 @@ static CURLcode ftp_state_list(struct Curl_easy *data) static CURLcode ftp_state_retr_prequote(struct Curl_easy *data) { - /* We've sent the TYPE, now we must send the list of prequote strings */ + /* We have sent the TYPE, now we must send the list of prequote strings */ return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); } static CURLcode ftp_state_stor_prequote(struct Curl_easy *data) { - /* We've sent the TYPE, now we must send the list of prequote strings */ + /* We have sent the TYPE, now we must send the list of prequote strings */ return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE); } @@ -1608,7 +1615,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data) struct ftp_conn *ftpc = &conn->proto.ftpc; /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP can't be much more than the file size and + information. Which in FTP cannot be much more than the file size and date. */ if(data->req.no_body && ftpc->file && ftp_need_type(conn, data->state.prefer_ascii)) { @@ -1668,13 +1675,13 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, if((data->state.resume_from && !sizechecked) || ((data->state.resume_from > 0) && sizechecked)) { - /* we're about to continue the uploading of a file */ + /* we are about to continue the uploading of a file */ /* 1. get already existing file's size. We use the SIZE command for this which may not exist in the server! The SIZE command is not in RFC959. */ /* 2. This used to set REST. But since we can do append, we - don't another ftp command. We just skip the source file + do not another ftp command. We just skip the source file offset and then we APPEND the rest on the file instead */ /* 3. pass file-size number of bytes in the source file */ @@ -1707,7 +1714,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, failf(data, "Could not seek stream"); return CURLE_FTP_COULDNT_USE_REST; } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { char scratch[4*1024]; size_t readthisamountnow = @@ -1736,17 +1743,17 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, infof(data, "File already completely uploaded"); /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); - /* Set ->transfer so that we won't get any error in - * ftp_done() because we didn't transfer anything! */ + /* Set ->transfer so that we will not get any error in + * ftp_done() because we did not transfer anything! */ ftp->transfer = PPTRANSFER_NONE; ftp_state(data, FTP_STOP); return CURLE_OK; } } - /* we've passed, proceed as normal */ + /* we have passed, proceed as normal */ } /* resume_from */ result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s", @@ -1835,16 +1842,16 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, } else { if(data->set.ignorecl || data->state.prefer_ascii) { - /* 'ignorecl' is used to support download of growing files. It + /* 'ignorecl' is used to support download of growing files. It prevents the state machine from requesting the file size from - the server. With an unknown file size the download continues + the server. With an unknown file size the download continues until the server terminates it, otherwise the client stops if - the received byte count exceeds the reported file size. Set + the received byte count exceeds the reported file size. Set option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this behavior. In addition: asking for the size for 'TYPE A' transfers is not - constructive since servers don't report the converted size. So + constructive since servers do not report the converted size. So skip it. */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); @@ -1882,7 +1889,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) #endif ) { - /* We can't disable EPSV when doing IPv6, so this is instead a fail */ + /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ failf(data, "Failed EPSV attempt, exiting"); return CURLE_WEIRD_SERVER_REPLY; } @@ -1907,7 +1914,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, static char *control_address(struct connectdata *conn) { /* Returns the control connection IP address. - If a proxy tunnel is used, returns the original host name instead, because + If a proxy tunnel is used, returns the original hostname instead, because the effective control connection address is the proxy address, not the ftp host. */ #ifndef CURL_DISABLE_PROXY @@ -2046,7 +2053,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, if(conn->bits.proxy) { /* * This connection uses a proxy and we need to connect to the proxy again - * here. We don't want to rely on a former host lookup that might've + * here. We do not want to rely on a former host lookup that might've * expired now, instead we remake the lookup here and now! */ const char * const host_name = conn->bits.socksproxy ? @@ -2061,7 +2068,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, connectport = (unsigned short)conn->primary.remote_port; if(!addr) { - failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport); + failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); return CURLE_COULDNT_RESOLVE_PROXY; } } @@ -2073,7 +2080,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* postponed address resolution in case of tcp fastopen */ if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { - Curl_conn_ev_update_info(data, conn); Curl_safefree(ftpc->newhost); ftpc->newhost = strdup(control_address(conn)); if(!ftpc->newhost) @@ -2088,7 +2094,8 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, connectport = ftpc->newport; /* we connect to the remote port */ if(!addr) { - failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport); + failf(data, "cannot resolve new host %s:%hu", + ftpc->newhost, connectport); return CURLE_FTP_CANT_GET_HOST; } } @@ -2098,7 +2105,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); if(result) { - Curl_resolv_unlock(data, addr); /* we're done using this address */ + Curl_resolv_unlink(data, &addr); /* we are done using this address */ if(ftpc->count1 == 0 && ftpcode == 229) return ftp_epsv_disable(data, conn); @@ -2116,7 +2123,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* this just dumps information about this second connection */ ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport); - Curl_resolv_unlock(data, addr); /* we're done using this address */ + Curl_resolv_unlink(data, &addr); /* we are done using this address */ Curl_safefree(conn->secondaryhostname); conn->secondary_port = ftpc->newport; @@ -2204,7 +2211,7 @@ static CURLcode client_write_header(struct Curl_easy *data, * call to Curl_client_write() so it does the right thing. * * Notice that we cannot enable this flag for FTP in general, - * as an FTP transfer might involve a HTTP proxy connection and + * as an FTP transfer might involve an HTTP proxy connection and * headers from CONNECT should not automatically be part of the * output. */ CURLcode result; @@ -2371,20 +2378,20 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, /* We always (attempt to) get the size of downloads, so it is done before this even when not doing resumes. */ if(filesize == -1) { - infof(data, "ftp server doesn't support SIZE"); - /* We couldn't get the size and therefore we can't know if there really + infof(data, "ftp server does not support SIZE"); + /* We could not get the size and therefore we cannot know if there really is a part of the file left to get, although the server will just - close the connection when we start the connection so it won't cause + close the connection when we start the connection so it will not cause us any harm, just not make us exit as nicely. */ } else { /* We got a file size report, so we check that there actually is a part of the file left to get, or else we go home. */ if(data->state.resume_from< 0) { - /* We're supposed to download the last abs(from) bytes */ + /* We are supposed to download the last abs(from) bytes */ if(filesize < -data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, filesize); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -2395,8 +2402,8 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, } else { if(filesize < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, filesize); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -2407,21 +2414,21 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, if(ftp->downloadsize == 0) { /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); - /* Set ->transfer so that we won't get any error in ftp_done() - * because we didn't transfer the any file */ + /* Set ->transfer so that we will not get any error in ftp_done() + * because we did not transfer the any file */ ftp->transfer = PPTRANSFER_NONE; ftp_state(data, FTP_STOP); return CURLE_OK; } /* Set resume file transfer offset */ - infof(data, "Instructs server to resume from offset %" - CURL_FORMAT_CURL_OFF_T, data->state.resume_from); + infof(data, "Instructs server to resume from offset %" FMT_OFF_T, + data->state.resume_from); - result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T, + result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T, data->state.resume_from); if(!result) ftp_state(data, FTP_RETR_REST); @@ -2479,7 +2486,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, if(-1 != filesize) { char clbuf[128]; int clbuflen = msnprintf(clbuf, sizeof(clbuf), - "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize); + "Content-Length: %" FMT_OFF_T "\r\n", filesize); result = client_write_header(data, clbuf, clbuflen); if(result) return result; @@ -2619,7 +2626,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, !data->set.ignorecl && (ftp->downloadsize < 1)) { /* - * It seems directory listings either don't show the size or very + * It seems directory listings either do not show the size or very * often uses size 0 anyway. ASCII transfers may very well turn out * that the transferred amount of data is not the same as this line * tells, why using this number in those cases only confuses us. @@ -2659,12 +2666,10 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, else if((instate != FTP_LIST) && (data->state.prefer_ascii)) size = -1; /* kludge for servers that understate ASCII mode file size */ - infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T, - data->req.maxdownload); + infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload); if(instate != FTP_LIST) - infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T, - size); + infof(data, "Getting file with size: %" FMT_OFF_T, size); /* FTP download: */ conn->proto.ftpc.state_saved = instate; @@ -2690,7 +2695,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, else { if((instate == FTP_LIST) && (ftpcode == 450)) { /* simply no matching files in the dir listing */ - ftp->transfer = PPTRANSFER_NONE; /* don't download anything */ + ftp->transfer = PPTRANSFER_NONE; /* do not download anything */ ftp_state(data, FTP_STOP); /* this phase is over */ } else { @@ -2777,7 +2782,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && !ftpc->ftp_trying_alternative) { - /* Ok, USER failed. Let's try the supplied command. */ + /* 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]); @@ -2863,7 +2868,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, #endif if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { - /* We don't have a SSL/TLS control connection yet, but FTPS is + /* We do not have a SSL/TLS control connection yet, but FTPS is requested. Try a FTPS connection now */ ftpc->count3 = 0; @@ -2880,7 +2885,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, default: failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d", (int)data->set.ftpsslauth); - return CURLE_UNKNOWN_OPTION; /* we don't know what to do */ + return CURLE_UNKNOWN_OPTION; /* we do not know what to do */ } result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); @@ -2980,7 +2985,13 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, case FTP_CCC: if(ftpcode < 500) { /* First shut down the SSL layer (note: this call will block) */ - result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET); + /* This has only been tested on the proftpd server, and the mod_tls + * code sends a close notify alert without waiting for a close notify + * alert in response. Thus we wait for a close notify alert from the + * server, but we do not send one. Let's hope other servers do + * the same... */ + result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET, + (data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)); if(result) failf(data, "Failed to clear the command channel (CCC)"); @@ -3069,7 +3080,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, data->state.most_recent_ftp_entrypath = ftpc->entrypath; } else { - /* couldn't get the path */ + /* could not get the path */ Curl_dyn_free(&out); infof(data, "Failed to figure out path"); } @@ -3168,7 +3179,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, else { /* return failure */ failf(data, "Server denied you to change to the given directory"); - ftpc->cwdfail = TRUE; /* don't remember this path as we failed + ftpc->cwdfail = TRUE; /* do not remember this path as we failed to enter it */ result = CURLE_REMOTE_ACCESS_DENIED; } @@ -3373,7 +3384,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, case CURLE_REMOTE_FILE_NOT_FOUND: case CURLE_WRITE_ERROR: /* the connection stays alive fine even though this happened */ - case CURLE_OK: /* doesn't affect the control connection's status */ + case CURLE_OK: /* does not affect the control connection's status */ if(!premature) break; @@ -3439,7 +3450,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, /* free the dir tree and file parts */ freedirs(ftpc); - /* shut down the socket to inform the server we're done */ + /* shut down the socket to inform the server we are done */ #ifdef _WIN32_WCE shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */ @@ -3457,7 +3468,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } - close_secondarysocket(data, conn); + close_secondarysocket(data); } if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && @@ -3523,8 +3534,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, (data->state.infilesize != data->req.writebytecount) && !data->set.crlf && (ftp->transfer == PPTRANSFER_BODY)) { - failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T - " out of %" CURL_FORMAT_CURL_OFF_T " bytes)", + failf(data, "Uploaded unaligned file size (%" FMT_OFF_T + " out of %" FMT_OFF_T " bytes)", data->req.writebytecount, data->state.infilesize); result = CURLE_PARTIAL_FILE; } @@ -3532,17 +3543,9 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, else { if((-1 != data->req.size) && (data->req.size != data->req.bytecount) && -#ifdef CURL_DO_LINEEND_CONV - /* Most FTP servers don't adjust their file SIZE response for CRLFs, so - * we'll check to see if the discrepancy can be explained by the number - * of CRLFs we've changed to LFs. - */ - ((data->req.size + data->state.crlf_conversions) != - data->req.bytecount) && -#endif /* CURL_DO_LINEEND_CONV */ (data->req.maxdownload != data->req.bytecount)) { - failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T - " bytes", data->req.bytecount); + failf(data, "Received only partial file: %" FMT_OFF_T " bytes", + data->req.bytecount); result = CURLE_PARTIAL_FILE; } else if(!ftpc->dont_check && @@ -3670,7 +3673,7 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, * ftp_pasv_verbose() * * This function only outputs some informationals about this second connection - * when we've issued a PASV command before and thus we have connected to a + * when we have issued a PASV command before and thus we have connected to a * possibly new IP address. * */ @@ -3678,7 +3681,7 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, static void ftp_pasv_verbose(struct Curl_easy *data, struct Curl_addrinfo *ai, - char *newhost, /* ascii version */ + char *newhost, /* ASCII version */ int port) { char buf[256]; @@ -3711,7 +3714,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * complete */ struct FTP *ftp = NULL; - /* if the second connection isn't done yet, wait for it to have + /* if the second connection is not done yet, wait for it to have * connected to the remote host. When using proxy tunneling, this * means the tunnel needs to have been establish. However, we * can not expect the remote host to talk to us in any way yet. @@ -3739,20 +3742,20 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) *completep = (int)complete; - /* if we got an error or if we don't wait for a data connection return + /* if we got an error or if we do not wait for a data connection return immediately */ if(result || !ftpc->wait_data_conn) return result; /* if we reach the end of the FTP state machine here, *complete will be TRUE but so is ftpc->wait_data_conn, which says we need to wait for the - data connection and therefore we're not actually complete */ + data connection and therefore we are not actually complete */ *completep = 0; } if(ftp->transfer <= PPTRANSFER_INFO) { - /* a transfer is about to take place, or if not a file name was given - so we'll do a SIZE on it later and then we need the right TYPE first */ + /* a transfer is about to take place, or if not a filename was given so we + will do a SIZE on it later and then we need the right TYPE first */ if(ftpc->wait_data_conn) { bool serv_conned; @@ -3791,7 +3794,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) result = Curl_range(data); if(result == CURLE_OK && data->req.maxdownload >= 0) { - /* Don't check for successful transfer */ + /* Do not check for successful transfer */ ftpc->dont_check = TRUE; } @@ -3824,7 +3827,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ @@ -3956,7 +3959,7 @@ static CURLcode init_wc_data(struct Curl_easy *data) if(data->set.ftp_filemethod == FTPFILE_NOCWD) data->set.ftp_filemethod = FTPFILE_MULTICWD; - /* try to parse ftp url */ + /* try to parse ftp URL */ result = ftp_parse_url_path(data); if(result) { goto fail; @@ -4022,7 +4025,7 @@ static CURLcode wc_statemach(struct Curl_easy *data) wildcard->state = CURLWC_CLEAN; continue; } - if(wildcard->filelist.size == 0) { + if(Curl_llist_count(&wildcard->filelist) == 0) { /* no corresponding file */ wildcard->state = CURLWC_CLEAN; return CURLE_REMOTE_FILE_NOT_FOUND; @@ -4033,7 +4036,8 @@ static CURLcode wc_statemach(struct Curl_easy *data) case CURLWC_DOWNLOADING: { /* filelist has at least one file, lets get first one */ struct ftp_conn *ftpc = &conn->proto.ftpc; - struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; + struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist); + struct curl_fileinfo *finfo = Curl_node_elem(head); struct FTP *ftp = data->req.p.ftp; char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); @@ -4049,7 +4053,8 @@ static CURLcode wc_statemach(struct Curl_easy *data) long userresponse; Curl_set_in_callback(data, true); userresponse = data->set.chunk_bgn( - finfo, data->set.wildcardptr, (int)wildcard->filelist.size); + finfo, data->set.wildcardptr, + (int)Curl_llist_count(&wildcard->filelist)); Curl_set_in_callback(data, false); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: @@ -4074,10 +4079,11 @@ static CURLcode wc_statemach(struct Curl_easy *data) if(result) return result; - /* we don't need the Curl_fileinfo of first file anymore */ - Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); + /* we do not need the Curl_fileinfo of first file anymore */ + Curl_node_remove(Curl_llist_head(&wildcard->filelist)); - if(wildcard->filelist.size == 0) { /* remains only one file to down. */ + if(Curl_llist_count(&wildcard->filelist) == 0) { + /* remains only one file to down. */ wildcard->state = CURLWC_CLEAN; /* after that will be ftp_do called once again and no transfer will be done because of CURLWC_CLEAN state */ @@ -4092,8 +4098,8 @@ static CURLcode wc_statemach(struct Curl_easy *data) data->set.chunk_end(data->set.wildcardptr); Curl_set_in_callback(data, false); } - Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); - wildcard->state = (wildcard->filelist.size == 0) ? + Curl_node_remove(Curl_llist_head(&wildcard->filelist)); + wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ? CURLWC_CLEAN : CURLWC_DOWNLOADING; continue; } @@ -4139,7 +4145,7 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done) *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ -#ifdef CURL_DO_LINEEND_CONV +#ifdef CURL_PREFER_LF_LINEENDS { /* FTP data may need conversion. */ struct Curl_cwriter *ftp_lc_writer; @@ -4155,7 +4161,7 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done) return result; } } -#endif /* CURL_DO_LINEEND_CONV */ +#endif /* CURL_PREFER_LF_LINEENDS */ if(data->state.wildcardmatch) { result = wc_statemach(data); @@ -4229,7 +4235,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, bad in any way, sending quit and waiting around here will make the disconnect wait in vain and cause more problems than we need to. - ftp_quit() will check the state of ftp->ctl_valid. If it's ok it + ftp_quit() will check the state of ftp->ctl_valid. If it is ok it will try to send the QUIT command, otherwise it will just return. */ if(dead_connection) @@ -4324,10 +4330,10 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) } ftpc->dirdepth = 1; /* we consider it to be a single dir */ - fileName = slashPos + 1; /* rest is file name */ + fileName = slashPos + 1; /* rest is filename */ } else - fileName = rawPath; /* file name only (or empty) */ + fileName = rawPath; /* filename only (or empty) */ break; default: /* allow pretty much anything */ @@ -4358,7 +4364,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) ++compLen; /* we skip empty path components, like "x//y" since the FTP command - CWD requires a parameter and a non-existent parameter a) doesn't + CWD requires a parameter and a non-existent parameter a) does not work on many servers and b) has no effect on the others. */ if(compLen > 0) { char *comp = Curl_memdup0(curPos, compLen); @@ -4372,7 +4378,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) } } DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc); - fileName = curPos; /* the rest is the file name (or empty) */ + fileName = curPos; /* the rest is the filename (or empty) */ } break; } /* switch */ @@ -4384,8 +4390,8 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) we make it a NULL pointer */ if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { - /* We need a file name when uploading. Return error! */ - failf(data, "Uploading to a URL without a file name"); + /* We need a filename when uploading. Return error! */ + failf(data, "Uploading to a URL without a filename"); free(rawPath); return CURLE_URL_MALFORMAT; } @@ -4426,16 +4432,16 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) CURLcode result = ftp_do_more(data, &completed); if(result) { - close_secondarysocket(data, conn); + close_secondarysocket(data); return result; } } if(ftp->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); else if(!connected) - /* since we didn't connect now, we want do_more to get called */ + /* since we did not connect now, we want do_more to get called */ conn->bits.do_more = TRUE; ftpc->ctl_valid = TRUE; /* seems good */ @@ -4540,10 +4546,10 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, } data->req.p.ftp = ftp; - ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ + ftp->path = &data->state.up.path[1]; /* do not include the initial slash */ /* FTP URLs support an extension like ";type=" that - * we'll try to get now! */ + * we will try to get now! */ type = strstr(ftp->path, ";type="); if(!type) diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 977fc883b..3d0af0158 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h @@ -61,7 +61,7 @@ enum { FTP_STOR_PREQUOTE, FTP_POSTQUOTE, FTP_CWD, /* change dir */ - FTP_MKD, /* if the dir didn't exist */ + FTP_MKD, /* if the dir did not exist */ FTP_MDTM, /* to figure out the datestamp */ FTP_TYPE, /* to set type when doing a head-like request */ FTP_LIST_TYPE, /* set type when about to do a dir list */ @@ -123,7 +123,7 @@ struct ftp_conn { char *account; char *alternative_to_user; char *entrypath; /* the PWD reply when we logged on */ - char *file; /* url-decoded file name (or path) */ + char *file; /* url-decoded filename (or path) */ char **dirs; /* realloc()ed array for path components */ char *newhost; char *prevpath; /* url-decoded conn->path from the previous transfer */ @@ -139,7 +139,7 @@ struct ftp_conn { int count1; /* general purpose counter for the state machine */ int count2; /* general purpose counter for the state machine */ int count3; /* general purpose counter for the state machine */ - /* newhost is the (allocated) IP addr or host name to connect the data + /* newhost is the (allocated) IP addr or hostname to connect the data connection to */ unsigned short newport; ftpstate state; /* always use ftp.c:state() to change state! */ diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c index 48ee97228..49a2e50fa 100644 --- a/Utilities/cmcurl/lib/getenv.c +++ b/Utilities/cmcurl/lib/getenv.c @@ -37,7 +37,7 @@ static char *GetEnv(const char *variable) return NULL; #elif defined(_WIN32) /* This uses Windows API instead of C runtime getenv() to get the environment - variable since some changes aren't always visible to the latter. #4774 */ + variable since some changes are not always visible to the latter. #4774 */ char *buf = NULL; char *tmp; DWORD bufsize; @@ -54,8 +54,8 @@ static char *GetEnv(const char *variable) buf = tmp; bufsize = rc; - /* It's possible for rc to be 0 if the variable was found but empty. - Since getenv doesn't make that distinction we ignore it as well. */ + /* it is possible for rc to be 0 if the variable was found but empty. + Since getenv does not make that distinction we ignore it as well. */ rc = GetEnvironmentVariableA(variable, buf, bufsize); if(!rc || rc == bufsize || rc > max) { free(buf); diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index e423f0b29..714610156 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c @@ -53,6 +53,7 @@ CURLcode Curl_initinfo(struct Curl_easy *data) pro->t_connect = 0; pro->t_appconnect = 0; pro->t_pretransfer = 0; + pro->t_posttransfer = 0; pro->t_starttransfer = 0; pro->timespent = 0; pro->t_redirect = 0; @@ -76,10 +77,9 @@ CURLcode Curl_initinfo(struct Curl_easy *data) free(info->wouldredirect); info->wouldredirect = NULL; - info->primary.remote_ip[0] = '\0'; - info->primary.local_ip[0] = '\0'; - info->primary.remote_port = 0; - info->primary.local_port = 0; + memset(&info->primary, 0, sizeof(info->primary)); + info->primary.remote_port = -1; + info->primary.local_port = -1; info->retry_after = 0; info->conn_scheme = 0; @@ -204,7 +204,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, #ifdef DEBUGBUILD char *timestr = getenv("CURL_TIME"); if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); + unsigned long val = strtoul(timestr, NULL, 10); switch(info) { case CURLINFO_LOCAL_PORT: *param_longp = (long)val; @@ -216,7 +216,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, /* use another variable for this to allow different values */ timestr = getenv("CURL_DEBUG_SIZE"); if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); + unsigned long val = strtoul(timestr, NULL, 10); switch(info) { case CURLINFO_HEADER_SIZE: case CURLINFO_REQUEST_SIZE: @@ -252,11 +252,13 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_SSL_VERIFYRESULT: *param_longp = data->set.ssl.certverifyresult; break; -#ifndef CURL_DISABLE_PROXY case CURLINFO_PROXY_SSL_VERIFYRESULT: +#ifndef CURL_DISABLE_PROXY *param_longp = data->set.proxy_ssl.certverifyresult; - break; +#else + *param_longp = 0; #endif + break; case CURLINFO_REDIRECT_COUNT: *param_longp = data->state.followlocation; break; @@ -277,8 +279,8 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_LASTSOCKET: sockfd = Curl_getconnectinfo(data, NULL); - /* note: this is not a good conversion for systems with 64 bit sockets and - 32 bit longs */ + /* note: this is not a good conversion for systems with 64-bit sockets and + 32-bit longs */ if(sockfd != CURL_SOCKET_BAD) *param_longp = (long)sockfd; else @@ -314,6 +316,12 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, case CURLINFO_RTSP_CSEQ_RECV: *param_longp = data->state.rtsp_CSeq_recv; break; +#else + case CURLINFO_RTSP_CLIENT_CSEQ: + case CURLINFO_RTSP_SERVER_CSEQ: + case CURLINFO_RTSP_CSEQ_RECV: + *param_longp = 0; + break; #endif case CURLINFO_HTTP_VERSION: switch(data->info.httpversion) { @@ -335,7 +343,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, } break; case CURLINFO_PROTOCOL: - *param_longp = data->info.conn_protocol; + *param_longp = (long)data->info.conn_protocol; break; case CURLINFO_USED_PROXY: *param_longp = @@ -361,13 +369,14 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, #ifdef DEBUGBUILD char *timestr = getenv("CURL_TIME"); if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); + unsigned long val = strtoul(timestr, NULL, 10); switch(info) { case CURLINFO_TOTAL_TIME_T: case CURLINFO_NAMELOOKUP_TIME_T: case CURLINFO_CONNECT_TIME_T: case CURLINFO_APPCONNECT_TIME_T: case CURLINFO_PRETRANSFER_TIME_T: + case CURLINFO_POSTTRANSFER_TIME_T: case CURLINFO_STARTTRANSFER_TIME_T: case CURLINFO_REDIRECT_TIME_T: case CURLINFO_SPEED_DOWNLOAD_T: @@ -384,24 +393,24 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = (curl_off_t)data->info.filetime; break; case CURLINFO_SIZE_UPLOAD_T: - *param_offt = data->progress.uploaded; + *param_offt = data->progress.ul.cur_size; break; case CURLINFO_SIZE_DOWNLOAD_T: - *param_offt = data->progress.downloaded; + *param_offt = data->progress.dl.cur_size; break; case CURLINFO_SPEED_DOWNLOAD_T: - *param_offt = data->progress.dlspeed; + *param_offt = data->progress.dl.speed; break; case CURLINFO_SPEED_UPLOAD_T: - *param_offt = data->progress.ulspeed; + *param_offt = data->progress.ul.speed; break; case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T: *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? - data->progress.size_dl:-1; + data->progress.dl.total_size:-1; break; case CURLINFO_CONTENT_LENGTH_UPLOAD_T: *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? - data->progress.size_ul:-1; + data->progress.ul.total_size:-1; break; case CURLINFO_TOTAL_TIME_T: *param_offt = data->progress.timespent; @@ -418,6 +427,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, case CURLINFO_PRETRANSFER_TIME_T: *param_offt = data->progress.t_pretransfer; break; + case CURLINFO_POSTTRANSFER_TIME_T: + *param_offt = data->progress.t_posttransfer; + break; case CURLINFO_STARTTRANSFER_TIME_T: *param_offt = data->progress.t_starttransfer; break; @@ -450,7 +462,7 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, #ifdef DEBUGBUILD char *timestr = getenv("CURL_TIME"); if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); + unsigned long val = strtoul(timestr, NULL, 10); switch(info) { case CURLINFO_TOTAL_TIME: case CURLINFO_NAMELOOKUP_TIME: @@ -488,24 +500,24 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer); break; case CURLINFO_SIZE_UPLOAD: - *param_doublep = (double)data->progress.uploaded; + *param_doublep = (double)data->progress.ul.cur_size; break; case CURLINFO_SIZE_DOWNLOAD: - *param_doublep = (double)data->progress.downloaded; + *param_doublep = (double)data->progress.dl.cur_size; break; case CURLINFO_SPEED_DOWNLOAD: - *param_doublep = (double)data->progress.dlspeed; + *param_doublep = (double)data->progress.dl.speed; break; case CURLINFO_SPEED_UPLOAD: - *param_doublep = (double)data->progress.ulspeed; + *param_doublep = (double)data->progress.ul.speed; break; case CURLINFO_CONTENT_LENGTH_DOWNLOAD: *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? - (double)data->progress.size_dl:-1; + (double)data->progress.dl.total_size:-1; break; case CURLINFO_CONTENT_LENGTH_UPLOAD: *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? - (double)data->progress.size_ul:-1; + (double)data->progress.ul.total_size:-1; break; case CURLINFO_REDIRECT_TIME: *param_doublep = DOUBLE_SECS(data->progress.t_redirect); diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 311bd3798..051e6e7ab 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c @@ -187,7 +187,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(strlen(sel) < 1) break; - result = Curl_xfer_send(data, sel, k, &amount); + result = Curl_xfer_send(data, sel, k, FALSE, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); if(result) @@ -209,9 +209,9 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - /* Don't busyloop. The entire loop thing is a work-around as it causes a + /* Do not busyloop. The entire loop thing is a work-around as it causes a BLOCKING behavior which is a NO-NO. This function should rather be - split up in a do and a doing piece where the pieces that aren't + split up in a do and a doing piece where the pieces that are not possible to send now will be sent in the doing function repeatedly until the entire request is sent. */ @@ -229,7 +229,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) free(sel_org); if(!result) - result = Curl_xfer_send(data, "\r\n", 2, &amount); + result = Curl_xfer_send(data, "\r\n", 2, FALSE, &amount); if(result) { failf(data, "Failed sending Gopher request"); return result; @@ -238,7 +238,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(result) return result; - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); return CURLE_OK; } #endif /* CURL_DISABLE_GOPHER */ diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index ddbae4d3e..1910ac5dc 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c @@ -33,6 +33,10 @@ /* The last #include file should be: */ #include "memdebug.h" +/* random patterns for API verification */ +#define HASHINIT 0x7017e781 +#define ITERINIT 0x5FEDCBA9 + static void hash_element_dtor(void *user, void *element) { @@ -40,7 +44,10 @@ hash_element_dtor(void *user, void *element) struct Curl_hash_element *e = (struct Curl_hash_element *) element; if(e->ptr) { - h->dtor(e->ptr); + if(e->dtor) + e->dtor(e->key, e->key_len, e->ptr); + else + h->dtor(e->ptr); e->ptr = NULL; } @@ -74,10 +81,14 @@ Curl_hash_init(struct Curl_hash *h, h->dtor = dtor; h->size = 0; h->slots = slots; +#ifdef DEBUGBUILD + h->init = HASHINIT; +#endif } static struct Curl_hash_element * -mk_hash_element(const void *key, size_t key_len, const void *p) +mk_hash_element(const void *key, size_t key_len, const void *p, + Curl_hash_elem_dtor dtor) { /* allocate the struct plus memory after it to store the key */ struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) + @@ -87,29 +98,23 @@ mk_hash_element(const void *key, size_t key_len, const void *p) memcpy(he->key, key, key_len); he->key_len = key_len; he->ptr = (void *) p; + he->dtor = dtor; } return he; } #define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)] -/* Insert the data in the hash. If there already was a match in the hash, that - * data is replaced. This function also "lazily" allocates the table if - * needed, as it isn't done in the _init function (anymore). - * - * @unittest: 1305 - * @unittest: 1602 - * @unittest: 1603 - */ -void * -Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) +void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, + Curl_hash_elem_dtor dtor) { struct Curl_hash_element *he; - struct Curl_llist_element *le; + struct Curl_llist_node *le; struct Curl_llist *l; DEBUGASSERT(h); DEBUGASSERT(h->slots); + DEBUGASSERT(h->init == HASHINIT); if(!h->table) { size_t i; h->table = malloc(h->slots * sizeof(struct Curl_llist)); @@ -121,16 +126,16 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) l = FETCH_LIST(h, key, key_len); - for(le = l->head; le; le = le->next) { - he = (struct Curl_hash_element *) le->ptr; + for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) { + he = (struct Curl_hash_element *) Curl_node_elem(le); if(h->comp_func(he->key, he->key_len, key, key_len)) { - Curl_llist_remove(l, le, (void *)h); + Curl_node_uremove(le, (void *)h); --h->size; break; } } - he = mk_hash_element(key, key_len, p); + he = mk_hash_element(key, key_len, p, dtor); if(he) { Curl_llist_append(l, he, &he->list); ++h->size; @@ -140,6 +145,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) return NULL; /* failure */ } +/* Insert the data in the hash. If there already was a match in the hash, that + * data is replaced. This function also "lazily" allocates the table if + * needed, as it is not done in the _init function (anymore). + * + * @unittest: 1305 + * @unittest: 1602 + * @unittest: 1603 + */ +void * +Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) +{ + return Curl_hash_add2(h, key, key_len, p, NULL); +} + /* Remove the identified hash entry. * Returns non-zero on failure. * @@ -147,18 +166,17 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) */ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) { - struct Curl_llist_element *le; - struct Curl_llist *l; - DEBUGASSERT(h); DEBUGASSERT(h->slots); + DEBUGASSERT(h->init == HASHINIT); if(h->table) { - l = FETCH_LIST(h, key, key_len); + struct Curl_llist_node *le; + struct Curl_llist *l = FETCH_LIST(h, key, key_len); - for(le = l->head; le; le = le->next) { - struct Curl_hash_element *he = le->ptr; + for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) { + struct Curl_hash_element *he = Curl_node_elem(le); if(h->comp_func(he->key, he->key_len, key, key_len)) { - Curl_llist_remove(l, le, (void *) h); + Curl_node_uremove(le, (void *) h); --h->size; return 0; } @@ -174,15 +192,15 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) void * Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) { - struct Curl_llist_element *le; - struct Curl_llist *l; - DEBUGASSERT(h); + DEBUGASSERT(h->init == HASHINIT); if(h->table) { + struct Curl_llist_node *le; + struct Curl_llist *l; DEBUGASSERT(h->slots); l = FETCH_LIST(h, key, key_len); - for(le = l->head; le; le = le->next) { - struct Curl_hash_element *he = le->ptr; + for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) { + struct Curl_hash_element *he = Curl_node_elem(le); if(h->comp_func(he->key, he->key_len, key, key_len)) { return he->ptr; } @@ -202,6 +220,7 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) void Curl_hash_destroy(struct Curl_hash *h) { + DEBUGASSERT(h->init == HASHINIT); if(h->table) { size_t i; for(i = 0; i < h->slots; ++i) { @@ -223,28 +242,33 @@ Curl_hash_clean(struct Curl_hash *h) Curl_hash_clean_with_criterium(h, NULL, NULL); } +size_t Curl_hash_count(struct Curl_hash *h) +{ + DEBUGASSERT(h->init == HASHINIT); + return h->size; +} + /* Cleans all entries that pass the comp function criteria. */ void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, int (*comp)(void *, void *)) { - struct Curl_llist_element *le; - struct Curl_llist_element *lnext; - struct Curl_llist *list; size_t i; if(!h || !h->table) return; + DEBUGASSERT(h->init == HASHINIT); for(i = 0; i < h->slots; ++i) { - list = &h->table[i]; - le = list->head; /* get first list entry */ + struct Curl_llist *list = &h->table[i]; + struct Curl_llist_node *le = + Curl_llist_head(list); /* get first list entry */ while(le) { - struct Curl_hash_element *he = le->ptr; - lnext = le->next; + struct Curl_hash_element *he = Curl_node_elem(le); + struct Curl_llist_node *lnext = Curl_node_next(le); /* ask the callback function if we shall remove this entry or not */ if(!comp || comp(user, he->ptr)) { - Curl_llist_remove(list, le, (void *) h); + Curl_node_uremove(le, (void *) h); --h->size; /* one less entry in the hash now */ } le = lnext; @@ -259,8 +283,9 @@ size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) size_t h = 5381; while(key_str < end) { + size_t j = (size_t)*key_str++; h += h << 5; - h ^= *key_str++; + h ^= j; } return (h % slots_num); @@ -278,29 +303,34 @@ size_t Curl_str_key_compare(void *k1, size_t key1_len, void Curl_hash_start_iterate(struct Curl_hash *hash, struct Curl_hash_iterator *iter) { + DEBUGASSERT(hash->init == HASHINIT); iter->hash = hash; iter->slot_index = 0; iter->current_element = NULL; +#ifdef DEBUGBUILD + iter->init = ITERINIT; +#endif } struct Curl_hash_element * Curl_hash_next_element(struct Curl_hash_iterator *iter) { - struct Curl_hash *h = iter->hash; - + struct Curl_hash *h; + DEBUGASSERT(iter->init == ITERINIT); + h = iter->hash; if(!h->table) return NULL; /* empty hash, nothing to return */ /* Get the next element in the current list, if any */ if(iter->current_element) - iter->current_element = iter->current_element->next; + iter->current_element = Curl_node_next(iter->current_element); /* If we have reached the end of the list, find the next one */ if(!iter->current_element) { size_t i; for(i = iter->slot_index; i < h->slots; i++) { - if(h->table[i].head) { - iter->current_element = h->table[i].head; + if(Curl_llist_head(&h->table[i])) { + iter->current_element = Curl_llist_head(&h->table[i]); iter->slot_index = i + 1; break; } @@ -308,7 +338,7 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter) } if(iter->current_element) { - struct Curl_hash_element *he = iter->current_element->ptr; + struct Curl_hash_element *he = Curl_node_elem(iter->current_element); return he; } return NULL; diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index 2bdc24717..b16039502 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h @@ -56,19 +56,31 @@ struct Curl_hash { Curl_hash_dtor dtor; size_t slots; size_t size; +#ifdef DEBUGBUILD + int init; +#endif }; +typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p); + struct Curl_hash_element { - struct Curl_llist_element list; + struct Curl_llist_node list; void *ptr; + Curl_hash_elem_dtor dtor; size_t key_len; +#ifdef DEBUGBUILD + int init; +#endif char key[1]; /* allocated memory following the struct */ }; struct Curl_hash_iterator { struct Curl_hash *hash; size_t slot_index; - struct Curl_llist_element *current_element; + struct Curl_llist_node *current_element; +#ifdef DEBUGBUILD + int init; +#endif }; void Curl_hash_init(struct Curl_hash *h, @@ -78,10 +90,13 @@ void Curl_hash_init(struct Curl_hash *h, Curl_hash_dtor dtor); void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); +void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, + Curl_hash_elem_dtor dtor); int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); -#define Curl_hash_count(h) ((h)->size) + void Curl_hash_destroy(struct Curl_hash *h); +size_t Curl_hash_count(struct Curl_hash *h); void Curl_hash_clean(struct Curl_hash *h); void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, int (*comp)(void *, void *)); diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index 9a5692e54..7c60c0798 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c @@ -42,7 +42,7 @@ static void copy_header_external(struct Curl_header_store *hs, size_t index, size_t amount, - struct Curl_llist_element *e, + struct Curl_llist_node *e, struct curl_header *hout) { struct curl_header *h = hout; @@ -54,7 +54,7 @@ static void copy_header_external(struct Curl_header_store *hs, impossible for applications to do == comparisons, as that would otherwise be very tempting and then lead to the reserved bits not being reserved anymore. */ - h->origin = hs->type | (1<<27); + h->origin = (unsigned int)(hs->type | (1<<27)); h->anchor = e; } @@ -66,8 +66,8 @@ CURLHcode curl_easy_header(CURL *easy, int request, struct curl_header **hout) { - struct Curl_llist_element *e; - struct Curl_llist_element *e_pick = NULL; + struct Curl_llist_node *e; + struct Curl_llist_node *e_pick = NULL; struct Curl_easy *data = easy; size_t match = 0; size_t amount = 0; @@ -85,8 +85,8 @@ CURLHcode curl_easy_header(CURL *easy, request = data->state.requests; /* we need a first round to count amount of this header */ - for(e = data->state.httphdrs.head; e; e = e->next) { - hs = e->ptr; + for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { + hs = Curl_node_elem(e); if(strcasecompare(hs->name, name) && (hs->type & type) && (hs->request == request)) { @@ -104,8 +104,8 @@ CURLHcode curl_easy_header(CURL *easy, /* if the last or only occurrence is what's asked for, then we know it */ hs = pick; else { - for(e = data->state.httphdrs.head; e; e = e->next) { - hs = e->ptr; + for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { + hs = Curl_node_elem(e); if(strcasecompare(hs->name, name) && (hs->type & type) && (hs->request == request) && @@ -114,7 +114,7 @@ CURLHcode curl_easy_header(CURL *easy, break; } } - if(!e) /* this shouldn't happen */ + if(!e) /* this should not happen */ return CURLHE_MISSING; } /* this is the name we want */ @@ -131,8 +131,8 @@ struct curl_header *curl_easy_nextheader(CURL *easy, struct curl_header *prev) { struct Curl_easy *data = easy; - struct Curl_llist_element *pick; - struct Curl_llist_element *e; + struct Curl_llist_node *pick; + struct Curl_llist_node *e; struct Curl_header_store *hs; size_t amount = 0; size_t index = 0; @@ -147,18 +147,18 @@ struct curl_header *curl_easy_nextheader(CURL *easy, if(!pick) /* something is wrong */ return NULL; - pick = pick->next; + pick = Curl_node_next(pick); } else - pick = data->state.httphdrs.head; + pick = Curl_llist_head(&data->state.httphdrs); if(pick) { /* make sure it is the next header of the desired type */ do { - hs = pick->ptr; + hs = Curl_node_elem(pick); if((hs->type & type) && (hs->request == request)) break; - pick = pick->next; + pick = Curl_node_next(pick); } while(pick); } @@ -166,12 +166,12 @@ struct curl_header *curl_easy_nextheader(CURL *easy, /* no more headers available */ return NULL; - hs = pick->ptr; + hs = Curl_node_elem(pick); /* count number of occurrences of this name within the mask and figure out the index for the currently selected entry */ - for(e = data->state.httphdrs.head; e; e = e->next) { - struct Curl_header_store *check = e->ptr; + for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { + struct Curl_header_store *check = Curl_node_elem(e); if(strcasecompare(hs->name, check->name) && (check->request == request) && (check->type & type)) @@ -247,7 +247,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *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); + Curl_node_remove(&hs->node); /* new size = struct + new value length + old name+value length */ newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); @@ -302,7 +302,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, /* line folding, append value to the previous header's value */ return unfold_value(data, header, hlen); else { - /* Can't unfold without a previous header. Instead of erroring, just + /* cannot unfold without a previous header. Instead of erroring, just pass the leading blanks. */ while(hlen && ISBLANK(*header)) { header++; @@ -405,12 +405,12 @@ CURLcode Curl_headers_init(struct Curl_easy *data) */ CURLcode Curl_headers_cleanup(struct Curl_easy *data) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; + struct Curl_llist_node *e; + struct Curl_llist_node *n; - for(e = data->state.httphdrs.head; e; e = n) { - struct Curl_header_store *hs = e->ptr; - n = e->next; + for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) { + struct Curl_header_store *hs = Curl_node_elem(e); + n = Curl_node_next(e); free(hs); } headers_reset(data); diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h index d9813388c..e11fe9804 100644 --- a/Utilities/cmcurl/lib/headers.h +++ b/Utilities/cmcurl/lib/headers.h @@ -28,7 +28,7 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) struct Curl_header_store { - struct Curl_llist_element node; + struct Curl_llist_node node; char *name; /* points into 'buffer' */ char *value; /* points into 'buffer */ int request; /* 0 is the first request, then 1.. 2.. */ diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c index 4019b67f8..90f37f0bf 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c @@ -42,7 +42,7 @@ * Generic HMAC algorithm. * * This module computes HMAC digests based on any hash function. Parameters - * and computing procedures are set-up dynamically at HMAC computation context + * and computing procedures are setup dynamically at HMAC computation context * initialization. */ diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c index 2f6762ca4..4d6a8e859 100644 --- a/Utilities/cmcurl/lib/hostasyn.c +++ b/Utilities/cmcurl/lib/hostasyn.c @@ -79,7 +79,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data, dns = Curl_cache_addr(data, ai, data->state.async.hostname, 0, - data->state.async.port); + data->state.async.port, FALSE); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 93c36e031..fc01dc3fb 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -84,8 +84,8 @@ * source file are these: * * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use - * that. The host may not be able to resolve IPv6, but we don't really have to - * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 + * that. The host may not be able to resolve IPv6, but we do not really have to + * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4 * defined. * * CURLRES_ARES - is defined if libcurl is built to use c-ares for @@ -115,7 +115,7 @@ * CURLRES_* defines based on the config*.h and curl_setup.h defines. */ -static void freednsentry(void *freethis); +static void hostcache_unlink_entry(void *entry); #ifndef CURL_DISABLE_VERBOSE_STRINGS static void show_resolve_info(struct Curl_easy *data, @@ -178,7 +178,7 @@ create_hostcache_id(const char *name, struct hostcache_prune_data { time_t now; time_t oldest; /* oldest time in cache not pruned. */ - int cache_timeout; + int max_age_sec; }; /* @@ -189,16 +189,16 @@ struct hostcache_prune_data { * cache. */ static int -hostcache_timestamp_remove(void *datap, void *hc) +hostcache_entry_is_stale(void *datap, void *hc) { struct hostcache_prune_data *prune = (struct hostcache_prune_data *) datap; - struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; + struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc; - if(c->timestamp) { + if(dns->timestamp) { /* age in seconds */ - time_t age = prune->now - c->timestamp; - if(age >= prune->cache_timeout) + time_t age = prune->now - dns->timestamp; + if(age >= prune->max_age_sec) return TRUE; if(age > prune->oldest) prune->oldest = age; @@ -216,13 +216,13 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout, { struct hostcache_prune_data user; - user.cache_timeout = cache_timeout; + user.max_age_sec = cache_timeout; user.now = now; user.oldest = 0; Curl_hash_clean_with_criterium(hostcache, (void *) &user, - hostcache_timestamp_remove); + hostcache_entry_is_stale); return user.oldest; } @@ -238,7 +238,7 @@ void Curl_hostcache_prune(struct Curl_easy *data) int timeout = data->set.dns_cache_timeout; if(!data->dns.hostcache) - /* NULL hostcache means we can't do it */ + /* NULL hostcache means we cannot do it */ return; if(data->share) @@ -257,7 +257,8 @@ void Curl_hostcache_prune(struct Curl_easy *data) /* if the cache size is still too big, use the oldest age as new prune limit */ - } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE)); + } while(timeout && + (Curl_hash_count(data->dns.hostcache) > MAX_DNS_CACHE_SIZE)); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -283,14 +284,14 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, size_t entry_len = create_hostcache_id(hostname, 0, port, entry_id, sizeof(entry_id)); - /* See if it's already in our dns cache */ + /* See if it is already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); /* No entry found in cache, check if we might have a wildcard entry */ if(!dns && data->state.wildcard_resolve) { entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id)); - /* See if it's already in our dns cache */ + /* See if it is already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); } @@ -299,10 +300,10 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, struct hostcache_prune_data user; user.now = time(NULL); - user.cache_timeout = data->set.dns_cache_timeout; + user.max_age_sec = data->set.dns_cache_timeout; user.oldest = 0; - if(hostcache_timestamp_remove(&user, dns)) { + if(hostcache_entry_is_stale(&user, dns)) { infof(data, "Hostname in DNS cache was stale, zapped"); dns = NULL; /* the memory deallocation is being handled by the hash */ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); @@ -329,7 +330,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, } if(!found) { - infof(data, "Hostname in DNS cache doesn't have needed family, zapped"); + infof(data, "Hostname in DNS cache does not 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); } @@ -348,8 +349,8 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, * * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! + * The returned data *MUST* be "released" with Curl_resolv_unlink() after + * use, or we will leak memory! */ struct Curl_dns_entry * Curl_fetch_addr(struct Curl_easy *data, @@ -364,7 +365,7 @@ Curl_fetch_addr(struct Curl_easy *data, dns = fetch_addr(data, hostname, port); if(dns) - dns->inuse++; /* we use it! */ + dns->refcount++; /* we use it! */ if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -428,8 +429,8 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { struct Curl_addrinfo *swap_tmp; for(i = num_addrs - 1; i > 0; i--) { - swap_tmp = nodes[rnd[i] % (i + 1)]; - nodes[rnd[i] % (i + 1)] = nodes[i]; + swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; + nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; nodes[i] = swap_tmp; } @@ -468,7 +469,8 @@ Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, const char *hostname, size_t hostlen, /* length or zero */ - int port) + int port, + bool permanent) { char entry_id[MAX_HOSTCACHE_LEN]; size_t entry_len; @@ -496,11 +498,15 @@ Curl_cache_addr(struct Curl_easy *data, entry_len = create_hostcache_id(hostname, hostlen, port, entry_id, sizeof(entry_id)); - dns->inuse = 1; /* the cache has the first reference */ + dns->refcount = 1; /* the cache has the first reference */ dns->addr = addr; /* this is the address(es) */ - time(&dns->timestamp); - if(dns->timestamp == 0) - dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */ + if(permanent) + dns->timestamp = 0; /* an entry that never goes stale */ + else { + dns->timestamp = time(NULL); + if(dns->timestamp == 0) + dns->timestamp = 1; + } dns->hostport = port; if(hostlen) memcpy(dns->hostname, hostname, hostlen); @@ -514,7 +520,7 @@ Curl_cache_addr(struct Curl_easy *data, } dns = dns2; - dns->inuse++; /* mark entry as in-use */ + dns->refcount++; /* mark entry as in-use */ return dns; } @@ -536,8 +542,8 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) sa6.sin6_port = htons(port16); sa6.sin6_flowinfo = 0; sa6.sin6_scope_id = 0; - if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1) - return NULL; + + (void)Curl_inet_pton(AF_INET6, "::1", ipv6); memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); ca->ai_flags = 0; @@ -602,7 +608,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) bool Curl_ipv6works(struct Curl_easy *data) { if(data) { - /* the nature of most system is that IPv6 status doesn't come and go + /* the nature of most system is that IPv6 status does not come and go during a program's lifetime so we only probe the first time and then we have the info kept for fast reuse */ DEBUGASSERT(data); @@ -618,7 +624,7 @@ bool Curl_ipv6works(struct Curl_easy *data) /* probe to see if we have a working IPv6 stack */ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); if(s == CURL_SOCKET_BAD) - /* an IPv6 address was requested but we can't get/use one */ + /* an IPv6 address was requested but we cannot get/use one */ ipv6_works = 0; else { ipv6_works = 1; @@ -662,12 +668,12 @@ static bool tailmatch(const char *full, const char *part) /* * 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 - * is provided). This function might return immediately if we're using asynch + * is provided). This function might return immediately if we are using asynch * resolves. See the return codes. * * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlock() later (when you're - * done using this struct) to decrease the counter again. + * function is used. You MUST call Curl_resolv_unlink() later (when you are + * done using this struct) to decrease the reference counter again. * * Return codes: * @@ -708,7 +714,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(dns) { infof(data, "Hostname %s was found in DNS cache", hostname); - dns->inuse++; /* we use it! */ + dns->refcount++; /* we use it! */ rc = CURLRESOLV_RESOLVED; } @@ -813,7 +819,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, if(respwait) { /* the response to our resolve call will come asynchronously at a later time, good or bad */ - /* First, check that we haven't received the info by now */ + /* First, check that we have not received the info by now */ result = Curl_resolv_check(data, &dns); if(result) /* error detected */ return CURLRESOLV_ERROR; @@ -828,7 +834,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, addr, hostname, 0, port); + dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -851,7 +857,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data, #ifdef USE_ALARM_TIMEOUT /* * This signal handler jumps back into the main libcurl code and continues - * execution. This effectively causes the remainder of the application to run + * execution. This effectively causes the remainder of the application to run * within a signal handler which is nonportable and could lead to problems. */ CURL_NORETURN static @@ -864,12 +870,12 @@ void alarmfunc(int sig) /* * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a - * timeout. This function might return immediately if we're using asynch + * timeout. This function might return immediately if we are using asynch * resolves. See the return codes. * * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlock() later (when you're - * done using this struct) to decrease the counter again. + * function is used. You MUST call Curl_resolv_unlink() later (when you are + * done using this struct) to decrease the reference counter again. * * If built with a synchronous resolver and use of signals is not * disabled by the application, then a nonzero timeout will cause a @@ -934,7 +940,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, will generate a signal and we will siglongjmp() from that here. This technique has problems (see alarmfunc). This should be the last thing we do before calling Curl_resolv(), - as otherwise we'd have to worry about variables that get modified + as otherwise we would have to worry about variables that get modified before we invoke Curl_resolv() (and thus use "volatile"). */ curl_simple_lock_lock(&curl_jmpenv_lock); @@ -955,7 +961,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, keep_copysig = TRUE; /* yes, we have a copy */ sigact.sa_handler = alarmfunc; #ifdef SA_RESTART - /* HPUX doesn't have SA_RESTART but defaults to that behavior! */ + /* HP-UX does not have SA_RESTART but defaults to that behavior! */ sigact.sa_flags &= ~SA_RESTART; #endif /* now set the new struct */ @@ -1022,7 +1028,7 @@ clean_up: ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { /* if the alarm time-left reached zero or turned "negative" (counted with unsigned values), we should fire off a SIGALRM here, but we - won't, and zero would be to switch it off so we never set it to + will not, and zero would be to switch it off so we never set it to less than 1! */ alarm(1); rc = CURLRESOLV_TIMEDOUT; @@ -1037,18 +1043,20 @@ clean_up: } /* - * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been - * made, the struct may be destroyed due to pruning. It is important that only - * one unlock is made for each Curl_resolv() call. + * Curl_resolv_unlink() releases a reference to the given cached DNS entry. + * When the reference count reaches 0, the entry is destroyed. It is important + * that only one unlink is made for each Curl_resolv() call. * * May be called with 'data' == NULL for global cache. */ -void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) +void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns) { + struct Curl_dns_entry *dns = *pdns; + *pdns = NULL; if(data && data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - freednsentry(dns); + hostcache_unlink_entry(dns); if(data && data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -1057,13 +1065,13 @@ void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) /* * File-internal: release cache dns entry reference, free if inuse drops to 0 */ -static void freednsentry(void *freethis) +static void hostcache_unlink_entry(void *entry) { - struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; - DEBUGASSERT(dns && (dns->inuse>0)); + struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry; + DEBUGASSERT(dns && (dns->refcount>0)); - dns->inuse--; - if(dns->inuse == 0) { + dns->refcount--; + if(dns->refcount == 0) { Curl_freeaddrinfo(dns->addr); #ifdef USE_HTTPSRR if(dns->hinfo) { @@ -1092,7 +1100,7 @@ static void freednsentry(void *freethis) void Curl_init_dnscache(struct Curl_hash *hash, size_t size) { Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare, - freednsentry); + hostcache_unlink_entry); } /* @@ -1150,7 +1158,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - /* delete entry, ignore if it didn't exist */ + /* delete entry, ignore if it did not exist */ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); if(data->share) @@ -1264,7 +1272,7 @@ err: if(data->share) Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - /* See if it's already in our dns cache */ + /* See if it is already in our dns cache */ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { @@ -1285,13 +1293,11 @@ err: } /* put this new host in the cache */ - dns = Curl_cache_addr(data, head, host_begin, hlen, port); + dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent); if(dns) { - if(permanent) - dns->timestamp = 0; /* mark as permanent */ /* release the returned reference; the cache itself will keep the * entry alive: */ - dns->inuse--; + dns->refcount--; } if(data->share) @@ -1362,7 +1368,7 @@ static void show_resolve_info(struct Curl_easy *data, if(!result) result = Curl_dyn_add(d, buf); if(result) { - infof(data, "too many IP, can't show"); + infof(data, "too many IP, cannot show"); goto fail; } } @@ -1443,8 +1449,7 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) if(result) { Curl_detach_connection(data); - Curl_conncache_remove_conn(data, conn, TRUE); - Curl_disconnect(data, conn, TRUE); + Curl_cpool_disconnect(data, conn, TRUE); } return result; } diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index bf4e94d2f..b1c5ecb2e 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h @@ -80,7 +80,7 @@ struct Curl_https_rrinfo { char *alpns; /* keytag = 1 */ bool no_def_alpn; /* keytag = 2 */ /* - * we don't support ports (keytag = 3) as we don't support + * we do not support ports (keytag = 3) as we do not support * port-switching yet */ unsigned char *ipv4hints; /* keytag = 4 */ @@ -97,13 +97,13 @@ struct Curl_dns_entry { #ifdef USE_HTTPSRR struct Curl_https_rrinfo *hinfo; #endif - /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */ + /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ time_t timestamp; - /* use-counter, use Curl_resolv_unlock to release reference */ - long inuse; + /* reference counter, entry is freed on reaching 0 */ + size_t refcount; /* hostname port number that resolved to addr. */ int hostport; - /* hostname that resolved to addr. may be NULL (unix domain sockets). */ + /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ char hostname[1]; }; @@ -113,8 +113,8 @@ bool Curl_host_is_ipnum(const char *hostname); * Curl_resolv() returns an entry with the info for the specified host * and port. * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! + * The returned data *MUST* be "released" with Curl_resolv_unlink() after + * use, or we will leak memory! */ /* return codes */ enum resolve_t { @@ -161,9 +161,9 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, int *waitp); -/* unlock a previously resolved dns entry */ -void Curl_resolv_unlock(struct Curl_easy *data, - struct Curl_dns_entry *dns); +/* unlink a dns entry, potentially shared with a cache */ +void Curl_resolv_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns); /* init a new dns cache */ void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize); @@ -199,8 +199,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ip, * * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! + * The returned data *MUST* be "released" with Curl_resolv_unlink() after + * use, or we will leak memory! */ struct Curl_dns_entry * Curl_fetch_addr(struct Curl_easy *data, @@ -209,12 +209,13 @@ Curl_fetch_addr(struct Curl_easy *data, /* * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. - * + * @param permanent iff TRUE, entry will never become stale * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. */ struct Curl_dns_entry * Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, - const char *hostname, size_t hostlen, int port); + const char *hostname, size_t hostlen, int port, + bool permanent); #ifndef INADDR_NONE #define CURL_INADDR_NONE (in_addr_t) ~0 diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index 9140180ff..3bfea48d4 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c @@ -62,7 +62,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) { (void)data; if(conn->ip_version == CURL_IPRESOLVE_V6) - /* An IPv6 address was requested and we can't get/use one */ + /* An IPv6 address was requested and we cannot get/use one */ return FALSE; return TRUE; /* OK, proceed */ @@ -82,7 +82,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) * detect which one this platform supports in the configure script and set up * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME - * has the corresponding rules. This is primarily on *nix. Note that some unix + * has the corresponding rules. This is primarily on *nix. Note that some Unix * flavours have thread-safe versions of the plain gethostbyname() etc. * */ @@ -193,8 +193,8 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, * small. Previous versions are known to return ERANGE for the same * problem. * - * This wouldn't be such a big problem if older versions wouldn't - * sometimes return EAGAIN on a common failure case. Alas, we can't + * This would not be such a big problem if older versions would not + * sometimes return EAGAIN on a common failure case. Alas, we cannot * assume that EAGAIN *or* ERANGE means ERANGE for any given version of * glibc. * @@ -210,9 +210,9 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, * gethostbyname_r() in glibc: * * In glibc 2.2.5 the interface is different (this has also been - * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't + * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I cannot * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 - * (shipped/upgraded by Redhat 7.2) don't show this behavior! + * (shipped/upgraded by Redhat 7.2) do not show this behavior! * * In this "buggy" version, the return code is -1 on error and 'errno' * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a @@ -221,9 +221,9 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, if(!h) /* failure */ #elif defined(HAVE_GETHOSTBYNAME_R_3) - /* AIX, Digital Unix/Tru64, HPUX 10, more? */ + /* AIX, Digital UNIX/Tru64, HP-UX 10, more? */ - /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of + /* For AIX 4.3 or later, we do not use gethostbyname_r() at all, because of * the plain fact that it does not return unique full buffers on each * call, but instead several of the pointers in the hostent structs will * point to the same actual data! This have the unfortunate down-side that @@ -237,7 +237,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, * * Troels Walsted Hansen helped us work this out on March 3rd, 2003. * - * [*] = much later we've found out that it isn't at all "completely + * [*] = much later we have found out that it is not at all "completely * thread-safe", but at least the gethostbyname() function is. */ @@ -253,7 +253,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, (struct hostent *)buf, (struct hostent_data *)((char *)buf + sizeof(struct hostent))); - h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */ + h_errnop = SOCKERRNO; /* we do not deal with this, but set it anyway */ } else res = -1; /* failure, too smallish buffer size */ @@ -263,8 +263,8 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, h = buf; /* result expected in h */ /* This is the worst kind of the different gethostbyname_r() interfaces. - * Since we don't know how big buffer this particular lookup required, - * we can't realloc down the huge alloc without doing closer analysis of + * Since we do not know how big buffer this particular lookup required, + * we cannot realloc down the huge alloc without doing closer analysis of * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every * name lookup. Fixing this would require an extra malloc() and then * calling Curl_addrinfo_copy() that subsequent realloc()s down the new @@ -280,7 +280,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #else /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || HAVE_GETHOSTBYNAME_R */ /* - * Here is code for platforms that don't have a thread safe + * Here is code for platforms that do not have a thread safe * getaddrinfo() nor gethostbyname_r() function or for which * gethostbyname() is the preferred one. */ diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index 18969a7a7..c16ddfe58 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c @@ -124,7 +124,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, #ifndef USE_RESOLVE_ON_IPS /* * The AI_NUMERICHOST must not be set to get synthesized IPv6 address from - * an IPv4 address on iOS and Mac OS X. + * an IPv4 address on iOS and macOS. */ if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) || (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) { diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index a5e767613..a5c216f6d 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c @@ -54,7 +54,7 @@ #define MAX_HSTS_DATELENSTR "64" #define UNLIMITED "unlimited" -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) || defined(UNITTESTS) /* to play well with debug builds, we can *set* a fixed time this will return */ time_t deltatime; /* allow for "adjustments" for unit test purposes */ @@ -94,11 +94,11 @@ void Curl_hsts_cleanup(struct hsts **hp) { struct hsts *h = *hp; if(h) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; + struct Curl_llist_node *e; + struct Curl_llist_node *n; + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + n = Curl_node_next(e); hsts_free(sts); } free(h->filename); @@ -215,7 +215,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, /* remove the entry if present verbatim (without subdomain match) */ sts = Curl_hsts(h, hostname, FALSE); if(sts) { - Curl_llist_remove(&h->list, &sts->node, NULL); + Curl_node_remove(&sts->node); hsts_free(sts); } return CURLE_OK; @@ -241,7 +241,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, } /* - * Return TRUE if the given host name is currently an HSTS one. + * Return TRUE if the given hostname is currently an HSTS one. * * The 'subdomain' argument tells the function if subdomain matching should be * attempted. @@ -253,8 +253,8 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, char buffer[MAX_HSTS_HOSTLEN + 1]; time_t now = time(NULL); size_t hlen = strlen(hostname); - struct Curl_llist_element *e; - struct Curl_llist_element *n; + struct Curl_llist_node *e; + struct Curl_llist_node *n; if((hlen > MAX_HSTS_HOSTLEN) || !hlen) return NULL; @@ -265,12 +265,12 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, buffer[hlen] = 0; hostname = buffer; - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + n = Curl_node_next(e); if(sts->expires <= now) { /* remove expired entries */ - Curl_llist_remove(&h->list, &sts->node, NULL); + Curl_node_remove(&sts->node); hsts_free(sts); continue; } @@ -353,8 +353,8 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp) CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, const char *file) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; + struct Curl_llist_node *e; + struct Curl_llist_node *n; CURLcode result = CURLE_OK; FILE *out; char *tempstore = NULL; @@ -368,7 +368,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, file = h->filename; if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0]) - /* marked as read-only, no file or zero length file name */ + /* marked as read-only, no file or zero length filename */ goto skipsave; result = Curl_fopen(data, file, &out, &tempstore); @@ -376,9 +376,9 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n" "# This file was generated by libcurl! Edit at your own risk.\n", out); - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + n = Curl_node_next(e); result = hsts_out(sts, out); if(result) break; @@ -393,14 +393,14 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, free(tempstore); skipsave: if(data->set.hsts_write) { - /* if there's a write callback */ + /* if there is a write callback */ struct curl_index i; /* count */ - i.total = h->list.size; + i.total = Curl_llist_count(&h->list); i.index = 0; - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); bool stop; - n = e->next; + n = Curl_node_next(e); result = hsts_push(data, &i, sts, &stop); if(result || stop) break; @@ -440,7 +440,7 @@ static CURLcode hsts_add(struct hsts *h, char *line) if(!e) result = hsts_create(h, p, subdomain, expires); else { - /* the same host name, use the largest expire time */ + /* the same hostname, use the largest expire time */ if(expires > e->expires) e->expires = expires; } @@ -508,7 +508,7 @@ static CURLcode hsts_load(struct hsts *h, const char *file) CURLcode result = CURLE_OK; FILE *fp; - /* we need a private copy of the file name so that the hsts cache file + /* we need a private copy of the filename so that the hsts cache file name survives an easy handle reset */ free(h->filename); h->filename = strdup(file); diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h index d3431a5d7..1c544f97b 100644 --- a/Utilities/cmcurl/lib/hsts.h +++ b/Utilities/cmcurl/lib/hsts.h @@ -29,18 +29,18 @@ #include #include "llist.h" -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) || defined(UNITTESTS) extern time_t deltatime; #endif struct stsentry { - struct Curl_llist_element node; + struct Curl_llist_node node; const char *host; bool includeSubDomains; curl_off_t expires; /* the timestamp of this entry's expiry */ }; -/* The HSTS cache. Needs to be able to tailmatch host names. */ +/* The HSTS cache. Needs to be able to tailmatch hostnames. */ struct hsts { struct Curl_llist list; char *filename; diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 2a41f8078..f00c803af 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -169,14 +169,6 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, { /* allocate the HTTP-specific struct for the Curl_easy, only to survive during this request */ - struct HTTP *http; - DEBUGASSERT(data->req.p.http == NULL); - - http = calloc(1, sizeof(struct HTTP)); - if(!http) - return CURLE_OUT_OF_MEMORY; - - data->req.p.http = http; connkeep(conn, "HTTP default"); if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) { @@ -418,9 +410,9 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, curl_off_t upload_remain = (expectsend >= 0)? (expectsend - bytessent) : -1; bool little_upload_remains = (upload_remain >= 0 && upload_remain < 2000); bool needs_rewind = Curl_creader_needs_rewind(data); - /* By default, we'd like to abort the transfer when little or - * unknown amount remains. But this may be overridden by authentications - * further below! */ + /* By default, we would like to abort the transfer when little or unknown + * amount remains. This may be overridden by authentications further + * below! */ bool abort_upload = (!data->req.upload_done && !little_upload_remains); const char *ongoing_auth = NULL; @@ -470,20 +462,19 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, if(abort_upload) { if(upload_remain >= 0) - infof(data, "%s%sclose instead of sending %" - CURL_FORMAT_CURL_OFF_T " more bytes", - ongoing_auth? ongoing_auth : "", - ongoing_auth? " send, " : "", - upload_remain); + infof(data, "%s%sclose instead of sending %" FMT_OFF_T " more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : "", + upload_remain); else infof(data, "%s%sclose instead of sending unknown amount " - "of more bytes", - ongoing_auth? ongoing_auth : "", - ongoing_auth? " send, " : ""); + "of more bytes", + ongoing_auth? ongoing_auth : "", + ongoing_auth? " send, " : ""); /* We decided to abort the ongoing transfer */ streamclose(conn, "Mid-auth HTTP and much data left to send"); /* FIXME: questionable manipulation here, can we do this differently? */ - data->req.size = 0; /* don't download any more than 0 bytes */ + data->req.size = 0; /* do not download any more than 0 bytes */ } return CURLE_OK; } @@ -556,7 +547,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) /* no (known) authentication available, authentication is not "done" yet and no authentication seems to be required and - we didn't try HEAD or GET */ + we did not try HEAD or GET */ if((data->state.httpreq != HTTPREQ_GET) && (data->state.httpreq != HTTPREQ_HEAD)) { data->req.newurl = strdup(data->state.url); /* clone URL */ @@ -746,13 +737,13 @@ Curl_http_output_auth(struct Curl_easy *data, if(authhost->want && !authhost->picked) /* The app has selected one or more methods, but none has been picked so far by a server round-trip. Then we set the picked one to the - want one, and if this is one single bit it'll be used instantly. */ + want one, and if this is one single bit it will be used instantly. */ authhost->picked = authhost->want; if(authproxy->want && !authproxy->picked) /* The app has selected one or more methods, but none has been picked so far by a proxy round-trip. Then we set the picked one to the want one, - and if this is one single bit it'll be used instantly. */ + and if this is one single bit it will be used instantly. */ authproxy->picked = authproxy->want; #ifndef CURL_DISABLE_PROXY @@ -767,7 +758,7 @@ Curl_http_output_auth(struct Curl_easy *data, #else (void)proxytunnel; #endif /* CURL_DISABLE_PROXY */ - /* we have no proxy so let's pretend we're done authenticating + /* we have no proxy so let's pretend we are done authenticating with it */ authproxy->done = TRUE; @@ -941,7 +932,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, authp->avail |= CURLAUTH_DIGEST; /* We call this function on input Digest headers even if Digest - * authentication isn't activated yet, as we need to store the + * authentication is not activated yet, as we need to store the * incoming data from this header in case we are going to use * Digest */ result = Curl_input_digest(data, proxy, auth); @@ -960,7 +951,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, authp->avail |= CURLAUTH_BASIC; if(authp->picked == CURLAUTH_BASIC) { /* We asked for Basic authentication but got a 40X back - anyway, which basically means our name+password isn't + anyway, which basically means our name+password is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Authentication problem. Ignoring this."); @@ -976,7 +967,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, authp->avail |= CURLAUTH_BEARER; if(authp->picked == CURLAUTH_BEARER) { /* We asked for Bearer authentication but got a 40X back - anyway, which basically means our token isn't valid. */ + anyway, which basically means our token is not valid. */ authp->avail = CURLAUTH_NONE; infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; @@ -996,7 +987,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, /* there may be multiple methods on one line, so keep reading */ while(*auth && *auth != ',') /* read up to the next comma */ auth++; - if(*auth == ',') /* if we're on a comma, skip it */ + if(*auth == ',') /* if we are on a comma, skip it */ auth++; while(*auth && ISSPACE(*auth)) auth++; @@ -1019,8 +1010,8 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) DEBUGASSERT(data->conn); /* - ** If we haven't been asked to fail on error, - ** don't fail. + ** If we have not been asked to fail on error, + ** do not fail. */ if(!data->set.http_fail_on_error) return FALSE; @@ -1040,7 +1031,7 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) return FALSE; /* - ** Any code >= 400 that's not 401 or 407 is always + ** Any code >= 400 that is not 401 or 407 is always ** a terminal error */ if((httpcode != 401) && (httpcode != 407)) @@ -1052,22 +1043,19 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) DEBUGASSERT((httpcode == 401) || (httpcode == 407)); /* - ** Examine the current authentication state to see if this - ** is an error. The idea is for this function to get - ** called after processing all the headers in a response - ** message. So, if we've been to asked to authenticate a - ** particular stage, and we've done it, we're OK. But, if - ** we're already completely authenticated, it's not OK to - ** get another 401 or 407. + ** Examine the current authentication state to see if this is an error. The + ** idea is for this function to get called after processing all the headers + ** in a response message. So, if we have been to asked to authenticate a + ** particular stage, and we have done it, we are OK. If we are already + ** completely authenticated, it is not OK to get another 401 or 407. ** - ** It is possible for authentication to go stale such that - ** the client needs to reauthenticate. Once that info is - ** available, use it here. + ** It is possible for authentication to go stale such that the client needs + ** to reauthenticate. Once that info is available, use it here. */ /* - ** Either we're not authenticating, or we're supposed to - ** be authenticating something else. This is an error. + ** Either we are not authenticating, or we are supposed to be authenticating + ** something else. This is an error. */ if((httpcode == 401) && !data->state.aptr.user) return TRUE; @@ -1106,7 +1094,7 @@ Curl_compareheader(const char *headerline, /* line to check */ DEBUGASSERT(content); if(!strncasecompare(headerline, header, hlen)) - return FALSE; /* doesn't start with header */ + return FALSE; /* does not start with header */ /* pass the header */ start = &headerline[hlen]; @@ -1118,11 +1106,11 @@ Curl_compareheader(const char *headerline, /* line to check */ /* find the end of the header line */ end = strchr(start, '\r'); /* lines end with CRLF */ if(!end) { - /* in case there's a non-standard compliant line here */ + /* in case there is a non-standard compliant line here */ end = strchr(start, '\n'); if(!end) - /* hm, there's no line ending here, use the zero byte! */ + /* hm, there is no line ending here, use the zero byte! */ end = strchr(start, '\0'); } @@ -1153,7 +1141,7 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) } /* this returns the socket to wait for in the DO and DOING state for the multi - interface and then we're always _sending_ a request and thus we wait for + interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn, @@ -1174,16 +1162,12 @@ CURLcode Curl_http_done(struct Curl_easy *data, CURLcode status, bool premature) { struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; - /* Clear multipass flag. If authentication isn't done yet, then it will get + /* Clear multipass flag. If authentication is not done yet, then it will get * a chance to be set back to true when we output the next auth header */ data->state.authhost.multipass = FALSE; data->state.authproxy.multipass = FALSE; - if(!http) - return CURLE_OK; - Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); @@ -1197,8 +1181,8 @@ CURLcode Curl_http_done(struct Curl_easy *data, (data->req.bytecount + data->req.headerbytecount - data->req.deductheadercount) <= 0) { - /* If this connection isn't simply closed to be retried, AND nothing was - read from the HTTP server (that counts), this can't be right so we + /* If this connection is not simply closed to be retried, AND nothing was + read from the HTTP server (that counts), this cannot be right so we return an error here */ failf(data, "Empty reply from server"); /* Mark it as closed to avoid the "left intact" message */ @@ -1357,7 +1341,7 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, DEBUGASSERT(name && value); if(data->state.aptr.host && - /* a Host: header was sent already, don't pass on any custom Host: + /* a Host: header was sent already, do not pass on any custom Host: header as that will produce *two* in the same request! */ hd_name_eq(name, namelen, STRCONST("Host:"))) ; @@ -1370,18 +1354,18 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, hd_name_eq(name, namelen, STRCONST("Content-Type:"))) ; else if(data->req.authneg && - /* while doing auth neg, don't allow the custom length since + /* while doing auth neg, do not allow the custom length since we will force length zero then */ hd_name_eq(name, namelen, STRCONST("Content-Length:"))) ; else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, don't pass on a custom + /* when asking for Transfer-Encoding, do not pass on a custom Connection: */ hd_name_eq(name, namelen, STRCONST("Connection:"))) ; else if((conn->httpversion >= 20) && hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) - /* HTTP/2 doesn't support chunked requests */ + /* HTTP/2 does not support chunked requests */ ; else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || hd_name_eq(name, namelen, STRCONST("Cookie:"))) && @@ -1503,8 +1487,9 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, char *compare = semicolonp ? semicolonp : headers->data; if(data->state.aptr.host && - /* a Host: header was sent already, don't pass on any custom Host: - header as that will produce *two* in the same request! */ + /* a Host: header was sent already, do not pass on any custom + Host: header as that will produce *two* in the same + request! */ checkprefix("Host:", compare)) ; else if(data->state.httpreq == HTTPREQ_POST_FORM && @@ -1516,18 +1501,18 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, checkprefix("Content-Type:", compare)) ; else if(data->req.authneg && - /* while doing auth neg, don't allow the custom length since + /* while doing auth neg, do not allow the custom length since we will force length zero then */ checkprefix("Content-Length:", compare)) ; else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, don't pass on a custom + /* when asking for Transfer-Encoding, do not pass on a custom Connection: */ checkprefix("Connection:", compare)) ; else if((conn->httpversion >= 20) && checkprefix("Transfer-Encoding:", compare)) - /* HTTP/2 doesn't support chunked requests */ + /* HTTP/2 does not support chunked requests */ ; else if((checkprefix("Authorization:", compare) || checkprefix("Cookie:", compare)) && @@ -1719,10 +1704,10 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) if(ptr && (!data->state.this_is_a_follow || strcasecompare(data->state.first_host, conn->host.name))) { #if !defined(CURL_DISABLE_COOKIES) - /* If we have a given custom Host: header, we extract the host name in + /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the custom Host: header if this is NOT a redirect, as setting Host: in the - redirected request is being out on thin ice. Except if the host name + redirected request is being out on thin ice. Except if the hostname is the same as the first one! */ char *cookiehost = Curl_copy_header_value(ptr); if(!cookiehost) @@ -1760,15 +1745,15 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) } } else { - /* When building Host: headers, we must put the host name within - [brackets] if the host name is a plain IPv6-address. RFC2732-style. */ + /* When building Host: headers, we must put the hostname within + [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */ const char *host = conn->host.name; if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) && (conn->remote_port == PORT_HTTPS)) || ((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 + /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ aptr->host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"", host, conn->bits.ipv6_ip?"]":""); @@ -1778,7 +1763,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) conn->remote_port); if(!aptr->host) - /* without Host: we can't make a nice request */ + /* without Host: we cannot make a nice request */ return CURLE_OUT_OF_MEMORY; } return CURLE_OK; @@ -1806,7 +1791,7 @@ CURLcode Curl_http_target(struct Curl_easy *data, /* The path sent to the proxy is in fact the entire URL. But if the remote host is a IDN-name, we must make sure that the request we produce only - uses the encoded host name! */ + uses the encoded hostname! */ /* and no fragment part */ CURLUcode uc; @@ -1829,7 +1814,7 @@ CURLcode Curl_http_target(struct Curl_easy *data, } if(strcasecompare("http", data->state.up.scheme)) { - /* when getting HTTP, we don't want the userinfo the URL */ + /* when getting HTTP, we do not want the userinfo the URL */ uc = curl_url_set(h, CURLUPART_USER, NULL, 0); if(uc) { curl_url_cleanup(h); @@ -1850,7 +1835,7 @@ CURLcode Curl_http_target(struct Curl_easy *data, curl_url_cleanup(h); - /* target or url */ + /* target or URL */ result = Curl_dyn_add(r, data->set.str[STRING_TARGET]? data->set.str[STRING_TARGET]:url); free(url); @@ -2053,7 +2038,7 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) if(data->state.resume_from < 0) { /* * This is meant to get the size of the present remote-file by itself. - * We don't support this now. Bail out! + * We do not support this now. Bail out! */ data->state.resume_from = 0; } @@ -2063,7 +2048,7 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) CURLcode result; result = Curl_creader_resume_from(data, data->state.resume_from); if(result) { - failf(data, "Unable to resume from offset %" CURL_FORMAT_CURL_OFF_T, + failf(data, "Unable to resume from offset %" FMT_OFF_T, data->state.resume_from); return result; } @@ -2138,7 +2123,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, if(data->req.upgr101 != UPGR101_INIT) return CURLE_OK; - /* For really small puts we don't use Expect: headers at all, and for + /* For really small puts we do not use Expect: headers at all, and for the somewhat bigger ones we allow the app to disable it. Just make sure that the expect100header is always set to the preferred value here. */ @@ -2190,7 +2175,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, case HTTPREQ_POST_MIME: #endif /* We only set Content-Length and allow a custom Content-Length if - we don't upload data chunked, as RFC2616 forbids us to set both + we do not upload data chunked, as RFC2616 forbids us to set both kinds of headers (Transfer-Encoding: chunked and Content-Length). We do not override a custom "Content-Length" header, but during authentication negotiation that header is suppressed. @@ -2199,10 +2184,9 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, (data->req.authneg || !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* we allow replacing this header if not during auth negotiation, - although it isn't very wise to actually set your own */ - result = Curl_dyn_addf(r, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", req_clen); + although it is not very wise to actually set your own */ + result = Curl_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n", + req_clen); } if(result) goto out; @@ -2247,7 +2231,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, out: if(!result) { /* setup variables for the upcoming transfer */ - Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); } return result; } @@ -2335,7 +2319,7 @@ CURLcode Curl_http_range(struct Curl_easy *data, { if(data->state.use_range) { /* - * A range is selected. We use different headers whether we're downloading + * A range is selected. We use different headers whether we are downloading * or uploading and we always let customized headers override our internal * ones if any such are specified. */ @@ -2353,12 +2337,11 @@ CURLcode Curl_http_range(struct Curl_easy *data, free(data->state.aptr.rangeline); if(data->set.set_resume_from < 0) { - /* Upload resume was asked for, but we don't know the size of the + /* Upload resume was asked for, but we do not know the size of the remote part so we tell the server (and act accordingly) that we upload the whole file (again) */ data->state.aptr.rangeline = - aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "\r\n", + aprintf("Content-Range: bytes 0-%" FMT_OFF_T "/%" FMT_OFF_T "\r\n", req_clen - 1, req_clen); } @@ -2371,15 +2354,14 @@ CURLcode Curl_http_range(struct Curl_easy *data, data->state.infilesize : (data->state.resume_from + req_clen); data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "\r\n", + aprintf("Content-Range: bytes %s%" FMT_OFF_T "/%" FMT_OFF_T "\r\n", data->state.range, total_len-1, total_len); } else { /* Range was selected and then we just pass the incoming range and append total size */ data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n", + aprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", data->state.range, req_clen); } if(!data->state.aptr.rangeline) @@ -2397,12 +2379,12 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) if(data->req.newurl) { if(conn->bits.close) { /* Abort after the headers if "follow Location" is set - and we're set to close anyway. */ + and we are set to close anyway. */ k->keepon &= ~KEEP_RECV; k->done = TRUE; return CURLE_OK; } - /* We have a new url to load, but since we want to be able to reuse this + /* We have a new URL to load, but since we want to be able to reuse this connection properly, we read the full response in "ignore more" */ k->ignorebody = TRUE; infof(data, "Ignoring the response-body"); @@ -2413,7 +2395,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) if(k->size == data->state.resume_from) { /* The resume point is at the end of file, consider this fine even if it - doesn't allow resume from here. */ + does not allow resume from here. */ infof(data, "The entire document is already downloaded"); streamclose(conn, "already downloaded"); /* Abort download */ @@ -2422,10 +2404,10 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) return CURLE_OK; } - /* we wanted to resume a download, although the server doesn't seem to - * support this and we did this with a GET (if it wasn't a GET we did a + /* we wanted to resume a download, although the server does not seem to + * support this and we did this with a GET (if it was not a GET we did a * POST or PUT resume) */ - failf(data, "HTTP server doesn't seem to support " + failf(data, "HTTP server does not seem to support " "byte ranges. Cannot resume."); return CURLE_RANGE_ERROR; } @@ -2437,7 +2419,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) if(!Curl_meets_timecondition(data, k->timeofdoc)) { k->done = TRUE; - /* We're simulating an HTTP 304 from server so we return + /* We are simulating an HTTP 304 from server so we return what should have been returned from the server */ data->info.httpcode = 304; infof(data, "Simulate an HTTP 304 response"); @@ -2459,7 +2441,7 @@ CURLcode Curl_transferencode(struct Curl_easy *data) /* When we are to insert a TE: header in the request, we must also insert TE in a Connection: header, so we need to merge the custom provided Connection: header and prevent the original to get sent. Note that if - the user has inserted his/her own TE: header we don't do this magic + the user has inserted his/her own TE: header we do not do this magic but then assume that the user will handle it all! */ char *cptr = Curl_checkheaders(data, STRCONST("Connection")); #define TE_HEADER "TE: gzip\r\n" @@ -2705,7 +2687,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!(conn->handler->flags&PROTOPT_SSL) && conn->httpversion < 20 && (data->state.httpwant == CURL_HTTP_VERSION_2)) { - /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done + /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done over SSL */ result = Curl_http2_request_upgrade(&req, data); if(result) { @@ -2849,7 +2831,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC v = (data->asi && ((data->conn->handler->flags & PROTOPT_SSL) || -#ifdef CURLDEBUG +#ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") #else @@ -2901,7 +2883,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, * Process Content-Encoding. Look for the values: identity, * gzip, deflate, compress, x-gzip and x-compress. x-gzip and * x-compress are the same as gzip and compress. (Sec 3.5 RFC - * 2616). zlib cannot handle compress. However, errors are + * 2616). zlib cannot handle compress. However, errors are * handled further down when the response body is processed */ return Curl_build_unencoding_stack(data, v, FALSE); @@ -2936,7 +2918,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, /* * An HTTP/1.0 reply with the 'Connection: keep-alive' line * tells us the connection will be kept alive for our - * pleasure. Default action for 1.0 is to close. + * pleasure. Default action for 1.0 is to close. * * [RFC2068, section 19.7.1] */ connkeep(conn, "Connection keep-alive"); @@ -3029,13 +3011,13 @@ CURLcode Curl_http_header(struct Curl_easy *data, * connection will be kept alive for our pleasure. * Default action for 1.0 is to close. */ - connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ + connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } else if((conn->httpversion == 11) && conn->bits.httpproxy && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { /* - * We get an HTTP/1.1 response from a proxy and it says it'll + * We get an HTTP/1.1 response from a proxy and it says it will * close down after this transfer. */ connclose(conn, "Proxy-Connection: asked to close after done"); @@ -3095,7 +3077,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, HD_VAL(hd, hdlen, "Set-Cookie:") : NULL; if(v) { /* If there is a custom-set Host: name, use it here, or else use - * real peer host name. */ + * real peer hostname. */ const char *host = data->state.aptr.cookiehost? data->state.aptr.cookiehost:conn->host.name; const bool secure_context = @@ -3116,7 +3098,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && ((conn->handler->flags & PROTOPT_SSL) || -#ifdef CURLDEBUG +#ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") #else @@ -3132,7 +3114,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifdef DEBUGBUILD else infof(data, "Parsed STS header fine (%zu entries)", - data->hsts->list.size); + Curl_llist_count(&data->hsts->list)); #endif } #endif @@ -3160,7 +3142,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, if(result) return result; if(!k->chunk && data->set.http_transfer_encoding) { - /* if this isn't chunked, only close can signal the end of this + /* if this is not chunked, only close can signal the end of this * transfer as Content-Length is said not to be trusted for * transfer-encoding! */ connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); @@ -3168,6 +3150,11 @@ CURLcode Curl_http_header(struct Curl_easy *data, } return CURLE_OK; } + v = HD_VAL(hd, hdlen, "Trailer:"); + if(v) { + data->req.resp_trailer = TRUE; + return CURLE_OK; + } break; case 'w': case 'W': @@ -3231,11 +3218,11 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, data->state.httpversion = (unsigned char)k->httpversion; /* - * This code executes as part of processing the header. As a - * result, it's not totally clear how to interpret the + * This code executes as part of processing the header. As a + * result, it is not totally clear how to interpret the * response code yet as that depends on what other headers may - * be present. 401 and 407 may be errors, but may be OK - * depending on how authentication is working. Other codes + * be present. 401 and 407 may be errors, but may be OK + * depending on how authentication is working. Other codes * are definitely errors, so give up here. */ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && @@ -3255,9 +3242,6 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, else if(k->httpversion == 20 || (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 */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; } k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; @@ -3287,7 +3271,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, } /* Content-Length must be ignored if any Transfer-Encoding is present in the - response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is + response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is figured out here after all headers have been received but before the final call to the user's header callback, so that a valid content length can be retrieved by the user in the final call. */ @@ -3299,10 +3283,13 @@ CURLcode Curl_http_size(struct Curl_easy *data) } else if(k->size != -1) { if(data->set.max_filesize && - k->size > data->set.max_filesize) { + !k->ignorebody && + (k->size > data->set.max_filesize)) { failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; } + if(k->ignorebody) + infof(data, "setting size while ignoring"); Curl_pgrsSetDownloadSize(data, k->size); k->maxdownload = k->size; } @@ -3323,7 +3310,7 @@ static CURLcode verify_header(struct Curl_easy *data, /* the first "header" is the status-line and it has no colon */ return CURLE_OK; if(((hd[0] == ' ') || (hd[0] == '\t')) && k->headerline > 2) - /* line folding, can't happen on line 2 */ + /* line folding, cannot happen on line 2 */ ; else { ptr = memchr(hd, ':', hdlen); @@ -3363,8 +3350,35 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data, return CURLE_OK; } +static CURLcode http_write_header(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + CURLcode result; + int writetype; + + /* now, only output this if the header AND body are requested: + */ + Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); + + writetype = CLIENTWRITE_HEADER | + ((data->req.httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); + + result = Curl_client_write(data, writetype, hd, hdlen); + if(result) + return result; + + result = Curl_bump_headersize(data, hdlen, FALSE); + if(result) + return result; + + data->req.deductheadercount = (100 <= data->req.httpcode && + 199 >= data->req.httpcode)? + data->req.headerbytecount:0; + return result; +} static CURLcode http_on_response(struct Curl_easy *data, + const char *last_hd, size_t last_hd_len, const char *buf, size_t blen, size_t *pconsumed) { @@ -3380,13 +3394,21 @@ static CURLcode http_on_response(struct Curl_easy *data, if(conn->httpversion != 20) infof(data, "Lying server, not serving HTTP/2"); } - if(conn->httpversion < 20) { - conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + + if(k->httpcode < 200 && last_hd) { + /* Intermediate responses might trigger processing of more + * responses, write the last header to the client before + * proceeding. */ + result = http_write_header(data, last_hd, last_hd_len); + last_hd = NULL; /* handled it */ + if(result) + goto out; } if(k->httpcode < 100) { failf(data, "Unsupported response code in HTTP response"); - return CURLE_UNSUPPORTED_PROTOCOL; + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; } else if(k->httpcode < 200) { /* "A user agent MAY ignore unexpected 1xx status responses." @@ -3405,15 +3427,18 @@ static CURLcode http_on_response(struct Curl_easy *data, break; case 101: /* Switching Protocols only allowed from HTTP/1.1 */ + if(conn->httpversion != 11) { /* invalid for other HTTP versions */ failf(data, "unexpected 101 response code"); - return CURLE_WEIRD_SERVER_REPLY; + result = CURLE_WEIRD_SERVER_REPLY; + goto out; } if(k->upgr101 == UPGR101_H2) { /* Switching to HTTP/2, where we will get more responses */ infof(data, "Received 101, Switching to HTTP/2"); k->upgr101 = UPGR101_RECEIVED; + data->conn->bits.asks_multiplex = FALSE; /* We expect more response from HTTP/2 later */ k->header = TRUE; k->headerline = 0; /* restart the header line counter */ @@ -3421,7 +3446,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * be processed. */ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); if(result) - return result; + goto out; *pconsumed += blen; } #ifdef USE_WEBSOCKETS @@ -3430,7 +3455,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * WebSockets format and taken in by the protocol handler. */ result = Curl_ws_accept(data, buf, blen); if(result) - return result; + goto out; *pconsumed += blen; /* ws accept handled the data */ k->header = FALSE; /* we will not get more responses */ if(data->set.connect_only) @@ -3451,7 +3476,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * to receive a final response eventually. */ break; } - return result; + goto out; } /* k->httpcode >= 200, final response */ @@ -3460,6 +3485,7 @@ static CURLcode http_on_response(struct Curl_easy *data, if(k->upgr101 == UPGR101_H2) { /* A requested upgrade was denied, poke the multi handle to possibly allow a pending pipewait to continue */ + data->conn->bits.asks_multiplex = FALSE; Curl_multi_connchanged(data->multi); } @@ -3509,10 +3535,11 @@ static CURLcode http_on_response(struct Curl_easy *data, #endif #ifdef USE_WEBSOCKETS - /* All >=200 HTTP status codes are errors when wanting websockets */ + /* All >=200 HTTP status codes are errors when wanting WebSockets */ if(data->req.upgr101 == UPGR101_WS) { failf(data, "Refused WebSockets upgrade: %d", k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; + result = CURLE_HTTP_RETURNED_ERROR; + goto out; } #endif @@ -3520,7 +3547,8 @@ static CURLcode http_on_response(struct Curl_easy *data, if(http_should_fail(data, data->req.httpcode)) { failf(data, "The requested URL returned error: %d", k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; + result = CURLE_HTTP_RETURNED_ERROR; + goto out; } /* Curl_http_auth_act() checks what authentication methods @@ -3528,7 +3556,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * use. It will set 'newurl' if an auth method was picked. */ result = Curl_http_auth_act(data); if(result) - return result; + goto out; if(k->httpcode >= 300) { if((!data->req.authneg) && !conn->bits.close && @@ -3551,7 +3579,7 @@ static CURLcode http_on_response(struct Curl_easy *data, case HTTPREQ_POST_MIME: /* We got an error response. If this happened before the whole * request body has been sent we stop sending and mark the - * connection for closure after we've read the entire response. + * connection for closure after we have read the entire response. */ if(!Curl_req_done_sending(data)) { if((k->httpcode == 417) && Curl_http_exp100_is_selected(data)) { @@ -3566,7 +3594,7 @@ static CURLcode http_on_response(struct Curl_easy *data, "Stop sending data before everything sent"); result = http_perhapsrewind(data, conn); if(result) - return result; + goto out; } data->state.disableexpect = TRUE; DEBUGASSERT(!data->req.newurl); @@ -3582,7 +3610,7 @@ static CURLcode http_on_response(struct Curl_easy *data, streamclose(conn, "Stop sending data before everything sent"); result = Curl_req_abort_sending(data); if(result) - return result; + goto out; } } break; @@ -3600,13 +3628,6 @@ static CURLcode http_on_response(struct Curl_easy *data, } - /* This is the last response that we will got for the current request. - * Check on the body size and determine if the response is complete. - */ - result = Curl_http_size(data); - if(result) - return result; - /* If we requested a "no body", this is a good time to get * out and return home. */ @@ -3614,9 +3635,9 @@ static CURLcode http_on_response(struct Curl_easy *data, k->download_done = TRUE; /* If max download size is *zero* (nothing) we already have - nothing and can safely return ok now! But for HTTP/2, we'd + nothing and can safely return ok now! But for HTTP/2, we would like to call http2_handle_stream_close to properly close a - stream. In order to do this, we keep reading until we + stream. In order to do this, we keep reading until we close the stream. */ if(0 == k->maxdownload && !Curl_conn_is_http2(data, conn, FIRSTSOCKET) @@ -3624,7 +3645,22 @@ static CURLcode http_on_response(struct Curl_easy *data, k->download_done = TRUE; /* final response without error, prepare to receive the body */ - return Curl_http_firstwrite(data); + result = Curl_http_firstwrite(data); + + if(!result) + /* This is the last response that we get for the current request. + * Check on the body size and determine if the response is complete. + */ + result = Curl_http_size(data); + +out: + if(last_hd) { + /* if not written yet, write it now */ + CURLcode r2 = http_write_header(data, last_hd, last_hd_len); + if(!result) + result = r2; + } + return result; } static CURLcode http_rw_hd(struct Curl_easy *data, @@ -3639,36 +3675,25 @@ static CURLcode http_rw_hd(struct Curl_easy *data, *pconsumed = 0; if((0x0a == *hd) || (0x0d == *hd)) { /* Empty header line means end of headers! */ + struct dynbuf last_header; size_t consumed; - /* now, only output this if the header AND body are requested: - */ - Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen); - - writetype = CLIENTWRITE_HEADER | - ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); - - result = Curl_client_write(data, writetype, hd, hdlen); - if(result) - return result; - - result = Curl_bump_headersize(data, hdlen, FALSE); + Curl_dyn_init(&last_header, hdlen + 1); + result = Curl_dyn_addn(&last_header, hd, hdlen); if(result) return result; - data->req.deductheadercount = - (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0; - /* analyze the response to find out what to do. */ /* Caveat: we clear anything in the header brigade, because a * response might switch HTTP version which may call use recursively. * Not nice, but that is currently the way of things. */ Curl_dyn_reset(&data->state.headerb); - result = http_on_response(data, buf_remain, blen, &consumed); - if(result) - return result; + result = http_on_response(data, Curl_dyn_ptr(&last_header), + Curl_dyn_len(&last_header), + buf_remain, blen, &consumed); *pconsumed += consumed; - return CURLE_OK; + Curl_dyn_free(&last_header); + return result; } /* @@ -3681,14 +3706,14 @@ static CURLcode http_rw_hd(struct Curl_easy *data, or else we consider this to be the body right away! */ bool fine_statusline = FALSE; - k->httpversion = 0; /* Don't know yet */ + k->httpversion = 0; /* Do not know yet */ if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { /* * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 * * The response code is always a three-digit number in HTTP as the spec * says. We allow any three-digit number here, but we cannot make - * guarantees on future behaviors since it isn't within the protocol. + * guarantees on future behaviors since it is not within the protocol. */ const char *p = hd; @@ -4405,9 +4430,18 @@ static CURLcode cr_exp100_read(struct Curl_easy *data, switch(ctx->state) { case EXP100_SENDING_REQUEST: + if(!Curl_req_sendbuf_empty(data)) { + /* The initial request data has not been fully sent yet. Do + * not start the timer yet. */ + DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); + *nread = 0; + *eos = FALSE; + return CURLE_OK; + } /* We are now waiting for a reply from the server or - * a timeout on our side */ - DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE")); + * a timeout on our side IFF the request has been fully sent. */ + DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " + "timeout %ldms", data->set.expect_100_timeout)); ctx->state = EXP100_AWAITING_CONTINUE; ctx->start = Curl_now(); Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); @@ -4431,7 +4465,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data, *eos = FALSE; return CURLE_OK; } - /* we've waited long enough, continue anyway */ + /* we have waited long enough, continue anyway */ http_exp100_continue(data, reader); infof(data, "Done waiting for 100-continue"); FALLTHROUGH(); @@ -4460,6 +4494,7 @@ static const struct Curl_crtype cr_exp100 = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, cr_exp100_done, sizeof(struct cr_exp100_ctx) }; diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index b0c4f5fd2..bb5974d94 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -73,7 +73,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, const struct connectdata *conn, const char *thisheader, const size_t thislen); -struct HTTP; /* see below */ CURLcode Curl_add_timecondition(struct Curl_easy *data, #ifndef USE_HYPER @@ -147,7 +146,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); selected to use no auth at all. Ie, we actively select no auth, as opposed to not having one selected. The other CURLAUTH_* defines are present in the public curl/curl.h header. */ -#define CURLAUTH_PICKNONE (1<<30) /* don't use auth */ +#define CURLAUTH_PICKNONE (1<<30) /* do not use auth */ /* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST data get included in the initial data chunk sent to the server. If the @@ -187,10 +186,6 @@ void Curl_http_exp100_got100(struct Curl_easy *data); /**************************************************************************** * HTTP unique setup ***************************************************************************/ -struct HTTP { - /* TODO: no longer used, we should remove it from SingleRequest */ - char unused; -}; CURLcode Curl_http_size(struct Curl_easy *data); @@ -240,7 +235,7 @@ struct httpreq { }; /** - * Create a HTTP request struct. + * Create an HTTP request struct. */ CURLcode Curl_http_req_make(struct httpreq **preq, const char *method, size_t m_len, @@ -290,7 +285,7 @@ struct http_resp { }; /** - * Create a HTTP response struct. + * Create an HTTP response struct. */ CURLcode Curl_http_resp_make(struct http_resp **presp, int status, diff --git a/Utilities/cmcurl/lib/http1.c b/Utilities/cmcurl/lib/http1.c index 182234ca9..d7e21fdce 100644 --- a/Utilities/cmcurl/lib/http1.c +++ b/Utilities/cmcurl/lib/http1.c @@ -217,7 +217,7 @@ static CURLcode start_req(struct h1_req_parser *parser, tmp[target_len] = '\0'; /* See if treating TARGET as an absolute URL makes sense */ if(Curl_is_absolute_url(tmp, NULL, 0, FALSE)) { - int url_options; + unsigned int url_options; url = curl_url(); if(!url) { diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index f0f7b566e..df3e6f0df 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -69,38 +69,44 @@ /* buffer dimensioning: * use 16K as chunk size, as that fits H2 DATA frames well */ #define H2_CHUNK_SIZE (16 * 1024) -/* this is how much we want "in flight" for a stream */ -#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) +/* connection window size */ +#define H2_CONN_WINDOW_SIZE (10 * 1024 * 1024) /* on receiving from TLS, we prep for holding a full stream window */ -#define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) +#define H2_NW_RECV_CHUNKS (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE) /* on send into TLS, we just want to accumulate small frames */ #define H2_NW_SEND_CHUNKS 1 -/* stream recv/send chunks are a result of window / chunk sizes */ -#define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) +/* this is how much we want "in flight" for a stream, unthrottled */ +#define H2_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) +/* this is how much we want "in flight" for a stream, initially, IFF + * nghttp2 allows us to tweak the local window size. */ +#if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE +#define H2_STREAM_WINDOW_SIZE_INITIAL (64 * 1024) +#else +#define H2_STREAM_WINDOW_SIZE_INITIAL H2_STREAM_WINDOW_SIZE_MAX +#endif /* keep smaller stream upload buffer (default h2 window size) to have * our progress bars and "upload done" reporting closer to reality */ #define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE) /* spare chunks we keep for a full window */ -#define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) +#define H2_STREAM_POOL_SPARES (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE) -/* We need to accommodate the max number of streams with their window - * sizes on the overall connection. Streams might become PAUSED which - * will block their received QUOTA in the connection window. And if we - * run out of space, the server is blocked from sending us any data. - * See #10988 for an issue with this. */ -#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE) +/* We need to accommodate the max number of streams with their window sizes on + * the overall connection. Streams might become PAUSED which will block their + * received QUOTA in the connection window. If we run out of space, the server + * is blocked from sending us any data. See #10988 for an issue with this. */ +#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE_MAX) #define H2_SETTINGS_IV_LEN 3 #define H2_BINSETTINGS_LEN 80 -static int populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data) +static size_t populate_settings(nghttp2_settings_entry *iv, + struct Curl_easy *data) { iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = Curl_multi_max_concurrent_streams(data->multi); iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = H2_STREAM_WINDOW_SIZE; + iv[1].value = H2_STREAM_WINDOW_SIZE_INITIAL; iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; iv[2].value = data->multi->push_cb != NULL; @@ -112,7 +118,7 @@ static ssize_t populate_binsettings(uint8_t *binsettings, struct Curl_easy *data) { nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - int ivlen; + size_t ivlen; ivlen = populate_settings(iv, data); /* this returns number of bytes it wrote or a negative number on error. */ @@ -130,13 +136,17 @@ struct cf_h2_ctx { struct bufc_pool stream_bufcp; /* spares for stream buffers */ struct dynbuf scratch; /* scratch buffer for temp use */ - struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */ + struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */ size_t drain_total; /* sum of all stream's UrlState drain */ uint32_t max_concurrent_streams; - int32_t goaway_error; - int32_t last_stream_id; + uint32_t goaway_error; /* goaway error code from server */ + int32_t remote_max_sid; /* max id processed by server */ + int32_t local_max_sid; /* max id processed by us */ + BIT(initialized); + BIT(via_h1_upgrade); BIT(conn_closed); - BIT(goaway); + BIT(rcvd_goaway); + BIT(sent_goaway); BIT(enable_push); BIT(nw_out_blocked); }; @@ -146,28 +156,38 @@ struct cf_h2_ctx { #define CF_CTX_CALL_DATA(cf) \ ((struct cf_h2_ctx *)(cf)->ctx)->call_data -static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) +static void h2_stream_hash_free(void *stream); + +static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) { - struct cf_call_data save = ctx->call_data; + Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); + Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); + Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free); + ctx->remote_max_sid = 2147483647; + ctx->via_h1_upgrade = via_h1_upgrade; + ctx->initialized = TRUE; +} - if(ctx->h2) { - nghttp2_session_del(ctx->h2); +static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_bufq_free(&ctx->inbufq); + Curl_bufq_free(&ctx->outbufq); + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + memset(ctx, 0, sizeof(*ctx)); } - Curl_bufq_free(&ctx->inbufq); - Curl_bufq_free(&ctx->outbufq); - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_dyn_free(&ctx->scratch); - Curl_hash_clean(&ctx->streams); - Curl_hash_destroy(&ctx->streams); - memset(ctx, 0, sizeof(*ctx)); - ctx->call_data = save; + free(ctx); } -static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) { - if(ctx) { - cf_h2_ctx_clear(ctx); - free(ctx); + if(ctx->h2) { + nghttp2_session_del(ctx->h2); } } @@ -183,8 +203,6 @@ struct h2_stream_ctx { struct h1_req_parser h1; /* parsing the request */ struct dynhds resp_trailers; /* response trailer fields */ size_t resp_hds_len; /* amount of response header bytes in recvbuf */ - size_t upload_blocked_len; - curl_off_t upload_left; /* number of request bytes left to upload */ curl_off_t nrcvd_data; /* number of DATA bytes received */ char **push_headers; /* allocated array */ @@ -194,19 +212,19 @@ struct h2_stream_ctx { int status_code; /* HTTP response status code */ uint32_t error; /* stream error code */ CURLcode xfer_result; /* Result of writing out response */ - uint32_t local_window_size; /* the local recv window size */ + int32_t local_window_size; /* the local recv window size */ int32_t id; /* HTTP/2 protocol identifier for stream */ BIT(resp_hds_complete); /* we have a complete, final response */ BIT(closed); /* TRUE on stream close */ BIT(reset); /* TRUE on stream reset */ BIT(close_handled); /* TRUE if stream closure is handled by libcurl */ BIT(bodystarted); - BIT(send_closed); /* transfer is done sending, we might have still - buffered data in stream->sendbuf to upload. */ + BIT(body_eos); /* the complete body has been added to `sendbuf` and + * is being/has been processed from there. */ }; #define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) { @@ -228,8 +246,7 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx) stream->closed = FALSE; stream->close_handled = FALSE; stream->error = NGHTTP2_NO_ERROR; - stream->local_window_size = H2_STREAM_WINDOW_SIZE; - stream->upload_left = 0; + stream->local_window_size = H2_STREAM_WINDOW_SIZE_INITIAL; stream->nrcvd_data = 0; return stream; } @@ -258,6 +275,77 @@ static void h2_stream_hash_free(void *stream) h2_stream_ctx_free((struct h2_stream_ctx *)stream); } +#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE +static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)cf; + if(data->set.max_recv_speed && data->set.max_recv_speed < INT32_MAX) { + /* The transfer should only receive `max_recv_speed` bytes per second. + * We restrict the stream's local window size, so that the server cannot + * send us "too much" at a time. + * This gets less precise the higher the latency. */ + return (int32_t)data->set.max_recv_speed; + } + return H2_STREAM_WINDOW_SIZE_MAX; +} + +static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + bool paused) +{ + struct cf_h2_ctx *ctx = cf->ctx; + int32_t dwsize; + int rv; + + dwsize = paused? 0 : cf_h2_get_desired_local_win(cf, data); + if(dwsize != stream->local_window_size) { + int32_t wsize = nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id); + if(dwsize > wsize) { + rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, dwsize - wsize); + if(rv) { + failf(data, "[%d] nghttp2_submit_window_update() failed: " + "%s(%d)", stream->id, nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + stream->local_window_size = dwsize; + CURL_TRC_CF(data, cf, "[%d] local window update by %d", + stream->id, dwsize - wsize); + } + else { + rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, dwsize); + if(rv) { + failf(data, "[%d] nghttp2_session_set_local_window_size() failed: " + "%s(%d)", stream->id, nghttp2_strerror(rv), rv); + return CURLE_HTTP2; + } + stream->local_window_size = dwsize; + CURL_TRC_CF(data, cf, "[%d] local window size now %d", + stream->id, dwsize); + } + } + return CURLE_OK; +} + +#else /* NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */ + +static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + bool paused) +{ + (void)cf; + (void)data; + (void)stream; + (void)paused; + return CURLE_OK; +} +#endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */ + /* * Mark this transfer to get "drained". */ @@ -269,10 +357,10 @@ static void drain_stream(struct Curl_cfilter *cf, (void)cf; bits = CURL_CSELECT_IN; - if(!stream->send_closed && - (stream->upload_left || stream->upload_blocked_len)) + if(!stream->closed && + (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf))) bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { + if(stream->closed || (data->state.select_bits != bits)) { CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", stream->id, bits); data->state.select_bits = bits; @@ -289,10 +377,6 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, (void)cf; DEBUGASSERT(data); - if(!data->req.p.http) { - failf(data, "initialization failure, transfer not http initialized"); - return CURLE_FAILED_INIT; - } stream = H2_STREAM_CTX(ctx, data); if(stream) { *pstream = stream; @@ -303,7 +387,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, if(!stream) return CURLE_OUT_OF_MEMORY; - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h2_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -318,7 +402,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); DEBUGASSERT(ctx); - if(!stream) + if(!stream || !ctx->initialized) return; if(ctx->h2) { @@ -332,7 +416,6 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) stream->id); stream->closed = TRUE; stream->reset = TRUE; - stream->send_closed = TRUE; nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, stream->id, NGHTTP2_STREAM_CLOSED); flush_egress = TRUE; @@ -342,7 +425,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) nghttp2_session_send(ctx->h2); } - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } static int h2_client_new(struct Curl_cfilter *cf, @@ -385,8 +468,8 @@ static ssize_t nw_out_writer(void *writer_ctx, struct Curl_easy *data = CF_DATA_CURRENT(cf); if(data) { - ssize_t nwritten = Curl_conn_cf_send(cf->next, data, - (const char *)buf, buflen, err); + ssize_t nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, + buflen, FALSE, err); if(nwritten > 0) CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); return nwritten; @@ -418,12 +501,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, static int error_callback(nghttp2_session *session, const char *msg, size_t len, void *userp); -/* - * Initialize the cfilter context - */ -static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool via_h1_upgrade) +static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream; @@ -432,12 +511,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, nghttp2_session_callbacks *cbs = NULL; DEBUGASSERT(!ctx->h2); - Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); - Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); - Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); - Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free); - ctx->last_stream_id = 2147483647; + DEBUGASSERT(ctx->initialized); rc = nghttp2_session_callbacks_new(&cbs); if(rc) { @@ -466,7 +540,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, } ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; - if(via_h1_upgrade) { + if(ctx->via_h1_upgrade) { /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted * in the H1 request and we upgrade from there. This stream * is opened implicitly as #1. */ @@ -486,7 +560,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, DEBUGASSERT(stream); stream->id = 1; /* queue SETTINGS frame (again) */ - rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, + rc = nghttp2_session_upgrade2(ctx->h2, binsettings, (size_t)binlen, data->state.httpreq == HTTPREQ_HEAD, NULL); if(rc) { @@ -507,7 +581,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, } else { nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - int ivlen; + size_t ivlen; ivlen = populate_settings(iv, data); rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, @@ -532,7 +606,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, /* all set, traffic will be send on connect */ result = CURLE_OK; CURL_TRC_CF(data, cf, "[0] created h2 session%s", - via_h1_upgrade? " (via h1 upgrade)" : ""); + ctx->via_h1_upgrade? " (via h1 upgrade)" : ""); out: if(cbs) @@ -612,8 +686,8 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, return FALSE; if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, only "protocol frames" */ CURLcode result; ssize_t nread = -1; @@ -794,18 +868,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, { struct Curl_easy *second = curl_easy_duphandle(data); if(second) { - /* setup the request struct */ - struct HTTP *http = calloc(1, sizeof(struct HTTP)); - if(!http) { - (void)Curl_close(&second); - } - else { - struct h2_stream_ctx *second_stream; - - second->req.p.http = http; - http2_data_setup(cf, second, &second_stream); - second->state.priority.weight = data->state.priority.weight; - } + struct h2_stream_ctx *second_stream; + http2_data_setup(cf, second, &second_stream); + second->state.priority.weight = data->state.priority.weight; } return second; } @@ -867,9 +932,7 @@ fail: static void discard_newhandle(struct Curl_cfilter *cf, struct Curl_easy *newhandle) { - if(newhandle->req.p.http) { - http2_data_done(cf, newhandle); - } + http2_data_done(cf, newhandle); (void)Curl_close(&newhandle); } @@ -967,6 +1030,10 @@ static int push_promise(struct Curl_cfilter *cf, rv = CURL_PUSH_DENY; goto fail; } + + /* success, remember max stream id processed */ + if(newstream->id > ctx->local_max_sid) + ctx->local_max_sid = newstream->id; } else { CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); @@ -985,6 +1052,8 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); + if(!stream->xfer_result && !eos) + stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", stream->id, stream->xfer_result, blen); @@ -1000,6 +1069,8 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, /* If we already encountered an error, skip further writes */ if(!stream->xfer_result) stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); + if(!stream->xfer_result && !eos) + stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE); /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { struct cf_h2_ctx *ctx = cf->ctx; @@ -1007,7 +1078,7 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, "RST-ing stream", stream->id, stream->xfer_result, blen); nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, - NGHTTP2_ERR_CALLBACK_FAILURE); + (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE); } } @@ -1048,7 +1119,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, break; case NGHTTP2_HEADERS: if(stream->bodystarted) { - /* Only valid HEADERS after body started is trailer HEADERS. We + /* Only valid HEADERS after body started is trailer HEADERS. We buffer them in on_header callback. */ break; } @@ -1093,13 +1164,19 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(frame->rst_stream.error_code) { stream->reset = TRUE; } - stream->send_closed = TRUE; drain_stream(cf, data, stream); break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data)) { + if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { + /* need more data, force processing of transfer */ drain_stream(cf, data, stream); } + else if(!Curl_bufq_is_empty(&stream->sendbuf)) { + /* resume the potentially suspended stream */ + rv = nghttp2_session_resume_data(ctx->h2, stream->id); + if(nghttp2_is_fatal(rv)) + return CURLE_SEND_ERROR; + } break; default: break; @@ -1252,12 +1329,12 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, break; } case NGHTTP2_GOAWAY: - ctx->goaway = TRUE; + ctx->rcvd_goaway = TRUE; ctx->goaway_error = frame->goaway.error_code; - ctx->last_stream_id = frame->goaway.last_stream_id; + ctx->remote_max_sid = frame->goaway.last_stream_id; if(data) { - infof(data, "received GOAWAY, error=%d, last_stream=%u", - ctx->goaway_error, ctx->last_stream_id); + infof(data, "received GOAWAY, error=%u, last_stream=%u", + ctx->goaway_error, ctx->remote_max_sid); Curl_multi_connchanged(data->multi); } break; @@ -1310,9 +1387,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, nghttp2_session_consume(ctx->h2, stream_id, len); stream->nrcvd_data += (curl_off_t)len; - - /* if we receive data for another handle, wake that up */ - drain_stream(cf, data_s, stream); return 0; } @@ -1327,8 +1401,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, (void)session; DEBUGASSERT(call_data); - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ + /* stream id 0 is the connection, do not look there for streams. */ data_s = stream_id? nghttp2_session_get_stream_user_data(session, stream_id) : NULL; if(!data_s) { @@ -1356,7 +1429,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->error = error_code; if(stream->error) { stream->reset = TRUE; - stream->send_closed = TRUE; } if(stream->error) @@ -1582,22 +1654,21 @@ static ssize_t req_body_read_callback(nghttp2_session *session, (void)source; (void)cf; - if(stream_id) { - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) - /* Receiving a Stream ID not in the hash should not happen, this is an - internal error more than anything else! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - - stream = H2_STREAM_CTX(ctx, data_s); - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - else + if(!stream_id) return NGHTTP2_ERR_INVALID_ARGUMENT; + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = nghttp2_session_get_stream_user_data(session, stream_id); + if(!data_s) + /* Receiving a Stream ID not in the hash should not happen, this is an + internal error more than anything else! */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + + stream = H2_STREAM_CTX(ctx, data_s); + if(!stream) + return NGHTTP2_ERR_CALLBACK_FAILURE; + nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result); if(nread < 0) { if(result != CURLE_AGAIN) @@ -1605,19 +1676,14 @@ static ssize_t req_body_read_callback(nghttp2_session *session, nread = 0; } - if(nread > 0 && stream->upload_left != -1) - stream->upload_left -= nread; + CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d", + stream_id, length, stream->body_eos, nread, result); - CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%" - CURL_FORMAT_CURL_OFF_T " -> %zd, %d", - stream_id, length, stream->upload_left, nread, result); - - if(stream->upload_left == 0) + if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) { *data_flags = NGHTTP2_DATA_FLAG_EOF; - else if(nread == 0) - return NGHTTP2_ERR_DEFERRED; - - return nread; + return nread; + } + return (nread == 0)? NGHTTP2_ERR_DEFERRED : nread; } #if !defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -1654,7 +1720,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, return CURLE_FAILED_INIT; } - result = Curl_base64url_encode((const char *)binsettings, binlen, + result = Curl_base64url_encode((const char *)binsettings, (size_t)binlen, &base64, &blen); if(result) { Curl_dyn_free(req); @@ -1669,37 +1735,11 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, free(base64); k->upgr101 = UPGR101_H2; + data->conn->bits.asks_multiplex = TRUE; return result; } -static CURLcode http2_data_done_send(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); - - if(!ctx || !ctx->h2 || !stream) - goto out; - - CURL_TRC_CF(data, cf, "[%d] data done send", stream->id); - if(!stream->send_closed) { - stream->send_closed = TRUE; - if(stream->upload_left) { - /* we now know that everything that is buffered is all there is. */ - stream->upload_left = Curl_bufq_len(&stream->sendbuf); - /* resume sending here to trigger the callback to get called again so - that it can signal EOF to nghttp2 */ - (void)nghttp2_session_resume_data(ctx->h2, stream->id); - drain_stream(cf, data, stream); - } - } - -out: - return result; -} - static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, struct Curl_easy *data, struct h2_stream_ctx *stream, @@ -1710,7 +1750,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, if(stream->error == NGHTTP2_REFUSED_STREAM) { CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " "connection", stream->id); - connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ + connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ data->state.refused_stream = TRUE; *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; @@ -1817,7 +1857,7 @@ static void h2_pri_spec(struct cf_h2_ctx *ctx, } /* - * Check if there's been an update in the priority / + * Check if there is been an update in the priority / * dependency settings and if so it submits a PRIORITY frame with the updated * info. * Flush any out data pending in the network buffer. @@ -1878,7 +1918,7 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(stream->reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || - (ctx->goaway && ctx->last_stream_id < stream->id)) { + (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) { CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2; nread = -1; @@ -1944,12 +1984,15 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, if(h2_process_pending_input(cf, data, &result)) return result; + CURL_TRC_CF(data, cf, "[0] progress ingress: inbufg=%zu", + Curl_bufq_len(&ctx->inbufq)); } if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { connclose(cf->conn, "GOAWAY received"); } + CURL_TRC_CF(data, cf, "[0] progress ingress: done"); return CURLE_OK; } @@ -1967,9 +2010,8 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, * (unlikely) or the transfer has been done, cleaned up its resources, but * a read() is called anyway. It is not clear what the calling sequence * is for such a case. */ - failf(data, "[%zd-%zd], http/2 recv on a transfer never opened " - "or already cleared", (ssize_t)data->id, - (ssize_t)cf->conn->connection_id); + failf(data, "http/2 recv on a transfer never opened " + "or already cleared, mid=%" FMT_OFF_T, data->mid); *err = CURLE_HTTP2; return -1; } @@ -2015,11 +2057,11 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = h2_progress_egress(cf, data); if(result == CURLE_AGAIN) { - /* pending data to send, need to be called again. Ideally, we'd - * monitor the socket for POLLOUT, but we might not be in SENDING - * transfer state any longer and are unable to make this happen. - */ - drain_stream(cf, data, stream); + /* pending data to send, need to be called again. Ideally, we + * monitor the socket for POLLOUT, but when not SENDING + * any more, we force processing of the transfer. */ + if(!CURL_WANT_SEND(data)) + drain_stream(cf, data, stream); } else if(result) { *err = result; @@ -2039,10 +2081,57 @@ out: return nread; } +static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + const void *buf, size_t blen, bool eos, + CURLcode *err) +{ + struct cf_h2_ctx *ctx = cf->ctx; + ssize_t nwritten; + + if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%d] discarding data" + "on closed stream with response", stream->id); + if(eos) + stream->body_eos = TRUE; + *err = CURLE_OK; + return (ssize_t)blen; + } + /* Server closed before we got a response, this is an error */ + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + return -1; + } + + nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err); + if(nwritten < 0) + return -1; + + if(eos && (blen == (size_t)nwritten)) + stream->body_eos = TRUE; + + if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) { + /* resume the potentially suspended stream */ + int rv = nghttp2_session_resume_data(ctx->h2, stream->id); + if(nghttp2_is_fatal(rv)) { + *err = CURLE_SEND_ERROR; + return -1; + } + } + return nwritten; +} + static ssize_t h2_submit(struct h2_stream_ctx **pstream, struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, - size_t *phdslen, CURLcode *err) + bool eos, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = NULL; @@ -2055,7 +2144,6 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, nghttp2_priority_spec pri_spec; ssize_t nwritten; - *phdslen = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = http2_data_setup(cf, data, &stream); @@ -2067,7 +2155,6 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - *phdslen = (size_t)nwritten; if(!stream->h1.done) { /* need more data */ goto out; @@ -2098,19 +2185,12 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, case HTTPREQ_POST_FORM: case HTTPREQ_POST_MIME: case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - data_prd.read_callback = req_body_read_callback; data_prd.source.ptr = NULL; stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, &data_prd, data); break; default: - stream->upload_left = 0; /* no request body */ stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, NULL, data); } @@ -2145,32 +2225,21 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, } stream->id = stream_id; - stream->local_window_size = H2_STREAM_WINDOW_SIZE; - if(data->set.max_recv_speed) { - /* We are asked to only receive `max_recv_speed` bytes per second. - * Let's limit our stream window size around that, otherwise the server - * will send in large bursts only. We make the window 50% larger to - * allow for data in flight and avoid stalling. */ - curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1); - n += CURLMAX((n/2), 1); - if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) && - n < (UINT_MAX / H2_CHUNK_SIZE)) { - stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE; - } - } body = (const char *)buf + nwritten; bodylen = len - nwritten; - if(bodylen) { - /* We have request body to send in DATA frame */ - ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err); - if(n < 0) { + if(bodylen || eos) { + ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err); + if(n >= 0) + nwritten += n; + else if(*err == CURLE_AGAIN) + *err = CURLE_OK; + else if(*err != CURLE_AGAIN) { *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } - nwritten += n; } out: @@ -2183,139 +2252,63 @@ out: } static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct cf_call_data save; - int rv; ssize_t nwritten; - size_t hdslen = 0; CURLcode result; - int blocked = 0, was_blocked = 0; CF_DATA_SAVE(save, cf, data); - if(stream && stream->id != -1) { - if(stream->upload_blocked_len) { - /* the data in `buf` has already been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - /* TODO: this assertion triggers in OSSFuzz runs and it is not - * clear why. Disable for now to let OSSFuzz continue its tests. */ - DEBUGASSERT(len >= stream->upload_blocked_len); - if(len < stream->upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happen. We are not prepared to handle that. */ - failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)", - len, stream->upload_blocked_len); - *err = CURLE_HTTP2; - nwritten = -1; - goto out; - } - nwritten = (ssize_t)stream->upload_blocked_len; - stream->upload_blocked_len = 0; - was_blocked = 1; - } - else if(stream->closed) { - if(stream->resp_hds_complete) { - /* Server decided to close the stream after having sent us a findl - * response. This is valid if it is not interested in the request - * body. This happens on 30x or 40x responses. - * We silently discard the data sent, since this is not a transport - * error situation. */ - CURL_TRC_CF(data, cf, "[%d] discarding data" - "on closed stream with response", stream->id); - *err = CURLE_OK; - nwritten = (ssize_t)len; - goto out; - } - infof(data, "stream %u closed", stream->id); - *err = CURLE_SEND_ERROR; - nwritten = -1; + if(!stream || stream->id == -1) { + nwritten = h2_submit(&stream, cf, data, buf, len, eos, err); + if(nwritten < 0) { goto out; } - else { - /* If stream_id != -1, we have dispatched request HEADERS and - * optionally request body, and now are going to send or sending - * more request body in DATA frame */ - nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - if(nwritten < 0 && *err != CURLE_AGAIN) - goto out; - } - - if(!Curl_bufq_is_empty(&stream->sendbuf)) { - /* req body data is buffered, resume the potentially suspended stream */ - rv = nghttp2_session_resume_data(ctx->h2, stream->id); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - } + DEBUGASSERT(stream); } - else { - nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err); + else if(stream->body_eos) { + /* We already wrote this, but CURLE_AGAINed the call due to not + * being able to flush stream->sendbuf. Make a 0-length write + * to trigger flushing again. + * If this works, we report to have written `len` bytes. */ + DEBUGASSERT(eos); + nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err); + CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d", + stream->id, nwritten, *err, eos); if(nwritten < 0) { goto out; } - DEBUGASSERT(stream); - DEBUGASSERT(hdslen <= (size_t)nwritten); + nwritten = len; + } + else { + nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err); + CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d", + stream->id, len, nwritten, *err, eos); } /* Call the nghttp2 send loop and flush to write ALL buffered data, * headers and/or request body completely out to the network */ result = h2_progress_egress(cf, data); + /* if the stream has been closed in egress handling (nghttp2 does that * when it does not like the headers, for example */ - if(stream && stream->closed && !was_blocked) { + if(stream && stream->closed) { infof(data, "stream %u closed", stream->id); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } - else if(result == CURLE_AGAIN) { - blocked = 1; - } - else if(result) { + else if(result && (result != CURLE_AGAIN)) { *err = result; nwritten = -1; goto out; } - else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { - /* although we wrote everything that nghttp2 wants to send now, - * there is data left in our stream send buffer unwritten. This may - * be due to the stream's HTTP/2 flow window being exhausted. */ - blocked = 1; - } - - if(stream && blocked && nwritten > 0) { - /* Unable to send all data, due to connection blocked or H2 window - * exhaustion. Data is left in our stream buffer, or nghttp2's internal - * frame buffer or our network out buffer. */ - size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, - stream->id); - /* At the start of a stream, we are called with request headers - * and, possibly, parts of the body. Later, only body data. - * If we cannot send pure body data, we EAGAIN. If there had been - * header, we return that *they* have been written and remember the - * block on the data length only. */ - stream->upload_blocked_len = ((size_t)nwritten) - hdslen; - CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "hds_len=%zu blocked_len=%zu", - stream->id, len, - nghttp2_session_get_remote_window_size(ctx->h2), rwin, - hdslen, stream->upload_blocked_len); - if(hdslen) { - *err = CURLE_OK; - nwritten = hdslen; - } - else { - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - } - else if(should_close_session(ctx)) { + + if(should_close_session(ctx)) { /* nghttp2 thinks this session is done. If the stream has not been * closed, this is an error state for out transfer */ if(stream->closed) { @@ -2331,11 +2324,10 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: if(stream) { CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " - "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " - "h2 windows %d-%d (stream-conn), " + "eos=%d, h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", stream->id, len, nwritten, *err, - stream->upload_left, + stream->body_eos, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -2353,11 +2345,54 @@ out: return nwritten; } +static CURLcode cf_h2_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + struct cf_call_data save; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { + /* resume the potentially suspended stream */ + int rv = nghttp2_session_resume_data(ctx->h2, stream->id); + if(nghttp2_is_fatal(rv)) { + result = CURLE_SEND_ERROR; + goto out; + } + } + + result = h2_progress_egress(cf, data); + +out: + if(stream) { + CURL_TRC_CF(data, cf, "[%d] flush -> %d, " + "h2 windows %d-%d (stream-conn), " + "buffers %zu-%zu (stream-conn)", + stream->id, result, + nghttp2_session_get_stream_remote_window_size( + ctx->h2, stream->id), + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&stream->sendbuf), + Curl_bufq_len(&ctx->outbufq)); + } + else { + CURL_TRC_CF(data, cf, "flush -> %d, " + "connection-window=%d, nw_send_buffer(%zu)", + result, nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->outbufq)); + } + CF_DATA_RESTORE(cf, save); + return result; +} + static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; curl_socket_t sock; bool want_recv, want_send; @@ -2368,7 +2403,6 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, Curl_pollset_check(data, ps, sock, &want_recv, &want_send); if(want_recv || want_send) { struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); - struct cf_call_data save; bool c_exhaust, s_exhaust; CF_DATA_SAVE(save, cf, data); @@ -2378,11 +2412,21 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, stream->id); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || - (!c_exhaust && nghttp2_session_want_write(ctx->h2)); + (!c_exhaust && nghttp2_session_want_write(ctx->h2)) || + !Curl_bufq_is_empty(&ctx->outbufq); Curl_pollset_set(data, ps, sock, want_recv, want_send); CF_DATA_RESTORE(cf, save); } + else if(ctx->sent_goaway && !cf->shutdown) { + /* shutdown in progress */ + CF_DATA_SAVE(save, cf, data); + want_send = nghttp2_session_want_write(ctx->h2) || + !Curl_bufq_is_empty(&ctx->outbufq); + want_recv = nghttp2_session_want_read(ctx->h2); + Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } } static CURLcode cf_h2_connect(struct Curl_cfilter *cf, @@ -2408,8 +2452,9 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, *done = FALSE; CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(ctx->initialized); if(!ctx->h2) { - result = cf_h2_ctx_init(cf, data, FALSE); + result = cf_h2_ctx_open(cf, data); if(result) goto out; } @@ -2444,8 +2489,9 @@ static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - cf_h2_ctx_clear(ctx); + cf_h2_ctx_close(ctx); CF_DATA_RESTORE(cf, save); + cf->connected = FALSE; } if(cf->next) cf->next->cft->do_close(cf->next, data); @@ -2462,30 +2508,68 @@ static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) } } +static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h2_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result; + int rv; + + if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + + if(!ctx->sent_goaway) { + rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE, + ctx->local_max_sid, 0, + (const uint8_t *)"shutdown", + sizeof("shutdown")); + if(rv) { + failf(data, "nghttp2_submit_goaway() failed: %s(%d)", + nghttp2_strerror(rv), rv); + result = CURLE_SEND_ERROR; + goto out; + } + ctx->sent_goaway = TRUE; + } + /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */ + result = CURLE_OK; + if(nghttp2_session_want_write(ctx->h2) || + !Curl_bufq_is_empty(&ctx->outbufq)) + result = h2_progress_egress(cf, data); + if(!result && nghttp2_session_want_read(ctx->h2)) + result = h2_progress_ingress(cf, data, 0); + + if(result == CURLE_AGAIN) + result = CURLE_OK; + + *done = (ctx->conn_closed || + (!result && !nghttp2_session_want_write(ctx->h2) && + !nghttp2_session_want_read(ctx->h2) && + Curl_bufq_is_empty(&ctx->outbufq))); + +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + static CURLcode http2_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause) { -#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); DEBUGASSERT(data); if(ctx && ctx->h2 && stream) { - uint32_t window = pause? 0 : stream->local_window_size; - - int rv = nghttp2_session_set_local_window_size(ctx->h2, - NGHTTP2_FLAG_NONE, - stream->id, - window); - if(rv) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } - - if(!pause) - drain_stream(cf, data, stream); + CURLcode result = cf_h2_update_local_win(cf, data, stream, pause); + if(result) + return result; /* attempt to send the window update */ (void)h2_progress_egress(cf, data); @@ -2499,21 +2583,9 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, drain_stream(cf, data, stream); Curl_expire(data, 0, EXPIRE_RUN_NOW); } - DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", - window, stream->id)); - -#ifdef DEBUGBUILD - { - /* read out the stream local window again */ - uint32_t window2 = - nghttp2_session_get_stream_local_window_size(ctx->h2, - stream->id); - DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", - window2, stream->id)); - } -#endif + CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id, + pause? "" : "un"); } -#endif return CURLE_OK; } @@ -2533,8 +2605,8 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_PAUSE: result = http2_data_pause(cf, data, (arg1 != 0)); break; - case CF_CTRL_DATA_DONE_SEND: - result = http2_data_done_send(cf, data); + case CF_CTRL_FLUSH: + result = cf_h2_flush(cf, data); break; case CF_CTRL_DATA_DETACH: http2_data_done(cf, data); @@ -2617,6 +2689,15 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, *pres1 = stream? (int)stream->error : 0; return CURLE_OK; } + case CF_QUERY_NEED_FLUSH: { + struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + if(!Curl_bufq_is_empty(&ctx->outbufq) || + (stream && !Curl_bufq_is_empty(&stream->sendbuf))) { + *pres1 = TRUE; + return CURLE_OK; + } + break; + } default: break; } @@ -2632,6 +2713,7 @@ struct Curl_cftype Curl_cft_nghttp2 = { cf_h2_destroy, cf_h2_connect, cf_h2_close, + cf_h2_shutdown, Curl_cf_def_get_host, cf_h2_adjust_pollset, cf_h2_data_pending, @@ -2657,6 +2739,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; + cf_h2_ctx_init(ctx, via_h1_upgrade); result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); if(result) @@ -2664,7 +2747,6 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, ctx = NULL; Curl_conn_cf_add(data, conn, sockindex, cf); - result = cf_h2_ctx_init(cf, data, via_h1_upgrade); out: if(result) @@ -2685,6 +2767,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, ctx = calloc(1, sizeof(*ctx)); if(!ctx) goto out; + cf_h2_ctx_init(ctx, via_h1_upgrade); result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); if(result) @@ -2692,7 +2775,6 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, ctx = NULL; Curl_conn_cf_insert_after(cf, cf_h2); - result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade); out: if(result) @@ -2729,7 +2811,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data, data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { #ifndef CURL_DISABLE_PROXY if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* We don't support HTTP/2 proxies yet. Also it's debatable + /* We do not support HTTP/2 proxies yet. Also it is debatable whether or not this setting should apply to HTTP/2 proxies. */ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); return FALSE; @@ -2747,15 +2829,14 @@ CURLcode Curl_http2_switch(struct Curl_easy *data, CURLcode result; DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, "switching to HTTP/2")); result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE); if(result) return result; + CURL_TRC_CF(data, cf, "switching connection to HTTP/2"); - conn->httpversion = 20; /* we know we're on HTTP/2 now */ + conn->httpversion = 20; /* we know we are on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; Curl_multi_connchanged(data->multi); if(cf->next) { @@ -2777,9 +2858,8 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) return result; cf_h2 = cf->next; - cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ + cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; Curl_multi_connchanged(data->multi); if(cf_h2->next) { @@ -2798,12 +2878,12 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, CURLcode result; DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); - DEBUGF(infof(data, "upgrading to HTTP/2")); DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); if(result) return result; + CURL_TRC_CF(data, cf, "upgrading connection to HTTP/2"); DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); ctx = cf->ctx; @@ -2830,9 +2910,8 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, " after upgrade: len=%zu", nread); } - conn->httpversion = 20; /* we know we're on HTTP/2 now */ + conn->httpversion = 20; /* we know we are on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; Curl_multi_connchanged(data->multi); if(cf->next) { diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 98cc033a0..3874993e9 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -60,11 +60,11 @@ #define TIMESTAMP_SIZE 17 /* hex-encoded with trailing null */ -#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1) +#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1) static void sha256_to_hex(char *dst, unsigned char *sha) { - Curl_hexencode(sha, SHA256_DIGEST_LENGTH, + Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH, (unsigned char *)dst, SHA256_HEX_LENGTH); } @@ -129,6 +129,37 @@ static void trim_headers(struct curl_slist *head) /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ #define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1) +/* alphabetically compare two headers by their name, expecting + headers to use ':' at this point */ +static int compare_header_names(const char *a, const char *b) +{ + const char *colon_a; + const char *colon_b; + size_t len_a; + size_t len_b; + size_t min_len; + int cmp; + + colon_a = strchr(a, ':'); + colon_b = strchr(b, ':'); + + DEBUGASSERT(colon_a); + DEBUGASSERT(colon_b); + + len_a = colon_a ? (size_t)(colon_a - a) : strlen(a); + len_b = colon_b ? (size_t)(colon_b - b) : strlen(b); + + min_len = (len_a < len_b) ? len_a : len_b; + + cmp = strncmp(a, b, min_len); + + /* return the shorter of the two if one is shorter */ + if(!cmp) + return (int)(len_a - len_b); + + return cmp; +} + /* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */ static CURLcode make_headers(struct Curl_easy *data, const char *hostname, @@ -240,7 +271,7 @@ static CURLcode make_headers(struct Curl_easy *data, if(!tmp_head) goto fail; head = tmp_head; - *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); + *date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp); } else { char *value; @@ -267,13 +298,13 @@ static CURLcode make_headers(struct Curl_easy *data, *date_header = NULL; } - /* alpha-sort in a case sensitive manner */ + /* alpha-sort by header name 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) { + if(next && compare_header_names(l->data, next->data) > 0) { char *tmp = l->data; l->data = next->data; @@ -423,6 +454,76 @@ static int compare_func(const void *a, const void *b) #define MAX_QUERYPAIRS 64 +/** + * found_equals have a double meaning, + * detect if an equal have been found when called from canon_query, + * and mark that this function is called to compute the path, + * if found_equals is NULL. + */ +static CURLcode canon_string(const char *q, size_t len, + struct dynbuf *dq, bool *found_equals) +{ + CURLcode result = CURLE_OK; + + for(; len && !result; q++, len--) { + if(ISALNUM(*q)) + result = Curl_dyn_addn(dq, q, 1); + else { + switch(*q) { + case '-': + case '.': + case '_': + case '~': + /* allowed as-is */ + result = Curl_dyn_addn(dq, q, 1); + break; + case '%': + /* uppercase the following if hexadecimal */ + if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { + char tmp[3]="%"; + tmp[1] = Curl_raw_toupper(q[1]); + tmp[2] = Curl_raw_toupper(q[2]); + result = Curl_dyn_addn(dq, tmp, 3); + q += 2; + len -= 2; + } + else + /* '%' without a following two-digit hex, encode it */ + result = Curl_dyn_addn(dq, "%25", 3); + break; + default: { + const char hex[] = "0123456789ABCDEF"; + char out[3]={'%'}; + + if(!found_equals) { + /* if found_equals is NULL assuming, been in path */ + if(*q == '/') { + /* allowed as if */ + result = Curl_dyn_addn(dq, q, 1); + break; + } + } + else { + /* allowed as-is */ + if(*q == '=') { + result = Curl_dyn_addn(dq, q, 1); + *found_equals = true; + break; + } + } + /* URL encode */ + out[1] = hex[((unsigned char)*q)>>4]; + out[2] = hex[*q & 0xf]; + result = Curl_dyn_addn(dq, out, 3); + break; + } + } + } + } + return result; +} + + static CURLcode canon_query(struct Curl_easy *data, const char *query, struct dynbuf *dq) { @@ -460,54 +561,11 @@ static CURLcode canon_query(struct Curl_easy *data, ap = &array[0]; for(i = 0; !result && (i < entry); i++, ap++) { - size_t len; const char *q = ap->p; bool found_equals = false; if(!ap->len) continue; - for(len = ap->len; len && !result; q++, len--) { - if(ISALNUM(*q)) - result = Curl_dyn_addn(dq, q, 1); - else { - switch(*q) { - case '-': - case '.': - case '_': - case '~': - /* allowed as-is */ - result = Curl_dyn_addn(dq, q, 1); - break; - case '=': - /* allowed as-is */ - result = Curl_dyn_addn(dq, q, 1); - found_equals = true; - break; - case '%': - /* uppercase the following if hexadecimal */ - if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { - char tmp[3]="%"; - tmp[1] = Curl_raw_toupper(q[1]); - tmp[2] = Curl_raw_toupper(q[2]); - result = Curl_dyn_addn(dq, tmp, 3); - q += 2; - len -= 2; - } - else - /* '%' without a following two-digit hex, encode it */ - result = Curl_dyn_addn(dq, "%25", 3); - break; - default: { - /* URL encode */ - const char hex[] = "0123456789ABCDEF"; - char out[3]={'%'}; - out[1] = hex[((unsigned char)*q)>>4]; - out[2] = hex[*q & 0xf]; - result = Curl_dyn_addn(dq, out, 3); - break; - } - } - } - } + result = canon_string(q, ap->len, dq, &found_equals); if(!result && !found_equals) { /* queries without value still need an equals */ result = Curl_dyn_addn(dq, "=", 1); @@ -540,12 +598,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) struct dynbuf canonical_headers; struct dynbuf signed_headers; struct dynbuf canonical_query; + struct dynbuf canonical_path; char *date_header = NULL; Curl_HttpReq httpreq; const char *method = NULL; char *payload_hash = NULL; size_t payload_hash_len = 0; - unsigned char sha_hash[SHA256_DIGEST_LENGTH]; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; char sha_hex[SHA256_HEX_LENGTH]; char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ char *canonical_request = NULL; @@ -554,8 +613,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char *str_to_sign = NULL; const char *user = data->state.aptr.user ? data->state.aptr.user : ""; char *secret = NULL; - unsigned char sign0[SHA256_DIGEST_LENGTH] = {0}; - unsigned char sign1[SHA256_DIGEST_LENGTH] = {0}; + unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = {0}; + unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0}; char *auth_headers = NULL; DEBUGASSERT(!proxy); @@ -570,6 +629,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); + Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); /* * Parameters parsing @@ -591,7 +651,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) ":%" MAX_SIGV4_LEN_TXT "s", provider0, provider1, region, service); if(!provider0[0]) { - failf(data, "first aws-sigv4 provider can't be empty"); + failf(data, "first aws-sigv4 provider cannot be empty"); result = CURLE_BAD_FUNCTION_ARGUMENT; goto fail; } @@ -665,10 +725,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(force_timestamp) clock = 0; else - time(&clock); + clock = time(NULL); } #else - time(&clock); + clock = time(NULL); #endif result = Curl_gmtime(clock, &tm); if(result) { @@ -698,22 +758,27 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) result = canon_query(data, data->state.up.query, &canonical_query); if(result) goto fail; + + result = canon_string(data->state.up.path, strlen(data->state.up.path), + &canonical_path, NULL); + if(result) + goto fail; result = CURLE_OUT_OF_MEMORY; 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, - Curl_dyn_ptr(&canonical_query) ? - Curl_dyn_ptr(&canonical_query) : "", - Curl_dyn_ptr(&canonical_headers), - Curl_dyn_ptr(&signed_headers), - (int)payload_hash_len, payload_hash); + aprintf("%s\n" /* HTTPRequestMethod */ + "%s\n" /* CanonicalURI */ + "%s\n" /* CanonicalQueryString */ + "%s\n" /* CanonicalHeaders */ + "%s\n" /* SignedHeaders */ + "%.*s", /* HashedRequestPayload in hex */ + method, + Curl_dyn_ptr(&canonical_path), + Curl_dyn_ptr(&canonical_query) ? + Curl_dyn_ptr(&canonical_query) : "", + Curl_dyn_ptr(&canonical_headers), + Curl_dyn_ptr(&signed_headers), + (int)payload_hash_len, payload_hash); if(!canonical_request) goto fail; @@ -721,12 +786,12 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* provider 0 lowercase */ Curl_strntolower(provider0, provider0, strlen(provider0)); - request_type = curl_maprintf("%s4_request", provider0); + request_type = aprintf("%s4_request", provider0); if(!request_type) goto fail; - credential_scope = curl_maprintf("%s/%s/%s/%s", - date, region, service, request_type); + credential_scope = aprintf("%s/%s/%s/%s", + date, region, service, request_type); if(!credential_scope) goto fail; @@ -743,22 +808,22 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) * Google allows using RSA key instead of HMAC, so this code might change * in the future. For now we only support HMAC. */ - str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ - "%s\n" /* RequestDateTime */ - "%s\n" /* CredentialScope */ - "%s", /* HashedCanonicalRequest in hex */ - provider0, - timestamp, - credential_scope, - sha_hex); + str_to_sign = aprintf("%s4-HMAC-SHA256\n" /* Algorithm */ + "%s\n" /* RequestDateTime */ + "%s\n" /* CredentialScope */ + "%s", /* HashedCanonicalRequest in hex */ + provider0, + timestamp, + credential_scope, + sha_hex); if(!str_to_sign) { goto fail; } /* provider 0 uppercase */ - secret = curl_maprintf("%s4%s", provider0, - data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + secret = aprintf("%s4%s", provider0, + data->state.aptr.passwd ? + data->state.aptr.passwd : ""); if(!secret) goto fail; @@ -771,24 +836,24 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) sha256_to_hex(sha_hex, sign0); /* provider 0 uppercase */ - auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 " - "Credential=%s/%s, " - "SignedHeaders=%s, " - "Signature=%s\r\n" - /* - * date_header is added here, only if it wasn't - * user-specified (using CURLOPT_HTTPHEADER). - * date_header includes \r\n - */ - "%s" - "%s", /* optional sha256 header includes \r\n */ - provider0, - user, - credential_scope, - Curl_dyn_ptr(&signed_headers), - sha_hex, - date_header ? date_header : "", - content_sha256_hdr); + auth_headers = aprintf("Authorization: %s4-HMAC-SHA256 " + "Credential=%s/%s, " + "SignedHeaders=%s, " + "Signature=%s\r\n" + /* + * date_header is added here, only if it was not + * user-specified (using CURLOPT_HTTPHEADER). + * date_header includes \r\n + */ + "%s" + "%s", /* optional sha256 header includes \r\n */ + provider0, + user, + credential_scope, + Curl_dyn_ptr(&signed_headers), + sha_hex, + date_header ? date_header : "", + content_sha256_hdr); if(!auth_headers) { goto fail; } @@ -800,6 +865,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) fail: Curl_dyn_free(&canonical_query); + Curl_dyn_free(&canonical_path); Curl_dyn_free(&canonical_headers); Curl_dyn_free(&signed_headers); free(canonical_request); diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index 48e7e462b..c228eb4f9 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c @@ -182,14 +182,14 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, case CHUNK_LF: /* waiting for the LF after a chunk size */ if(*buf == 0x0a) { - /* we're now expecting data to come, unless size was zero! */ + /* we are now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { ch->state = CHUNK_TRAILER; /* now check for trailers */ } else { ch->state = CHUNK_DATA; CURL_TRC_WRITE(data, "http_chunked, chunk start of %" - CURL_FORMAT_CURL_OFF_T " bytes", ch->datasize); + FMT_OFF_T " bytes", ch->datasize); } } @@ -226,7 +226,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, buf += piece; /* move read pointer forward */ blen -= piece; /* decrease space left in this round */ CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %" - CURL_FORMAT_CURL_OFF_T " bytes in chunk remain", + FMT_OFF_T " bytes in chunk remain", piece, ch->datasize); if(0 == ch->datasize) @@ -289,9 +289,9 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, break; } else { - /* no trailer, we're on the final CRLF pair */ + /* no trailer, we are on the final CRLF pair */ ch->state = CHUNK_TRAILER_POSTCR; - break; /* don't advance the pointer */ + break; /* do not advance the pointer */ } } else { @@ -344,7 +344,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, blen--; (*pconsumed)++; /* Record the length of any data left in the end of the buffer - even if there's no more chunks to read */ + even if there is no more chunks to read */ ch->datasize = blen; ch->state = CHUNK_DONE; CURL_TRC_WRITE(data, "http_chunk, response complete"); @@ -470,7 +470,7 @@ const struct Curl_cwtype Curl_httpchunk_unencoder = { sizeof(struct chunked_writer) }; -/* max length of a HTTP chunk that we want to generate */ +/* max length of an HTTP chunk that we want to generate */ #define CURL_CHUNKED_MINLEN (1024) #define CURL_CHUNKED_MAXLEN (64 * 1024) @@ -659,6 +659,7 @@ const struct Curl_crtype Curl_httpchunk_encoder = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct chunked_reader) }; diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h index d3ecc36c7..34951ea0f 100644 --- a/Utilities/cmcurl/lib/http_chunks.h +++ b/Utilities/cmcurl/lib/http_chunks.h @@ -33,12 +33,12 @@ struct connectdata; /* * The longest possible hexadecimal number we support in a chunked transfer. * Neither RFC2616 nor the later HTTP specs define a maximum chunk size. - * For 64 bit curl_off_t we support 16 digits. For 32 bit, 8 digits. + * For 64-bit curl_off_t we support 16 digits. For 32-bit, 8 digits. */ #define CHUNK_MAXNUM_LEN (SIZEOF_CURL_OFF_T * 2) typedef enum { - /* await and buffer all hexadecimal digits until we get one that isn't a + /* await and buffer all hexadecimal digits until we get one that is not a hexadecimal digit. When done, we go CHUNK_LF */ CHUNK_HEX, @@ -54,9 +54,9 @@ typedef enum { big deal. */ CHUNK_POSTLF, - /* Used to mark that we're out of the game. NOTE: that there's a 'datasize' - field in the struct that will tell how many bytes that were not passed to - the client in the end of the last buffer! */ + /* Used to mark that we are out of the game. NOTE: that there is a + 'datasize' field in the struct that will tell how many bytes that were + not passed to the client in the end of the last buffer! */ CHUNK_STOP, /* At this point optional trailer headers can be found, unless the next line diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index 4cbe2df42..26e475c27 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c @@ -30,6 +30,7 @@ #include "sendf.h" #include "http_negotiate.h" #include "vauth/vauth.h" +#include "vtls/vtls.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -95,7 +96,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, Curl_http_auth_cleanup_negotiate(conn); } else if(state != GSS_AUTHNONE) { - /* The server rejected our authentication and hasn't supplied any more + /* The server rejected our authentication and has not supplied any more negotiation mechanisms */ Curl_http_auth_cleanup_negotiate(conn); return CURLE_LOGIN_DENIED; @@ -106,11 +107,27 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) neg_ctx->sslContext = conn->sslContext; #endif + /* Check if the connection is using SSL and get the channel binding data */ +#ifdef HAVE_GSSAPI + if(conn->handler->flags & PROTOPT_SSL) { + Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE); + result = Curl_ssl_get_channel_binding( + data, FIRSTSOCKET, &neg_ctx->channel_binding_data); + if(result) { + Curl_http_auth_cleanup_negotiate(conn); + return result; + } + } +#endif /* Initialize the security context and decode our challenge */ result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, host, header, neg_ctx); +#ifdef HAVE_GSSAPI + Curl_dyn_free(&neg_ctx->channel_binding_data); +#endif + if(result) Curl_http_auth_cleanup_negotiate(conn); @@ -218,7 +235,7 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { /* connection is already authenticated, - * don't send a header in future requests */ + * do not send a header in future requests */ authp->done = TRUE; } diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index 3dccb5cb7..49230bc1b 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c @@ -123,7 +123,7 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, } /* - * This is for creating ntlm header output + * This is for creating NTLM header output */ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) { @@ -187,10 +187,10 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) passwdp = ""; #ifdef USE_WINDOWS_SSPI - if(!s_hSecDll) { + if(!Curl_hSecDll) { /* not thread safe and leaks - use curl_global_init() to avoid */ CURLcode err = Curl_sspi_global_init(); - if(!s_hSecDll) + if(!Curl_hSecDll) return err; } #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS @@ -200,7 +200,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) Curl_bufref_init(&ntlmmsg); - /* connection is already authenticated, don't send a header in future + /* connection is already authenticated, do not send a header in future * requests so go directly to NTLMSTATE_LAST */ if(*state == NTLMSTATE_TYPE3) *state = NTLMSTATE_LAST; diff --git a/Utilities/cmcurl/lib/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h index f37572bae..c1cf05701 100644 --- a/Utilities/cmcurl/lib/http_ntlm.h +++ b/Utilities/cmcurl/lib/http_ntlm.h @@ -28,11 +28,11 @@ #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) -/* this is for ntlm header input */ +/* this is for NTLM header input */ CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy, const char *header); -/* this is for creating ntlm header output */ +/* this is for creating NTLM header output */ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy); void Curl_http_auth_cleanup_ntlm(struct connectdata *conn); diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index 7c035d4b6..a5f27f5ce 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -298,6 +298,7 @@ struct Curl_cftype Curl_cft_http_proxy = { http_proxy_cf_destroy, http_proxy_cf_connect, http_proxy_cf_close, + Curl_cf_def_shutdown, Curl_cf_http_proxy_get_host, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index c79567254..ed20cdc16 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -53,64 +53,105 @@ /* for macOS and iOS targets */ #if defined(USE_APPLE_IDN) #include +#include +#include -static CURLcode mac_idn_to_ascii(const char *in, char **out) +#define MAX_HOST_LENGTH 512 + +static CURLcode iconv_to_utf8(const char *in, size_t inlen, + char **out, size_t *outlen) { - UErrorCode err = U_ZERO_ERROR; - UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); - if(U_FAILURE(err)) { - return CURLE_OUT_OF_MEMORY; + iconv_t cd = iconv_open("UTF-8", nl_langinfo(CODESET)); + if(cd != (iconv_t)-1) { + size_t iconv_outlen = *outlen; + char *iconv_in = (char *)in; + size_t iconv_inlen = inlen; + size_t iconv_result = iconv(cd, &iconv_in, &iconv_inlen, + out, &iconv_outlen); + *outlen -= iconv_outlen; + iconv_close(cd); + if(iconv_result == (size_t)-1) { + if(errno == ENOMEM) + return CURLE_OUT_OF_MEMORY; + else + return CURLE_URL_MALFORMAT; + } + + return CURLE_OK; } else { - UIDNAInfo info = UIDNA_INFO_INITIALIZER; - char buffer[256] = {0}; - (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer, - sizeof(buffer), &info, &err); - uidna_close(idna); - if(U_FAILURE(err)) { - return CURLE_URL_MALFORMAT; - } - else { - *out = strdup(buffer); - if(*out) - return CURLE_OK; - else - return CURLE_OUT_OF_MEMORY; + if(errno == ENOMEM) + return CURLE_OUT_OF_MEMORY; + else + return CURLE_FAILED_INIT; + } +} + +static CURLcode mac_idn_to_ascii(const char *in, char **out) +{ + size_t inlen = strlen(in); + if(inlen < MAX_HOST_LENGTH) { + char iconv_buffer[MAX_HOST_LENGTH] = {0}; + char *iconv_outptr = iconv_buffer; + size_t iconv_outlen = sizeof(iconv_buffer); + CURLcode iconv_result = iconv_to_utf8(in, inlen, + &iconv_outptr, &iconv_outlen); + if(!iconv_result) { + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46( + UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err); + if(!U_FAILURE(err)) { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[MAX_HOST_LENGTH] = {0}; + (void)uidna_nameToASCII_UTF8(idna, iconv_buffer, (int)iconv_outlen, + buffer, sizeof(buffer) - 1, &info, &err); + uidna_close(idna); + if(!U_FAILURE(err) && !info.errors) { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } + } } + else + return iconv_result; } + return CURLE_URL_MALFORMAT; } static CURLcode mac_ascii_to_idn(const char *in, char **out) { - UErrorCode err = U_ZERO_ERROR; - UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); - if(U_FAILURE(err)) { - return CURLE_OUT_OF_MEMORY; - } - else { - UIDNAInfo info = UIDNA_INFO_INITIALIZER; - char buffer[256] = {0}; - (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, - sizeof(buffer), &info, &err); - uidna_close(idna); - if(U_FAILURE(err)) { - return CURLE_URL_MALFORMAT; - } - else { - *out = strdup(buffer); - if(*out) - return CURLE_OK; - else - return CURLE_OUT_OF_MEMORY; + size_t inlen = strlen(in); + if(inlen < MAX_HOST_LENGTH) { + UErrorCode err = U_ZERO_ERROR; + UIDNA* idna = uidna_openUTS46( + UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_UNICODE, &err); + if(!U_FAILURE(err)) { + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + char buffer[MAX_HOST_LENGTH] = {0}; + (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, + sizeof(buffer) - 1, &info, &err); + uidna_close(idna); + if(!U_FAILURE(err)) { + *out = strdup(buffer); + if(*out) + return CURLE_OK; + else + return CURLE_OUT_OF_MEMORY; + } } } + return CURLE_URL_MALFORMAT; } #endif #ifdef USE_WIN32_IDN /* using Windows kernel32 and normaliz libraries. */ -#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600 +#if (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600) && \ + (!defined(WINVER) || WINVER < 0x600) WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, const WCHAR *lpUnicodeCharStr, int cchUnicodeChar, @@ -207,7 +248,7 @@ bool Curl_is_ASCII_name(const char *hostname) * Curl_idn_decode() returns an allocated IDN decoded string if it was * possible. NULL on error. * - * CURLE_URL_MALFORMAT - the host name could not be converted + * CURLE_URL_MALFORMAT - the hostname could not be converted * CURLE_OUT_OF_MEMORY - memory problem * */ @@ -319,7 +360,7 @@ void Curl_free_idnconverted_hostname(struct hostname *host) */ CURLcode Curl_idnconvert_hostname(struct hostname *host) { - /* set the name we use to display the host name */ + /* set the name we use to display the hostname */ host->dispname = host->name; #ifdef USE_IDN diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index 42e14500b..55afd553d 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c @@ -216,7 +216,15 @@ if2ip_result_t Curl_if2ip(int af, memcpy(req.ifr_name, interf, len + 1); req.ifr_addr.sa_family = AF_INET; +#if defined(__GNUC__) && defined(_AIX) +/* Suppress warning inside system headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshift-sign-overflow" +#endif if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { +#if defined(__GNUC__) && defined(_AIX) +#pragma GCC diagnostic pop +#endif sclose(dummy); /* With SIOCGIFADDR, we cannot tell the difference between an interface that does not exist and an interface that has no address of the diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index f6e98bd81..4979a18ed 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -512,7 +512,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data, char *passwd; /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ + connect phase if we do not */ if(!data->state.aptr.user) { imap_state(data, IMAP_STOP); @@ -612,7 +612,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, saslprogress progress; /* Check if already authenticated OR if there is enough data to authenticate - with and end the connect phase if we don't */ + with and end the connect phase if we do not */ if(imapc->preauth || !Curl_sasl_can_authenticate(&imapc->sasl, data)) { imap_state(data, IMAP_STOP); @@ -776,7 +776,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) /* Prepare the mime data if some. */ if(data->set.mimepost.kind != MIMEKIND_NONE) { /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~MIME_BODY_ONLY; + data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ curl_mime_headers(&data->set.mimepost, data->set.headers, 0); @@ -814,8 +814,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; /* Send the APPEND command */ - result = imap_sendf(data, - "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", + result = imap_sendf(data, "APPEND %s (\\Seen) {%" FMT_OFF_T "}", mailbox, data->state.infilesize); free(mailbox); @@ -1168,8 +1167,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, } if(parsed) { - infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download", - size); + infof(data, "Found %" FMT_OFF_T " bytes to download", size); Curl_pgrsSetDownloadSize(data, size); if(pp->overflow) { @@ -1187,7 +1185,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, chunk = (size_t)size; if(!chunk) { - /* no size, we're done with the data */ + /* no size, we are done with the data */ imap_state(data, IMAP_STOP); return CURLE_OK; } @@ -1196,7 +1194,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(result) return result; - infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU + infof(data, "Written %zu bytes, %" FMT_OFF_TU " bytes are left for transfer", chunk, size - chunk); /* Have we used the entire overflow or just part of it?*/ @@ -1214,18 +1212,18 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, if(data->req.bytecount == size) /* The entire data is already transferred! */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); else { /* IMAP download */ data->req.maxdownload = size; /* force a recv/send check of this connection, as the data might've been read off the socket already */ data->state.select_bits = CURL_CSELECT_IN; - Curl_xfer_setup(data, FIRSTSOCKET, size, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE); } } else { - /* We don't know how to parse this line */ + /* We do not know how to parse this line */ failf(data, "Failed to parse FETCH response."); result = CURLE_WEIRD_SERVER_REPLY; } @@ -1269,7 +1267,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode, Curl_pgrsSetUploadSize(data, data->state.infilesize); /* IMAP upload */ - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* End of DO phase */ imap_state(data, IMAP_STOP); @@ -1694,7 +1692,7 @@ static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected) if(imap->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c index 4520b8715..a2812cf8e 100644 --- a/Utilities/cmcurl/lib/inet_ntop.c +++ b/Utilities/cmcurl/lib/inet_ntop.c @@ -58,7 +58,7 @@ * - uses no statics * - takes a unsigned char* not an in_addr as input */ -static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) +static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) { char tmp[sizeof("255.255.255.255")]; size_t len; @@ -84,14 +84,14 @@ static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) /* * Convert IPv6 binary address into presentation (printable) format. */ -static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) +static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like + * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. + * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; char *tp; @@ -168,7 +168,7 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) *tp++ = ':'; *tp++ = '\0'; - /* Check for overflow, copy, and we're done. + /* Check for overflow, copy, and we are done. */ if((size_t)(tp - tmp) > size) { errno = ENOSPC; @@ -185,10 +185,9 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) * Returns NULL on error and errno set with the specific * error, EAFNOSUPPORT or ENOSPC. * - * On Windows we store the error in the thread errno, not - * in the winsock error code. This is to avoid losing the - * actual last winsock error. So when this function returns - * NULL, check errno not SOCKERRNO. + * On Windows we store the error in the thread errno, not in the Winsock error + * code. This is to avoid losing the actual last Winsock error. When this + * function returns NULL, check errno not SOCKERRNO. */ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) { diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h index 7c3ead434..f592f2525 100644 --- a/Utilities/cmcurl/lib/inet_ntop.h +++ b/Utilities/cmcurl/lib/inet_ntop.h @@ -32,8 +32,13 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); #ifdef HAVE_ARPA_INET_H #include #endif +#ifdef _WIN32 #define Curl_inet_ntop(af,addr,buf,size) \ - inet_ntop(af, addr, buf, (curl_socklen_t)size) + inet_ntop(af, addr, buf, size) +#else +#define Curl_inet_ntop(af,addr,buf,size) \ + inet_ntop(af, addr, buf, (curl_socklen_t)(size)) +#endif #endif #endif /* HEADER_CURL_INET_NTOP_H */ diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c index 9cfcec1c9..97e6f80d7 100644 --- a/Utilities/cmcurl/lib/inet_pton.c +++ b/Utilities/cmcurl/lib/inet_pton.c @@ -48,8 +48,8 @@ #endif /* - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + * WARNING: Do not even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static int inet_pton4(const char *src, unsigned char *dst); @@ -61,12 +61,12 @@ static int inet_pton6(const char *src, unsigned char *dst); * to network format (which is usually some kind of binary format). * return: * 1 if the address was valid for the specified address family - * 0 if the address wasn't valid (`dst' is untouched in this case) + * 0 if the address was not valid (`dst' is untouched in this case) * -1 if some other error occurred (`dst' is untouched in this case, too) * notice: * On Windows we store the error in the thread errno, not - * in the winsock error code. This is to avoid losing the - * actual last winsock error. So when this function returns + * in the Winsock error code. This is to avoid losing the + * actual last Winsock error. When this function returns * -1, check errno not SOCKERRNO. * author: * Paul Vixie, 1996. @@ -92,7 +92,7 @@ Curl_inet_pton(int af, const char *src, void *dst) * return: * 1 if `src' is a valid dotted quad, else 0. * notice: - * does not touch `dst' unless it's returning 1. + * does not touch `dst' unless it is returning 1. * author: * Paul Vixie, 1996. */ @@ -147,7 +147,7 @@ inet_pton4(const char *src, unsigned char *dst) * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: - * (1) does not touch `dst' unless it's returning 1. + * (1) does not touch `dst' unless it is returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. @@ -221,7 +221,7 @@ inet_pton6(const char *src, unsigned char *dst) if(colonp) { /* * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. + * overlapping regions, we will do the shift by hand. */ const ssize_t n = tp - colonp; ssize_t i; diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index d355665b8..f3649cd1a 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -25,7 +25,7 @@ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -91,7 +91,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_xfer_send(data, sptr, write_len, &bytes_written); + result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; @@ -169,7 +169,7 @@ krb5_encode(void *app_data, const void *from, int length, int level, void **to) * libraries modify the input buffer in gss_wrap() */ dec.value = (void *)from; - dec.length = length; + dec.length = (size_t)length; maj = gss_wrap(&min, *context, level == PROT_PRIVATE, GSS_C_QOP_DEFAULT, @@ -178,7 +178,7 @@ krb5_encode(void *app_data, const void *from, int length, int level, void **to) if(maj != GSS_S_COMPLETE) return -1; - /* malloc a new buffer, in case gss_release_buffer doesn't work as + /* malloc a new buffer, in case gss_release_buffer does not work as expected */ *to = malloc(enc.length); if(!*to) @@ -227,7 +227,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) /* this loop will execute twice (once for service, once for host) */ for(;;) { - /* this really shouldn't be repeated here, but can't help it */ + /* this really should not be repeated here, but cannot help it */ if(service == srv_host) { result = ftpsend(data, conn, "AUTH GSSAPI"); if(result) @@ -329,24 +329,27 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) size_t len = Curl_dyn_len(&pp->recvbuf); p = Curl_dyn_ptr(&pp->recvbuf); if((len < 4) || (p[0] != '2' && p[0] != '3')) { - infof(data, "Server didn't accept auth data"); + infof(data, "Server did not accept auth data"); ret = AUTH_ERROR; break; } } _gssresp.value = NULL; /* make sure it is initialized */ + _gssresp.length = 0; p += 4; /* over '789 ' */ p = strstr(p, "ADAT="); if(p) { - result = Curl_base64_decode(p + 5, - (unsigned char **)&_gssresp.value, - &_gssresp.length); + unsigned char *outptr; + size_t outlen; + result = Curl_base64_decode(p + 5, &outptr, &outlen); if(result) { failf(data, "base64-decoding: %s", curl_easy_strerror(result)); ret = AUTH_CONTINUE; break; } + _gssresp.value = outptr; + _gssresp.length = outlen; } gssresp = &_gssresp; @@ -497,7 +500,7 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to, size_t written; while(len > 0) { - result = Curl_conn_send(data, sockindex, to_p, len, &written); + result = Curl_conn_send(data, sockindex, to_p, len, FALSE, &written); if(!result && written > 0) { len -= written; to_p += written; @@ -524,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex, return result; if(len) { - len = ntohl(len); + len = (int)ntohl((uint32_t)len); if(len > CURL_MAX_INPUT_LENGTH) return CURLE_TOO_LARGE; @@ -536,7 +539,7 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex, do { char buffer[1024]; nread = CURLMIN(len, (int)sizeof(buffer)); - result = socket_read(data, sockindex, buffer, nread); + result = socket_read(data, sockindex, buffer, (size_t)nread); if(result) return result; result = Curl_dyn_addn(&buf->buf, buffer, nread); @@ -630,7 +633,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, else prot_level = conn->command_prot; } - bytes = conn->mech->encode(conn->app_data, from, length, prot_level, + bytes = conn->mech->encode(conn->app_data, from, length, (int)prot_level, (void **)&buffer); if(!buffer || bytes <= 0) return; /* error */ @@ -658,7 +661,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, } } else { - htonl_bytes = htonl(bytes); + htonl_bytes = (int)htonl((OM_uint32)bytes); socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes)); socket_write(data, fd, buffer, curlx_sitouz(bytes)); } @@ -686,10 +689,12 @@ static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, /* Matches Curl_send signature */ static ssize_t sec_send(struct Curl_easy *data, int sockindex, - const void *buffer, size_t len, CURLcode *err) + const void *buffer, size_t len, bool eos, + CURLcode *err) { struct connectdata *conn = data->conn; curl_socket_t fd = conn->sock[sockindex]; + (void)eos; /* unused */ *err = CURLE_OK; return sec_write(data, conn, fd, buffer, len); } @@ -724,7 +729,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, decoded_len = curlx_uztosi(decoded_sz); decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, - level, conn); + (int)level, conn); if(decoded_len <= 0) { free(buf); return -1; @@ -789,7 +794,7 @@ static int sec_set_protection_level(struct Curl_easy *data) if(pbsz) { /* stick to default value if the check fails */ if(ISDIGIT(pbsz[5])) - buffer_size = atoi(&pbsz[5]); + buffer_size = (unsigned int)atoi(&pbsz[5]); if(buffer_size < conn->buffer_size) conn->buffer_size = buffer_size; } @@ -878,7 +883,7 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn) if(ret != AUTH_CONTINUE) { if(ret != AUTH_OK) { - /* Mechanism has dumped the error to stderr, don't error here. */ + /* Mechanism has dumped the error to stderr, do not error here. */ return CURLE_USE_SSL_FAILED; } DEBUGASSERT(ret == AUTH_OK); diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 678b4d5af..01429ba79 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -143,7 +143,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #endif #if defined(USE_WIN32_LDAP) && defined(ldap_err2string) -/* Use ansi error strings in UNICODE builds */ +/* Use ANSI error strings in Unicode builds */ #undef ldap_err2string #define ldap_err2string ldap_err2stringA #endif @@ -252,16 +252,17 @@ static int ldap_win_bind_auth(LDAP *server, const char *user, } if(method && user && passwd) { - rc = Curl_create_sspi_identity(user, passwd, &cred); + CURLcode res = Curl_create_sspi_identity(user, passwd, &cred); + rc = (int)res; if(!rc) { - rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method); + rc = (int)ldap_bind_s(server, NULL, (TCHAR *)&cred, method); Curl_sspi_free_identity(&cred); } } else { /* proceed with current user credentials */ method = LDAP_AUTH_NEGOTIATE; - rc = ldap_bind_s(server, NULL, NULL, method); + rc = (int)ldap_bind_s(server, NULL, NULL, method); } return rc; } @@ -279,14 +280,14 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server, inuser = curlx_convert_UTF8_to_tchar((char *) user); inpass = curlx_convert_UTF8_to_tchar((char *) passwd); - rc = ldap_simple_bind_s(server, inuser, inpass); + rc = (int)ldap_simple_bind_s(server, inuser, inpass); curlx_unicodefree(inuser); curlx_unicodefree(inpass); } #if defined(USE_WINDOWS_SSPI) else { - rc = ldap_win_bind_auth(server, user, passwd, data->set.httpauth); + rc = (int)ldap_win_bind_auth(server, user, passwd, data->set.httpauth); } #endif @@ -296,8 +297,10 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server, #if defined(USE_WIN32_LDAP) #define FREE_ON_WINLDAP(x) curlx_unicodefree(x) +#define curl_ldap_num_t ULONG #else #define FREE_ON_WINLDAP(x) +#define curl_ldap_num_t int #endif @@ -337,7 +340,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) rc = _ldap_url_parse(data, conn, &ludp); #endif if(rc) { - failf(data, "Bad LDAP URL: %s", ldap_err2string(rc)); + failf(data, "Bad LDAP URL: %s", ldap_err2string((curl_ldap_num_t)rc)); result = CURLE_URL_MALFORMAT; goto quit; } @@ -372,8 +375,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP - /* Win32 LDAP SDK doesn't support insecure mode without CA! */ - server = ldap_sslinit(host, conn->primary.remote_port, 1); + /* Win32 LDAP SDK does not support insecure mode without CA! */ + server = ldap_sslinit(host, (curl_ldap_num_t)conn->primary.remote_port, 1); ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); #else int ldap_option; @@ -503,7 +506,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } else { - server = ldap_init(host, conn->primary.remote_port); + server = ldap_init(host, (curl_ldap_num_t)conn->primary.remote_port); if(!server) { failf(data, "LDAP local: Cannot connect to %s:%u", conn->host.dispname, conn->primary.remote_port); @@ -529,7 +532,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(rc) { #ifdef USE_WIN32_LDAP failf(data, "LDAP local: bind via ldap_win_bind %s", - ldap_err2string(rc)); + ldap_err2string((ULONG)rc)); #else failf(data, "LDAP local: bind via ldap_simple_bind_s %s", ldap_err2string(rc)); @@ -539,11 +542,12 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) } Curl_pgrsSetDownloadCounter(data, 0); - rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); + rc = (int)ldap_search_s(server, ludp->lud_dn, + (curl_ldap_num_t)ludp->lud_scope, + ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) { - failf(data, "LDAP remote: %s", ldap_err2string(rc)); + failf(data, "LDAP remote: %s", ldap_err2string((curl_ldap_num_t)rc)); result = CURLE_LDAP_SEARCH_FAILED; goto quit; } @@ -754,7 +758,7 @@ quit: FREE_ON_WINLDAP(host); /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); connclose(conn, "LDAP connection always disable reuse"); return result; diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc index daa2d62d8..1ceb4691f 100644 --- a/Utilities/cmcurl/lib/libcurl.rc +++ b/Utilities/cmcurl/lib/libcurl.rc @@ -32,7 +32,7 @@ VS_VERSION_INFO VERSIONINFO FILEVERSION RC_VERSION PRODUCTVERSION RC_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#if defined(DEBUGBUILD) || defined(_DEBUG) +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0L diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index 716f0cd57..7e19cd509 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c @@ -32,16 +32,34 @@ /* this must be the last include file */ #include "memdebug.h" +#define LLISTINIT 0x100cc001 /* random pattern */ +#define NODEINIT 0x12344321 /* random pattern */ +#define NODEREM 0x54321012 /* random pattern */ + + +#ifdef DEBUGBUILD +#define VERIFYNODE(x) verifynode(x) +static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) +{ + DEBUGASSERT(!n || (n->_init == NODEINIT)); + return n; +} +#else +#define VERIFYNODE(x) x +#endif /* * @unittest: 1300 */ void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) { - l->size = 0; - l->dtor = dtor; - l->head = NULL; - l->tail = NULL; + l->_size = 0; + l->_dtor = dtor; + l->_head = NULL; + l->_tail = NULL; +#ifdef DEBUGBUILD + l->_init = LLISTINIT; +#endif } /* @@ -56,36 +74,45 @@ Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) * @unittest: 1300 */ void -Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e, +Curl_llist_insert_next(struct Curl_llist *list, + struct Curl_llist_node *e, /* may be NULL */ const void *p, - struct Curl_llist_element *ne) + struct Curl_llist_node *ne) { - ne->ptr = (void *) p; - if(list->size == 0) { - list->head = ne; - list->head->prev = NULL; - list->head->next = NULL; - list->tail = ne; + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + DEBUGASSERT(ne); + +#ifdef DEBUGBUILD + ne->_init = NODEINIT; +#endif + ne->_ptr = (void *) p; + ne->_list = list; + if(list->_size == 0) { + list->_head = ne; + list->_head->_prev = NULL; + list->_head->_next = NULL; + list->_tail = ne; } else { /* if 'e' is NULL here, we insert the new element first in the list */ - ne->next = e?e->next:list->head; - ne->prev = e; + ne->_next = e?e->_next:list->_head; + ne->_prev = e; if(!e) { - list->head->prev = ne; - list->head = ne; + list->_head->_prev = ne; + list->_head = ne; } - else if(e->next) { - e->next->prev = ne; + else if(e->_next) { + e->_next->_prev = ne; } else { - list->tail = ne; + list->_tail = ne; } if(e) - e->next = ne; + e->_next = ne; } - ++list->size; + ++list->_size; } /* @@ -99,64 +126,141 @@ Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e, */ void Curl_llist_append(struct Curl_llist *list, const void *p, - struct Curl_llist_element *ne) + struct Curl_llist_node *ne) { - Curl_llist_insert_next(list, list->tail, p, ne); + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + DEBUGASSERT(ne); + Curl_llist_insert_next(list, list->_tail, p, ne); } /* * @unittest: 1300 */ void -Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e, - void *user) +Curl_node_uremove(struct Curl_llist_node *e, void *user) { void *ptr; - if(!e || list->size == 0) + struct Curl_llist *list; + if(!e) return; - if(e == list->head) { - list->head = e->next; + list = e->_list; + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + DEBUGASSERT(list->_size); + DEBUGASSERT(e->_init == NODEINIT); + if(e == list->_head) { + list->_head = e->_next; - if(!list->head) - list->tail = NULL; + if(!list->_head) + list->_tail = NULL; else - e->next->prev = NULL; + e->_next->_prev = NULL; } else { - if(e->prev) - e->prev->next = e->next; + if(e->_prev) + e->_prev->_next = e->_next; - if(!e->next) - list->tail = e->prev; + if(!e->_next) + list->_tail = e->_prev; else - e->next->prev = e->prev; + e->_next->_prev = e->_prev; } - ptr = e->ptr; + ptr = e->_ptr; - e->ptr = NULL; - e->prev = NULL; - e->next = NULL; + e->_list = NULL; + e->_ptr = NULL; + e->_prev = NULL; + e->_next = NULL; +#ifdef DEBUGBUILD + e->_init = NODEREM; /* specific pattern on remove - not zero */ +#endif - --list->size; + --list->_size; /* call the dtor() last for when it actually frees the 'e' memory itself */ - if(list->dtor) - list->dtor(user, ptr); + if(list->_dtor) + list->_dtor(user, ptr); +} + +void Curl_node_remove(struct Curl_llist_node *e) +{ + Curl_node_uremove(e, NULL); } void Curl_llist_destroy(struct Curl_llist *list, void *user) { if(list) { - while(list->size > 0) - Curl_llist_remove(list, list->tail, user); + DEBUGASSERT(list->_init == LLISTINIT); + while(list->_size > 0) + Curl_node_uremove(list->_tail, user); } } -size_t -Curl_llist_count(struct Curl_llist *list) +/* Curl_llist_head() returns the first 'struct Curl_llist_node *', which + might be NULL */ +struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) +{ + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + return VERIFYNODE(list->_head); +} + +#ifdef UNITTESTS +/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which + might be NULL */ +struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) +{ + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + return VERIFYNODE(list->_tail); +} +#endif + +/* Curl_llist_count() returns a size_t the number of nodes in the list */ +size_t Curl_llist_count(struct Curl_llist *list) +{ + DEBUGASSERT(list); + DEBUGASSERT(list->_init == LLISTINIT); + return list->_size; +} + +/* Curl_node_elem() returns the custom data from a Curl_llist_node */ +void *Curl_node_elem(struct Curl_llist_node *n) +{ + DEBUGASSERT(n); + DEBUGASSERT(n->_init == NODEINIT); + return n->_ptr; +} + +/* Curl_node_next() returns the next element in a list from a given + Curl_llist_node */ +struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) +{ + DEBUGASSERT(n); + DEBUGASSERT(n->_init == NODEINIT); + return VERIFYNODE(n->_next); +} + +#ifdef UNITTESTS + +/* Curl_node_prev() returns the previous element in a list from a given + Curl_llist_node */ +struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) +{ + DEBUGASSERT(n); + DEBUGASSERT(n->_init == NODEINIT); + return VERIFYNODE(n->_prev); +} + +#endif + +struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) { - return list->size; + DEBUGASSERT(n); + DEBUGASSERT(!n->_list || n->_init == NODEINIT); + return n->_list; } diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index d75582fa9..26581869a 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h @@ -27,28 +27,63 @@ #include "curl_setup.h" #include -typedef void (*Curl_llist_dtor)(void *, void *); +typedef void (*Curl_llist_dtor)(void *user, void *elem); -struct Curl_llist_element { - void *ptr; - struct Curl_llist_element *prev; - struct Curl_llist_element *next; -}; +/* none of these struct members should be referenced directly, use the + dedicated functions */ struct Curl_llist { - struct Curl_llist_element *head; - struct Curl_llist_element *tail; - Curl_llist_dtor dtor; - size_t size; + struct Curl_llist_node *_head; + struct Curl_llist_node *_tail; + Curl_llist_dtor _dtor; + size_t _size; +#ifdef DEBUGBUILD + int _init; /* detect API usage mistakes */ +#endif +}; + +struct Curl_llist_node { + struct Curl_llist *_list; /* the list where this belongs */ + void *_ptr; + struct Curl_llist_node *_prev; + struct Curl_llist_node *_next; +#ifdef DEBUGBUILD + int _init; /* detect API usage mistakes */ +#endif }; void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); -void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *, - const void *, struct Curl_llist_element *node); +void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_node *, + const void *, struct Curl_llist_node *node); void Curl_llist_append(struct Curl_llist *, - const void *, struct Curl_llist_element *node); -void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *, - void *); -size_t Curl_llist_count(struct Curl_llist *); + const void *, struct Curl_llist_node *node); +void Curl_node_uremove(struct Curl_llist_node *, void *); +void Curl_node_remove(struct Curl_llist_node *); void Curl_llist_destroy(struct Curl_llist *, void *); + +/* Curl_llist_head() returns the first 'struct Curl_llist_node *', which + might be NULL */ +struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); + +/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which + might be NULL */ +struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list); + +/* Curl_llist_count() returns a size_t the number of nodes in the list */ +size_t Curl_llist_count(struct Curl_llist *list); + +/* Curl_node_elem() returns the custom data from a Curl_llist_node */ +void *Curl_node_elem(struct Curl_llist_node *n); + +/* Curl_node_next() returns the next element in a list from a given + Curl_llist_node */ +struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); + +/* Curl_node_prev() returns the previous element in a list from a given + Curl_llist_node */ +struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n); + +/* Curl_node_llist() return the list the node is in or NULL. */ +struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n); + #endif /* HEADER_CURL_LLIST_H */ diff --git a/Utilities/cmcurl/lib/macos.c b/Utilities/cmcurl/lib/macos.c index 9e8e76e86..e4662be1d 100644 --- a/Utilities/cmcurl/lib/macos.c +++ b/Utilities/cmcurl/lib/macos.c @@ -34,21 +34,19 @@ CURLcode Curl_macos_init(void) { - { - /* - * The automagic conversion from IPv4 literals to IPv6 literals only - * works if the SCDynamicStoreCopyProxies system function gets called - * first. As Curl currently doesn't support system-wide HTTP proxies, we - * therefore don't use any value this function might return. - * - * This function is only available on macOS and is not needed for - * IPv4-only builds, hence the conditions for defining - * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h. - */ - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); - if(dict) - CFRelease(dict); - } + /* + * The automagic conversion from IPv4 literals to IPv6 literals only + * works if the SCDynamicStoreCopyProxies system function gets called + * first. As Curl currently does not support system-wide HTTP proxies, we + * therefore do not use any value this function might return. + * + * This function is only available on macOS and is not needed for + * IPv4-only builds, hence the conditions for defining + * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h. + */ + CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); + if(dict) + CFRelease(dict); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index db0028135..f006bdcf0 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -37,6 +37,9 @@ #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL) /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 +#else +/* Cover also OPENSSL_NO_MD4 configured in openssl */ +#include #endif #endif /* USE_OPENSSL */ @@ -217,7 +220,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) } #else -/* When no other crypto library is available, or the crypto library doesn't +/* When no other crypto library is available, or the crypto library does not * support MD4, we use this code segment this implementation of it * * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. @@ -229,8 +232,8 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * Author: * Alexander Peslyak, better known as Solar Designer * - * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. In case + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. In case * this attempt to disclaim copyright and place the software in the public * domain is deemed null and void, then the software is Copyright (c) 2001 * Alexander Peslyak and it is hereby released to the general public under the @@ -239,19 +242,19 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * Redistribution and use in source and binary forms, with or without * modification, are permitted. * - * There's ABSOLUTELY NO WARRANTY, express or implied. + * There is ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) * * This differs from Colin Plumb's older public domain implementation in that * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there's no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from + * unsigned integer data type will do), there is no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from * Colin Plumb's implementation has been reused; this comment merely compares * the properties of the two independent implementations. * * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known + * It is meant to be fast, but not as fast as possible. Some known * optimizations are not included to reduce source code size and avoid * compile-time configuration. */ @@ -277,14 +280,14 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx); * F and G are optimized compared to their RFC 1320 definitions, with the * optimization for F borrowed from Colin Plumb's MD5 implementation. */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define MD4_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define MD4_G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define MD4_H(x, y, z) ((x) ^ (y) ^ (z)) /* * The MD4 transformation for all three rounds. */ -#define STEP(f, a, b, c, d, x, s) \ +#define MD4_STEP(f, a, b, c, d, x, s) \ (a) += f((b), (c), (d)) + (x); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); @@ -293,30 +296,31 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx); * in a properly aligned word in host byte order. * * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * doesn't work. + * memory accesses is just an optimization. Nothing will break if it + * does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define SET(n) \ +#define MD4_SET(n) \ (*(MD4_u32plus *)(void *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) +#define MD4_GET(n) \ + MD4_SET(n) #else -#define SET(n) \ +#define MD4_SET(n) \ (ctx->block[(n)] = \ (MD4_u32plus)ptr[(n) * 4] | \ ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ +#define MD4_GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update - * the bit counters. There are no alignment requirements. + * the bit counters. There are no alignment requirements. */ -static const void *body(MD4_CTX *ctx, const void *data, unsigned long size) +static const void *my_md4_body(MD4_CTX *ctx, + const void *data, unsigned long size) { const unsigned char *ptr; MD4_u32plus a, b, c, d; @@ -337,58 +341,58 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size) saved_d = d; /* Round 1 */ - STEP(F, a, b, c, d, SET(0), 3) - STEP(F, d, a, b, c, SET(1), 7) - STEP(F, c, d, a, b, SET(2), 11) - STEP(F, b, c, d, a, SET(3), 19) - STEP(F, a, b, c, d, SET(4), 3) - STEP(F, d, a, b, c, SET(5), 7) - STEP(F, c, d, a, b, SET(6), 11) - STEP(F, b, c, d, a, SET(7), 19) - STEP(F, a, b, c, d, SET(8), 3) - STEP(F, d, a, b, c, SET(9), 7) - STEP(F, c, d, a, b, SET(10), 11) - STEP(F, b, c, d, a, SET(11), 19) - STEP(F, a, b, c, d, SET(12), 3) - STEP(F, d, a, b, c, SET(13), 7) - STEP(F, c, d, a, b, SET(14), 11) - STEP(F, b, c, d, a, SET(15), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(0), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(1), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(2), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(3), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(4), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(5), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(6), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(7), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(8), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(9), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(10), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(11), 19) + MD4_STEP(MD4_F, a, b, c, d, MD4_SET(12), 3) + MD4_STEP(MD4_F, d, a, b, c, MD4_SET(13), 7) + MD4_STEP(MD4_F, c, d, a, b, MD4_SET(14), 11) + MD4_STEP(MD4_F, b, c, d, a, MD4_SET(15), 19) /* Round 2 */ - STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(0) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(4) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(8) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(12) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(1) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(5) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(9) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(13) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(2) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(6) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(10) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(14) + 0x5a827999, 13) + MD4_STEP(MD4_G, a, b, c, d, MD4_GET(3) + 0x5a827999, 3) + MD4_STEP(MD4_G, d, a, b, c, MD4_GET(7) + 0x5a827999, 5) + MD4_STEP(MD4_G, c, d, a, b, MD4_GET(11) + 0x5a827999, 9) + MD4_STEP(MD4_G, b, c, d, a, MD4_GET(15) + 0x5a827999, 13) /* Round 3 */ - STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(0) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(8) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(4) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(12) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(2) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(10) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(6) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(14) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(1) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(9) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(5) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(13) + 0x6ed9eba1, 15) + MD4_STEP(MD4_H, a, b, c, d, MD4_GET(3) + 0x6ed9eba1, 3) + MD4_STEP(MD4_H, d, a, b, c, MD4_GET(11) + 0x6ed9eba1, 9) + MD4_STEP(MD4_H, c, d, a, b, MD4_GET(7) + 0x6ed9eba1, 11) + MD4_STEP(MD4_H, b, c, d, a, MD4_GET(15) + 0x6ed9eba1, 15) a += saved_a; b += saved_b; @@ -442,11 +446,11 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) memcpy(&ctx->buffer[used], data, available); data = (const unsigned char *)data + available; size -= available; - body(ctx, ctx->buffer, 64); + my_md4_body(ctx, ctx->buffer, 64); } if(size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); + data = my_md4_body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } @@ -465,7 +469,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) if(available < 8) { memset(&ctx->buffer[used], 0, available); - body(ctx, ctx->buffer, 64); + my_md4_body(ctx, ctx->buffer, 64); used = 0; available = 64; } @@ -482,7 +486,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); - body(ctx, ctx->buffer, 64); + my_md4_body(ctx, ctx->buffer, 64); result[0] = curlx_ultouc((ctx->a)&0xff); result[1] = curlx_ultouc((ctx->a >> 8)&0xff); diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 01415af91..7b51429b4 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -172,7 +172,7 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) /* For Apple operating systems: CommonCrypto has the functions we need. These functions are available on Tiger and later, as well as iOS 2.0 - and later. If you're building for an older cat, well, sorry. + and later. If you are building for an older cat, well, sorry. Declaring the functions as static like this seems to be a bit more reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ @@ -254,7 +254,7 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) * Author: * Alexander Peslyak, better known as Solar Designer * - * This software was written by Alexander Peslyak in 2001. No copyright is + * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is @@ -264,19 +264,19 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) * Redistribution and use in source and binary forms, with or without * modification, are permitted. * - * There's ABSOLUTELY NO WARRANTY, express or implied. + * There is ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) * * This differs from Colin Plumb's older public domain implementation in that * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there's no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from + * unsigned integer data type will do), there is no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from * Colin Plumb's implementation has been reused; this comment merely compares * the properties of the two independent implementations. * * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known + * It is meant to be fast, but not as fast as possible. Some known * optimizations are not included to reduce source code size and avoid * compile-time configuration. */ @@ -304,16 +304,16 @@ static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); * architectures that lack an AND-NOT instruction, just like in Colin Plumb's * implementation. */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -#define H(x, y, z) (((x) ^ (y)) ^ (z)) -#define H2(x, y, z) ((x) ^ ((y) ^ (z))) -#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define MD5_G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define MD5_H(x, y, z) (((x) ^ (y)) ^ (z)) +#define MD5_H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define MD5_I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ -#define STEP(f, a, b, c, d, x, t, s) \ +#define MD5_STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); @@ -323,30 +323,31 @@ static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); * in a properly aligned word in host byte order. * * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * doesn't work. + * memory accesses is just an optimization. Nothing will break if it + * does not work. */ #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define SET(n) \ +#define MD5_SET(n) \ (*(MD5_u32plus *)(void *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) +#define MD5_GET(n) \ + MD5_SET(n) #else -#define SET(n) \ +#define MD5_SET(n) \ (ctx->block[(n)] = \ (MD5_u32plus)ptr[(n) * 4] | \ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ +#define MD5_GET(n) \ (ctx->block[(n)]) #endif /* * This processes one or more 64-byte data blocks, but does NOT update - * the bit counters. There are no alignment requirements. + * the bit counters. There are no alignment requirements. */ -static const void *body(my_md5_ctx *ctx, const void *data, unsigned long size) +static const void *my_md5_body(my_md5_ctx *ctx, + const void *data, unsigned long size) { const unsigned char *ptr; MD5_u32plus a, b, c, d; @@ -367,76 +368,76 @@ static const void *body(my_md5_ctx *ctx, const void *data, unsigned long size) saved_d = d; /* Round 1 */ - STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) - STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) - STEP(F, c, d, a, b, SET(2), 0x242070db, 17) - STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) - STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) - STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) - STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) - STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) - STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) - STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) - STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) - STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) - STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) - STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) - STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) - STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(0), 0xd76aa478, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(1), 0xe8c7b756, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(2), 0x242070db, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(3), 0xc1bdceee, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(4), 0xf57c0faf, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(5), 0x4787c62a, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(6), 0xa8304613, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(7), 0xfd469501, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(8), 0x698098d8, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(9), 0x8b44f7af, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(10), 0xffff5bb1, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(11), 0x895cd7be, 22) + MD5_STEP(MD5_F, a, b, c, d, MD5_SET(12), 0x6b901122, 7) + MD5_STEP(MD5_F, d, a, b, c, MD5_SET(13), 0xfd987193, 12) + MD5_STEP(MD5_F, c, d, a, b, MD5_SET(14), 0xa679438e, 17) + MD5_STEP(MD5_F, b, c, d, a, MD5_SET(15), 0x49b40821, 22) /* Round 2 */ - STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) - STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) - STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) - STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) - STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) - STEP(G, d, a, b, c, GET(10), 0x02441453, 9) - STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) - STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) - STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) - STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) - STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) - STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) - STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) - STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) - STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) - STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(1), 0xf61e2562, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(6), 0xc040b340, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(11), 0x265e5a51, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(0), 0xe9b6c7aa, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(5), 0xd62f105d, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(10), 0x02441453, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(15), 0xd8a1e681, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(4), 0xe7d3fbc8, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(9), 0x21e1cde6, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(14), 0xc33707d6, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(3), 0xf4d50d87, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(8), 0x455a14ed, 20) + MD5_STEP(MD5_G, a, b, c, d, MD5_GET(13), 0xa9e3e905, 5) + MD5_STEP(MD5_G, d, a, b, c, MD5_GET(2), 0xfcefa3f8, 9) + MD5_STEP(MD5_G, c, d, a, b, MD5_GET(7), 0x676f02d9, 14) + MD5_STEP(MD5_G, b, c, d, a, MD5_GET(12), 0x8d2a4c8a, 20) /* Round 3 */ - STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) - STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) - STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) - STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) - STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) - STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) - STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) - STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) - STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) - STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) - STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) - STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) - STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) - STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) - STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) - STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(5), 0xfffa3942, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(8), 0x8771f681, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(11), 0x6d9d6122, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(14), 0xfde5380c, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(1), 0xa4beea44, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(4), 0x4bdecfa9, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(7), 0xf6bb4b60, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(10), 0xbebfbc70, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(13), 0x289b7ec6, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(0), 0xeaa127fa, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(3), 0xd4ef3085, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(6), 0x04881d05, 23) + MD5_STEP(MD5_H, a, b, c, d, MD5_GET(9), 0xd9d4d039, 4) + MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(12), 0xe6db99e5, 11) + MD5_STEP(MD5_H, c, d, a, b, MD5_GET(15), 0x1fa27cf8, 16) + MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(2), 0xc4ac5665, 23) /* Round 4 */ - STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) - STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) - STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) - STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) - STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) - STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) - STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) - STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) - STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) - STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) - STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) - STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) - STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) - STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) - STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) - STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(0), 0xf4292244, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(7), 0x432aff97, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(14), 0xab9423a7, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(5), 0xfc93a039, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(12), 0x655b59c3, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(3), 0x8f0ccc92, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(10), 0xffeff47d, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(1), 0x85845dd1, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(8), 0x6fa87e4f, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(15), 0xfe2ce6e0, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(6), 0xa3014314, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(13), 0x4e0811a1, 21) + MD5_STEP(MD5_I, a, b, c, d, MD5_GET(4), 0xf7537e82, 6) + MD5_STEP(MD5_I, d, a, b, c, MD5_GET(11), 0xbd3af235, 10) + MD5_STEP(MD5_I, c, d, a, b, MD5_GET(2), 0x2ad7d2bb, 15) + MD5_STEP(MD5_I, b, c, d, a, MD5_GET(9), 0xeb86d391, 21) a += saved_a; b += saved_b; @@ -492,11 +493,11 @@ static void my_md5_update(my_md5_ctx *ctx, const void *data, memcpy(&ctx->buffer[used], data, available); data = (const unsigned char *)data + available; size -= available; - body(ctx, ctx->buffer, 64); + my_md5_body(ctx, ctx->buffer, 64); } if(size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); + data = my_md5_body(ctx, data, size & ~(unsigned long)0x3f); size &= 0x3f; } @@ -515,7 +516,7 @@ static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) if(available < 8) { memset(&ctx->buffer[used], 0, available); - body(ctx, ctx->buffer, 64); + my_md5_body(ctx, ctx->buffer, 64); used = 0; available = 64; } @@ -532,7 +533,7 @@ static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); - body(ctx, ctx->buffer, 64); + my_md5_body(ctx, ctx->buffer, 64); result[0] = curlx_ultouc((ctx->a)&0xff); result[1] = curlx_ultouc((ctx->a >> 8)&0xff); diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index fce933a32..bc83d3eea 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c @@ -30,7 +30,7 @@ #include "urldata.h" -#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */ +#define MEMDEBUG_NODEFINES /* do not redefine the standard functions */ /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -44,8 +44,8 @@ struct memdebug { double d; void *p; } mem[1]; - /* I'm hoping this is the thing with the strictest alignment - * requirements. That also means we waste some space :-( */ + /* I am hoping this is the thing with the strictest alignment + * requirements. That also means we waste some space :-( */ }; /* @@ -53,7 +53,7 @@ struct memdebug { * remain so. For advanced analysis, record a log file and write perl scripts * to analyze them! * - * Don't use these with multithreaded test programs! + * Do not use these with multithreaded test programs! */ FILE *curl_dbg_logfile = NULL; @@ -75,7 +75,7 @@ static void curl_dbg_cleanup(void) curl_dbg_logfile = NULL; } -/* this sets the log file name */ +/* this sets the log filename */ void curl_dbg_memdebug(const char *logname) { if(!curl_dbg_logfile) { @@ -84,7 +84,7 @@ void curl_dbg_memdebug(const char *logname) else curl_dbg_logfile = stderr; #ifdef MEMDEBUG_LOG_SYNC - /* Flush the log file after every line so the log isn't lost in a crash */ + /* Flush the log file after every line so the log is not lost in a crash */ if(curl_dbg_logfile) setbuf(curl_dbg_logfile, (char *)NULL); #endif @@ -103,7 +103,7 @@ void curl_dbg_memlimit(long limit) } } -/* returns TRUE if this isn't allowed! */ +/* returns TRUE if this is not allowed! */ static bool countcheck(const char *func, int line, const char *source) { /* if source is NULL, then the call is made internally and this check @@ -312,7 +312,7 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol, sockfd = socket(domain, type, protocol); if(source && (sockfd != CURL_SOCKET_BAD)) - curl_dbg_log("FD %s:%d socket() = %" CURL_FORMAT_SOCKET_T "\n", + curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n", source, line, sockfd); return sockfd; @@ -356,8 +356,8 @@ int curl_dbg_socketpair(int domain, int type, int protocol, if(source && (0 == res)) curl_dbg_log("FD %s:%d socketpair() = " - "%" CURL_FORMAT_SOCKET_T " %" CURL_FORMAT_SOCKET_T "\n", - source, line, socket_vector[0], socket_vector[1]); + "%" FMT_SOCKET_T " %" FMT_SOCKET_T "\n", + source, line, socket_vector[0], socket_vector[1]); return res; } @@ -372,7 +372,7 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, curl_socket_t sockfd = accept(s, addr, addrlen); if(source && (sockfd != CURL_SOCKET_BAD)) - curl_dbg_log("FD %s:%d accept() = %" CURL_FORMAT_SOCKET_T "\n", + curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", source, line, sockfd); return sockfd; @@ -382,7 +382,7 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) { if(source) - curl_dbg_log("FD %s:%d sclose(%" CURL_FORMAT_SOCKET_T ")\n", + curl_dbg_log("FD %s:%d sclose(%" FMT_SOCKET_T ")\n", source, line, sockfd); } diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h index 51147cdcb..cabadbcc8 100644 --- a/Utilities/cmcurl/lib/memdebug.h +++ b/Utilities/cmcurl/lib/memdebug.h @@ -34,9 +34,9 @@ #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))) +# 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) @@ -114,11 +114,17 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); /* Set this symbol on the command-line, recompile all lib-sources */ #undef strdup #define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) +#undef malloc #define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) +#undef calloc #define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__) +#undef realloc #define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__) +#undef free #define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__) +#undef send #define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) +#undef recv #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) #ifdef _WIN32 @@ -137,13 +143,14 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); #undef socket #define socket(domain,type,protocol)\ - curl_dbg_socket(domain, type, protocol, __LINE__, __FILE__) + curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) #undef accept /* for those with accept as a macro */ #define accept(sock,addr,len)\ curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) #ifdef HAVE_SOCKETPAIR #define socketpair(domain,type,protocol,socket_vector)\ - curl_dbg_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__) + curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ + __LINE__, __FILE__) #endif #ifdef HAVE_GETADDRINFO diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index a2356c473..885eca62f 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -92,7 +92,7 @@ static const char base64enc[] = /* Quoted-printable character class table. * * We cannot rely on ctype functions since quoted-printable input data - * is assumed to be ascii-compatible, even on non-ascii platforms. */ + * is assumed to be ASCII-compatible, even on non-ASCII platforms. */ #define QP_OK 1 /* Can be represented by itself. */ #define QP_SP 2 /* Space or tab. */ #define QP_CR 3 /* Carriage return. */ @@ -557,7 +557,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, /* On all platforms, input is supposed to be ASCII compatible: for this reason, we use hexadecimal ASCII codes in this function rather than - character constants that can be interpreted as non-ascii on some + character constants that can be interpreted as non-ASCII on some platforms. Preserve ASCII encoding on output too. */ while(st->bufbeg < st->bufend) { size_t len = 1; @@ -1137,7 +1137,7 @@ static void cleanup_part_content(curl_mimepart *part) part->datasize = (curl_off_t) 0; /* No size yet. */ cleanup_encoder_state(&part->encstate); part->kind = MIMEKIND_NONE; - part->flags &= ~MIME_FAST_READ; + part->flags &= ~(unsigned int)MIME_FAST_READ; part->lastreadstatus = 1; /* Successful read status. */ part->state.state = MIMESTATE_BEGIN; } @@ -1147,7 +1147,7 @@ static void mime_subparts_free(void *ptr) curl_mime *mime = (curl_mime *) ptr; if(mime && mime->parent) { - mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ + mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ } curl_mime_free(mime); @@ -1159,7 +1159,7 @@ static void mime_subparts_unbind(void *ptr) curl_mime *mime = (curl_mime *) ptr; if(mime && mime->parent) { - mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ + mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ mime->parent = NULL; } @@ -1186,7 +1186,7 @@ void curl_mime_free(curl_mime *mime) curl_mimepart *part; if(mime) { - mime_subparts_unbind(mime); /* Be sure it's not referenced anymore. */ + mime_subparts_unbind(mime); /* Be sure it is not referenced anymore. */ while(mime->firstpart) { part = mime->firstpart; mime->firstpart = part->nextpart; @@ -1354,7 +1354,7 @@ CURLcode curl_mime_name(curl_mimepart *part, const char *name) return CURLE_OK; } -/* Set mime part remote file name. */ +/* Set mime part remote filename. */ CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) { if(!part) @@ -1497,7 +1497,7 @@ CURLcode curl_mime_headers(curl_mimepart *part, if(part->flags & MIME_USERHEADERS_OWNER) { if(part->userheaders != headers) /* Allow setting twice the same list. */ curl_slist_free_all(part->userheaders); - part->flags &= ~MIME_USERHEADERS_OWNER; + part->flags &= ~(unsigned int)MIME_USERHEADERS_OWNER; } part->userheaders = headers; if(headers && take_ownership) @@ -1554,7 +1554,7 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, while(root->parent && root->parent->parent) root = root->parent->parent; if(subparts == root) { - /* Can't add as a subpart of itself. */ + /* cannot add as a subpart of itself. */ return CURLE_BAD_FUNCTION_ARGUMENT; } } @@ -1587,6 +1587,8 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream) (void) size; /* Always 1. */ + /* TODO: this loop is broken. If `nitems` is <= 4, some encoders will + * return STOP_FILLING without adding any data and this loops infinitely. */ do { hasread = FALSE; ret = readback_part(part, buffer, nitems, &hasread); @@ -1662,7 +1664,8 @@ static curl_off_t mime_size(curl_mimepart *part) if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) { /* Compute total part size. */ size += slist_size(part->curlheaders, 2, NULL, 0); - size += slist_size(part->userheaders, 2, STRCONST("Content-Type")); + size += slist_size(part->userheaders, 2, + STRCONST("Content-Type")); size += 2; /* CRLF after headers. */ } return size; @@ -1677,7 +1680,7 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) va_list ap; va_start(ap, fmt); - s = curl_mvaprintf(fmt, ap); + s = vaprintf(fmt, ap); va_end(ap); if(s) { @@ -1770,7 +1773,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, curl_slist_free_all(part->curlheaders); part->curlheaders = NULL; - /* Be sure we won't access old headers later. */ + /* Be sure we will not access old headers later. */ if(part->state.state == MIMESTATE_CURLHEADERS) mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL); @@ -1943,13 +1946,17 @@ static CURLcode cr_mime_read(struct Curl_easy *data, struct cr_mime_ctx *ctx = reader->ctx; size_t nread; + /* Once we have errored, we will return the same error forever */ if(ctx->errored) { + CURL_TRC_READ(data, "cr_mime_read(len=%zu) is errored -> %d, eos=0", + blen, ctx->error_result); *pnread = 0; *peos = FALSE; return ctx->error_result; } if(ctx->seen_eos) { + CURL_TRC_READ(data, "cr_mime_read(len=%zu) seen eos -> 0, eos=1", blen); *pnread = 0; *peos = TRUE; return CURLE_OK; @@ -1962,16 +1969,27 @@ static CURLcode cr_mime_read(struct Curl_easy *data, else if(remain < (curl_off_t)blen) blen = (size_t)remain; } - nread = 0; - if(blen) { - nread = Curl_mime_read(buf, 1, blen, ctx->part); + + if(blen <= 4) { + /* TODO: Curl_mime_read() may go into an infinite loop when reading + * such small lengths. Returning 0 bytes read is a fix that only works + * as request upload buffers will get flushed eventually and larger + * reads will happen again. */ + CURL_TRC_READ(data, "cr_mime_read(len=%zu), too small, return", blen); + *pnread = 0; + *peos = FALSE; + goto out; } + nread = Curl_mime_read(buf, 1, blen, ctx->part); + CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd", + blen, nread); + switch(nread) { case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client mime read EOF fail, " - "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T + "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); return CURLE_READ_ERROR; } @@ -1990,11 +2008,21 @@ static CURLcode cr_mime_read(struct Curl_easy *data, case CURL_READFUNC_PAUSE: /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + CURL_TRC_READ(data, "cr_mime_read(len=%zu), paused by callback", blen); data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ *pnread = 0; *peos = FALSE; break; /* nothing was read */ + case STOP_FILLING: + case READ_ERROR: + failf(data, "read error getting mime data"); + *pnread = 0; + *peos = FALSE; + ctx->errored = TRUE; + ctx->error_result = CURLE_READ_ERROR; + return CURLE_READ_ERROR; + default: if(nread > blen) { /* the read function returned a too large value */ @@ -2012,9 +2040,11 @@ static CURLcode cr_mime_read(struct Curl_easy *data, *peos = ctx->seen_eos; break; } - DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T - ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d", - blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos)); + +out: + CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T + ", read=%"FMT_OFF_T") -> %d, %zu, %d", + blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos); return CURLE_OK; } @@ -2056,7 +2086,7 @@ static CURLcode cr_mime_resume_from(struct Curl_easy *data, if((nread == 0) || (nread > readthisamountnow)) { /* this checks for greater-than only to make sure that the CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T + failf(data, "Could only read %" FMT_OFF_T " bytes from the mime post", passed); return CURLE_READ_ERROR; } @@ -2071,7 +2101,7 @@ static CURLcode cr_mime_resume_from(struct Curl_easy *data, return CURLE_PARTIAL_FILE; } } - /* we've passed, proceed as normal */ + /* we have passed, proceed as normal */ } return CURLE_OK; } @@ -2095,6 +2125,14 @@ static CURLcode cr_mime_unpause(struct Curl_easy *data, return CURLE_OK; } +static bool cr_mime_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_mime_ctx *ctx = reader->ctx; + (void)data; + return (ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE); +} + static const struct Curl_crtype cr_mime = { "cr-mime", cr_mime_init, @@ -2105,6 +2143,7 @@ static const struct Curl_crtype cr_mime = { cr_mime_resume_from, cr_mime_rewind, cr_mime_unpause, + cr_mime_is_paused, Curl_creader_def_done, sizeof(struct cr_mime_ctx) }; diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h index 954b3ccf3..5073a38f7 100644 --- a/Utilities/cmcurl/lib/mime.h +++ b/Utilities/cmcurl/lib/mime.h @@ -112,7 +112,7 @@ struct curl_mimepart { curl_mimepart *nextpart; /* Forward linked list. */ enum mimekind kind; /* The part kind. */ unsigned int flags; /* Flags. */ - char *data; /* Memory data or file name. */ + char *data; /* Memory data or filename. */ curl_read_callback readfunc; /* Read function. */ curl_seek_callback seekfunc; /* Seek function. */ curl_free_callback freefunc; /* Argument free function. */ @@ -121,7 +121,7 @@ struct curl_mimepart { struct curl_slist *curlheaders; /* Part headers. */ struct curl_slist *userheaders; /* Part headers. */ char *mimetype; /* Part mime type. */ - char *filename; /* Remote file name. */ + char *filename; /* Remote filename. */ char *name; /* Data name. */ curl_off_t datasize; /* Expected data size. */ struct mime_state state; /* Current readback state. */ diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index 1829abc73..42993c717 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c @@ -25,7 +25,6 @@ #include "curl_setup.h" #include "dynbuf.h" #include "curl_printf.h" -#include #include "curl_memory.h" /* The last #include file should be: */ @@ -863,7 +862,7 @@ number: str = (char *)iptr->val.str; if(!str) { - /* Write null string if there's space. */ + /* Write null string if there is space. */ if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) { str = nilstr; len = sizeof(nilstr) - 1; @@ -1040,7 +1039,7 @@ static int addbyter(unsigned char outc, void *f) { struct nsprintf *infop = f; if(infop->length < infop->max) { - /* only do this if we haven't reached max length yet */ + /* only do this if we have not reached max length yet */ *infop->buffer++ = (char)outc; /* store */ infop->length++; /* we are now one byte larger */ return 0; /* fputc() returns like this on success */ @@ -1062,10 +1061,10 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, if(info.max) { /* we terminate this with a zero byte */ if(info.max == info.length) { - /* we're at maximum, scrap the last letter */ + /* we are at maximum, scrap the last letter */ info.buffer[-1] = 0; DEBUGASSERT(retcode); - retcode--; /* don't count the nul byte */ + retcode--; /* do not count the nul byte */ } else info.buffer[0] = 0; diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 4ca24eb41..22d354a5c 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -121,7 +121,7 @@ static CURLcode mqtt_send(struct Curl_easy *data, CURLcode result = CURLE_OK; struct MQTT *mq = data->req.p.mqtt; size_t n; - result = Curl_xfer_send(data, buf, len, &n); + result = Curl_xfer_send(data, buf, len, FALSE, &n); if(result) return result; Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); @@ -154,15 +154,15 @@ static int mqtt_getsock(struct Curl_easy *data, static int mqtt_encode_len(char *buf, size_t len) { - unsigned char encoded; int i; for(i = 0; (len > 0) && (i<4); i++) { + unsigned char encoded; encoded = len % 0x80; len /= 0x80; if(len) encoded |= 0x80; - buf[i] = encoded; + buf[i] = (char)encoded; } return i; @@ -312,7 +312,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) start_user = pos + 3 + MQTT_CLIENTID_LEN; /* position where starts the password payload */ start_pwd = start_user + ulen; - /* if user name was provided, add it to the packet */ + /* if username was provided, add it to the packet */ if(ulen) { start_pwd += 2; @@ -585,7 +585,7 @@ static size_t mqtt_decode_len(unsigned char *buf, return len; } -#ifdef CURLDEBUG +#ifdef DEBUGBUILD static const char *statenames[]={ "MQTT_FIRST", "MQTT_REMAINING_LENGTH", @@ -606,7 +606,7 @@ static void mqstate(struct Curl_easy *data, { struct connectdata *conn = data->conn; struct mqtt_conn *mqtt = &conn->proto.mqtt; -#ifdef CURLDEBUG +#ifdef DEBUGBUILD infof(data, "%s (from %s) (next is %s)", statenames[state], statenames[mqtt->state], @@ -743,7 +743,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) struct mqtt_conn *mqtt = &conn->proto.mqtt; struct MQTT *mq = data->req.p.mqtt; ssize_t nread; - unsigned char byte; + unsigned char recvbyte; *done = FALSE; @@ -776,13 +776,13 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) FALLTHROUGH(); case MQTT_REMAINING_LENGTH: do { - result = Curl_xfer_recv(data, (char *)&byte, 1, &nread); + result = Curl_xfer_recv(data, (char *)&recvbyte, 1, &nread); if(result || !nread) break; - Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1); - mq->pkt_hd[mq->npacket++] = byte; - } while((byte & 0x80) && (mq->npacket < 4)); - if(!result && nread && (byte & 0x80)) + Curl_debug(data, CURLINFO_HEADER_IN, (char *)&recvbyte, 1); + mq->pkt_hd[mq->npacket++] = recvbyte; + } while((recvbyte & 0x80) && (mq->npacket < 4)); + if(!result && nread && (recvbyte & 0x80)) /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 + 127 * 128^3 bytes. server tried to send more */ result = CURLE_WEIRD_SERVER_REPLY; diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 6bbdfe267..78e5c0a1e 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -57,7 +57,7 @@ /* 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 + to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every CURL handle takes 45-50 K memory, therefore this 3K are not significant. */ #ifndef CURL_SOCKET_HASH_TABLE_SIZE @@ -94,9 +94,12 @@ static CURLMcode add_next_timeout(struct curltime now, struct Curl_multi *multi, struct Curl_easy *d); static CURLMcode multi_timeout(struct Curl_multi *multi, + struct curltime *expire_time, long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); +static void Curl_expire_ex(struct Curl_easy *data, const struct curltime *nowp, + timediff_t milli, expire_id id); #ifdef DEBUGBUILD static const char * const multi_statename[]={ @@ -135,7 +138,7 @@ static void init_completed(struct Curl_easy *data) { /* this is a completed transfer */ - /* Important: reset the conn pointer so that we don't point to memory + /* Important: reset the conn pointer so that we do not point to memory that could be freed anytime */ Curl_detach_connection(data); Curl_expire_clear(data); /* stop all timers */ @@ -175,7 +178,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state #endif if(oldstate == state) - /* don't bother when the new state is the same as the old state */ + /* do not bother when the new state is the same as the old state */ return; data->mstate = state; @@ -191,7 +194,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state #endif if(state == MSTATE_COMPLETED) { - /* changing to COMPLETED means there's one less easy handle 'alive' */ + /* changing to COMPLETED means there is one less easy handle 'alive' */ DEBUGASSERT(data->multi->num_alive > 0); data->multi->num_alive--; if(!data->multi->num_alive) { @@ -247,10 +250,8 @@ static size_t trhash(void *key, size_t key_length, size_t slots_num) static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) { - (void)k1_len; (void)k2_len; - - return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2; + return !memcmp(k1, k2, k1_len); } static void trhash_dtor(void *nada) @@ -343,7 +344,7 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num) curl_socket_t fd = *((curl_socket_t *) key); (void) key_length; - return (fd % slots_num); + return (fd % (curl_socket_t)slots_num); } /* @@ -354,12 +355,12 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num) * "Some tests at 7000 and 9000 connections showed that the socket hash lookup * is somewhat of a bottle neck. Its current implementation may be a bit too * limiting. It simply has a fixed-size array, and on each entry in the array - * it has a linked list with entries. So the hash only checks which list to - * scan through. The code I had used so for used a list with merely 7 slots - * (as that is what the DNS hash uses) but with 7000 connections that would - * make an average of 1000 nodes in each list to run through. I upped that to - * 97 slots (I believe a prime is suitable) and noticed a significant speed - * increase. I need to reconsider the hash implementation or use a rather + * it has a linked list with entries. The hash only checks which list to scan + * through. The code I had used so for used a list with merely 7 slots (as + * that is what the DNS hash uses) but with 7000 connections that would make + * an average of 1000 nodes in each list to run through. I upped that to 97 + * slots (I believe a prime is suitable) and noticed a significant speed + * increase. I need to reconsider the hash implementation or use a rather * large default value like this. At 9000 connections I was still below 10us * per call." * @@ -370,6 +371,17 @@ static void sh_init(struct Curl_hash *hash, size_t hashsize) sh_freeentry); } +/* multi->proto_hash destructor. Should never be called as elements + * MUST be added with their own destructor */ +static void ph_freeentry(void *p) +{ + (void)p; + /* Will always be FALSE. Cannot use a 0 assert here since compilers + * are not in agreement if they then want a NORETURN attribute or + * not. *sigh* */ + DEBUGASSERT(p == NULL); +} + /* * multi_addmsg() * @@ -396,15 +408,21 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ sh_init(&multi->sockhash, hashsize); - if(Curl_conncache_init(&multi->conn_cache, chashsize)) + Curl_hash_init(&multi->proto_hash, 23, + Curl_hash_str, Curl_str_key_compare, ph_freeentry); + + if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect, + multi, NULL, chashsize)) goto error; Curl_llist_init(&multi->msglist, NULL); + Curl_llist_init(&multi->process, NULL); Curl_llist_init(&multi->pending, NULL); Curl_llist_init(&multi->msgsent, NULL); multi->multiplexing = TRUE; multi->max_concurrent_streams = 100; + multi->last_timeout_ms = -1; #ifdef USE_WINSOCK multi->wsa_event = WSACreateEvent(); @@ -412,14 +430,7 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ goto error; #else #ifdef ENABLE_WAKEUP - 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) { - wakeup_close(multi->wakeup_pair[0]); - wakeup_close(multi->wakeup_pair[1]); + if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { multi->wakeup_pair[0] = CURL_SOCKET_BAD; multi->wakeup_pair[1] = CURL_SOCKET_BAD; } @@ -431,8 +442,9 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ error: sockhash_destroy(&multi->sockhash); + Curl_hash_destroy(&multi->proto_hash); Curl_hash_destroy(&multi->hostcache); - Curl_conncache_destroy(&multi->conn_cache); + Curl_cpool_destroy(&multi->cpool); free(multi); return NULL; } @@ -458,52 +470,6 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) #define multi_warn_debug(x,y) Curl_nop_stmt #endif -/* returns TRUE if the easy handle is supposed to be present in the main link - list */ -static bool in_main_list(struct Curl_easy *data) -{ - return ((data->mstate != MSTATE_PENDING) && - (data->mstate != MSTATE_MSGSENT)); -} - -static void link_easy(struct Curl_multi *multi, - struct Curl_easy *data) -{ - /* We add the new easy entry last in the list. */ - data->next = NULL; /* end of the line */ - if(multi->easyp) { - struct Curl_easy *last = multi->easylp; - last->next = data; - data->prev = last; - multi->easylp = data; /* the new last node */ - } - else { - /* first node, make prev NULL! */ - data->prev = NULL; - multi->easylp = multi->easyp = data; /* both first and last */ - } -} - -/* unlink the given easy handle from the linked list of easy handles */ -static void unlink_easy(struct Curl_multi *multi, - struct Curl_easy *data) -{ - /* make the previous node point to our next */ - if(data->prev) - data->prev->next = data->next; - else - multi->easyp = data->next; /* point to first node */ - - /* make our next point to our previous node */ - if(data->next) - data->next->prev = data->prev; - else - multi->easylp = data->prev; /* point to last node */ - - data->prev = data->next = NULL; -} - - CURLMcode curl_multi_add_handle(struct Curl_multi *multi, struct Curl_easy *data) { @@ -544,10 +510,10 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, Curl_llist_init(&data->state.timeoutlist, NULL); /* - * No failure allowed in this function beyond this point. And no - * modification of easy nor multi handle allowed before this except for - * potential multi's connection cache growing which won't be undone in this - * function no matter what. + * No failure allowed in this function beyond this point. No modification of + * easy nor multi handle allowed before this except for potential multi's + * connection pool growing which will not be undone in this function no + * matter what. */ if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; @@ -566,21 +532,11 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, happen. */ Curl_expire(data, 0, EXPIRE_RUN_NOW); - /* A somewhat crude work-around for a little glitch in Curl_update_timer() - that happens if the lastcall time is set to the same time when the handle - is removed as when the next handle is added, as then the check in - Curl_update_timer() that prevents calling the application multiple times - with the same timer info will not trigger and then the new handle's - timeout will not be notified to the app. - - The work-around is thus simply to clear the 'lastcall' variable to force - Curl_update_timer() to always trigger a callback to the app when a new - easy handle is added */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - rc = Curl_update_timer(multi); - if(rc) + if(rc) { + data->multi = NULL; /* not anymore */ return rc; + } /* set the easy handle */ multistate(data, MSTATE_INIT); @@ -593,13 +549,6 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->dns.hostcachetype = HCACHE_MULTI; } - /* Point to the shared or multi handle connection cache */ - if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) - data->state.conn_cache = &data->share->conn_cache; - else - data->state.conn_cache = &multi->conn_cache; - data->state.lastconnect_id = -1; - #ifdef USE_LIBPSL /* Do the same for PSL. */ if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL))) @@ -608,7 +557,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, data->psl = &multi->psl; #endif - link_easy(multi, data); + /* add the easy handle to the process list */ + Curl_llist_append(&multi->process, data, &data->multi_queue); /* increase the node-counter */ multi->num_easy++; @@ -616,21 +566,12 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, /* increase the alive-counter */ multi->num_alive++; - CONNCACHE_LOCK(data); - /* The closure handle only ever has default timeouts set. To improve the - state somewhat we clone the timeouts from each added handle so that the - closure handle always has the same timeouts as the most recently added - easy handle. */ - data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; - data->state.conn_cache->closure_handle->set.server_response_timeout = - data->set.server_response_timeout; - data->state.conn_cache->closure_handle->set.no_signal = - data->set.no_signal; - data->id = data->state.conn_cache->next_easy_id++; - if(data->state.conn_cache->next_easy_id <= 0) - data->state.conn_cache->next_easy_id = 0; - CONNCACHE_UNLOCK(data); + /* the identifier inside the multi instance */ + data->mid = multi->next_easy_mid++; + if(multi->next_easy_mid <= 0) + multi->next_easy_mid = 0; + Curl_cpool_xfer_init(data); multi_warn_debug(multi, data); return CURLM_OK; @@ -652,6 +593,91 @@ static void debug_print_sock_hash(void *p) } #endif +struct multi_done_ctx { + BIT(premature); +}; + +static void multi_done_locked(struct connectdata *conn, + struct Curl_easy *data, + void *userdata) +{ + struct multi_done_ctx *mdctx = userdata; + + Curl_detach_connection(data); + + if(CONN_INUSE(conn)) { + /* Stop if still used. */ + DEBUGF(infof(data, "Connection still in use %zu, " + "no more multi_done now!", + Curl_llist_count(&conn->easyq))); + return; + } + + data->state.done = TRUE; /* called just now! */ + data->state.recent_conn_id = conn->connection_id; + + if(conn->dns_entry) + Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */ + Curl_hostcache_prune(data); + + /* if data->set.reuse_forbid is TRUE, it means the libcurl client has + forced us to close this connection. This is ignored for requests taking + place in a NTLM/NEGOTIATE authentication handshake + + if conn->bits.close is TRUE, it means that the connection should be + closed in spite of all our efforts to be nice, due to protocol + restrictions in our or the server's end + + if premature is TRUE, it means this connection was said to be DONE before + the entire request operation is complete and thus we cannot know in what + state it is for reusing, so we are forced to close it. In a perfect world + we can add code that keep track of if we really must close it here or not, + but currently we have no such detail knowledge. + */ + + if((data->set.reuse_forbid +#if defined(USE_NTLM) + && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || + conn->proxy_ntlm_state == NTLMSTATE_TYPE2) +#endif +#if defined(USE_SPNEGO) + && !(conn->http_negotiate_state == GSS_AUTHRECV || + conn->proxy_negotiate_state == GSS_AUTHRECV) +#endif + ) || conn->bits.close + || (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { + DEBUGF(infof(data, "multi_done, not reusing connection=%" + FMT_OFF_T ", forbid=%d" + ", close=%d, premature=%d, conn_multiplex=%d", + conn->connection_id, data->set.reuse_forbid, + conn->bits.close, mdctx->premature, + Curl_conn_is_multiplex(conn, FIRSTSOCKET))); + connclose(conn, "disconnecting"); + Curl_cpool_disconnect(data, conn, mdctx->premature); + } + else { + /* the connection is no longer in use by any transfer */ + if(Curl_cpool_conn_now_idle(data, conn)) { + /* connection kept in the cpool */ + const char *host = +#ifndef CURL_DISABLE_PROXY + conn->bits.socksproxy ? + conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : +#endif + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname; + data->state.lastconnect_id = conn->connection_id; + infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", + conn->connection_id, host); + } + else { + /* connection was removed from the cpool and destroyed. */ + data->state.lastconnect_id = -1; + } + } +} + static CURLcode multi_done(struct Curl_easy *data, CURLcode status, /* an error if this is called after an error was detected */ @@ -659,6 +685,9 @@ static CURLcode multi_done(struct Curl_easy *data, { CURLcode result, r2; struct connectdata *conn = data->conn; + struct multi_done_ctx mdctx; + + memset(&mdctx, 0, sizeof(mdctx)); #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d", @@ -684,8 +713,8 @@ static CURLcode multi_done(struct Curl_easy *data, case CURLE_ABORTED_BY_CALLBACK: case CURLE_READ_ERROR: case CURLE_WRITE_ERROR: - /* When we're aborted due to a callback return code it basically have to - be counted as premature as there is trouble ahead if we don't. We have + /* When we are aborted due to a callback return code it basically have to + be counted as premature as there is trouble ahead if we do not. We have many callbacks and protocols work differently, we could potentially do this more fine-grained in the future. */ premature = TRUE; @@ -721,106 +750,22 @@ static CURLcode multi_done(struct Curl_easy *data, if(!result) result = Curl_req_done(&data->req, data, premature); - CONNCACHE_LOCK(data); - Curl_detach_connection(data); - if(CONN_INUSE(conn)) { - /* Stop if still used. */ - CONNCACHE_UNLOCK(data); - DEBUGF(infof(data, "Connection still in use %zu, " - "no more multi_done now!", - conn->easyq.size)); - return CURLE_OK; - } - - data->state.done = TRUE; /* called just now! */ - - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ - conn->dns_entry = NULL; - } - Curl_hostcache_prune(data); - - /* if data->set.reuse_forbid is TRUE, it means the libcurl client has - forced us to close this connection. This is ignored for requests taking - place in a NTLM/NEGOTIATE authentication handshake - - if conn->bits.close is TRUE, it means that the connection should be - closed in spite of all our efforts to be nice, due to protocol - restrictions in our or the server's end - - if premature is TRUE, it means this connection was said to be DONE before - the entire request operation is complete and thus we can't know in what - state it is for reusing, so we're forced to close it. In a perfect world - we can add code that keep track of if we really must close it here or not, - but currently we have no such detail knowledge. - */ - - data->state.recent_conn_id = conn->connection_id; - if((data->set.reuse_forbid -#if defined(USE_NTLM) - && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || - conn->proxy_ntlm_state == NTLMSTATE_TYPE2) -#endif -#if defined(USE_SPNEGO) - && !(conn->http_negotiate_state == GSS_AUTHRECV || - conn->proxy_negotiate_state == GSS_AUTHRECV) -#endif - ) || conn->bits.close - || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { - DEBUGF(infof(data, "multi_done, not reusing connection=%" - CURL_FORMAT_CURL_OFF_T ", forbid=%d" - ", close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, - data->set.reuse_forbid, conn->bits.close, premature, - Curl_conn_is_multiplex(conn, FIRSTSOCKET))); - connclose(conn, "disconnecting"); - Curl_conncache_remove_conn(data, conn, FALSE); - CONNCACHE_UNLOCK(data); - Curl_disconnect(data, conn, premature); - } - else { - char buffer[256]; - const char *host = -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? - conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname; - /* create string before returning the connection */ - curl_off_t connection_id = conn->connection_id; - msnprintf(buffer, sizeof(buffer), - "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact", - connection_id, host); - /* the connection is no longer in use by this transfer */ - CONNCACHE_UNLOCK(data); - if(Curl_conncache_return_conn(data, conn)) { - /* remember the most recently used connection */ - data->state.lastconnect_id = connection_id; - data->state.recent_conn_id = connection_id; - infof(data, "%s", buffer); - } - else - data->state.lastconnect_id = -1; - } + /* Under the potential connection pool's share lock, decide what to + * do with the transfer's connection. */ + mdctx.premature = premature; + Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); return result; } -static int close_connect_only(struct Curl_easy *data, - struct connectdata *conn, void *param) +static void close_connect_only(struct connectdata *conn, + struct Curl_easy *data, + void *userdata) { - (void)param; - if(data->state.lastconnect_id != conn->connection_id) - return 0; - - if(!conn->connect_only) - return 1; - - connclose(conn, "Removing connect-only easy handle"); - - return 1; + (void)userdata; + (void)data; + if(conn->connect_only) + connclose(conn, "Removing connect-only easy handle"); } CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, @@ -828,15 +773,16 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, { struct Curl_easy *easy = data; bool premature; - struct Curl_llist_element *e; + struct Curl_llist_node *e; CURLMcode rc; + bool removed_timer = FALSE; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) + if(!GOOD_EASY_HANDLE(data) || !multi->num_easy) return CURLM_BAD_EASY_HANDLE; /* Prevent users from trying to remove same easy handle more than once */ @@ -863,7 +809,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, if(data->conn && data->mstate > MSTATE_DO && data->mstate < MSTATE_COMPLETED) { - /* Set connection owner so that the DONE function closes it. We can + /* Set connection owner so that the DONE function closes it. We can safely do this here since connection is killed. */ streamclose(data->conn, "Removed with partial response"); } @@ -872,7 +818,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* multi_done() clears the association between the easy handle and the connection. - Note that this ignores the return code simply because there's + Note that this ignores the return code simply because there is nothing really useful to do with it anyway! */ (void)multi_done(data, data->result, premature); } @@ -880,18 +826,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* The timer must be shut down before data->multi is set to NULL, else the timenode will remain in the splay tree after curl_easy_cleanup is called. Do it after multi_done() in case that sets another time! */ - Curl_expire_clear(data); + removed_timer = Curl_expire_clear(data); - if(data->connect_queue.ptr) { - /* the handle is in the pending or msgsent lists, so go ahead and remove - it */ - if(data->mstate == MSTATE_PENDING) - Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); - else - Curl_llist_remove(&multi->msgsent, &data->connect_queue, NULL); - } - if(in_main_list(data)) - unlink_easy(multi, data); + /* the handle is in a list, remove it from whichever it is */ + Curl_node_remove(&data->multi_queue); if(data->dns.hostcachetype == HCACHE_MULTI) { /* stop using the multi handle's DNS cache, *after* the possible @@ -906,7 +844,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, what we want */ data->mstate = MSTATE_COMPLETED; - /* This ignores the return code even in case of problems because there's + /* This ignores the return code even in case of problems because there is nothing more to do about that, here */ (void)singlesocket(multi, easy); /* to let the application know what sockets that vanish with this handle */ @@ -918,7 +856,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* 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 + forbidden from reuse. This easy handle cannot find the connection anymore once removed from the multi handle Better close the connection here, at once. @@ -927,15 +865,14 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, 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); + Curl_cpool_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, - NULL, close_connect_only); + Curl_cpool_do_by_id(data, data->state.lastconnect_id, + close_connect_only, NULL); } #ifdef USE_LIBPSL @@ -944,33 +881,31 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, data->psl = NULL; #endif - /* as this was using a shared connection cache we clear the pointer to that - since we're not part of that multi handle anymore */ - data->state.conn_cache = NULL; - - data->multi = NULL; /* clear the association to this multi handle */ - - /* make sure there's no pending message in the queue sent from this easy + /* make sure there is no pending message in the queue sent from this easy handle */ - for(e = multi->msglist.head; e; e = e->next) { - struct Curl_message *msg = e->ptr; + for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) { + struct Curl_message *msg = Curl_node_elem(e); if(msg->extmsg.easy_handle == easy) { - Curl_llist_remove(&multi->msglist, e, NULL); + Curl_node_remove(e); /* there can only be one from this specific handle */ break; } } + data->multi = NULL; /* clear the association to this multi handle */ + data->mid = -1; + /* NOTE NOTE NOTE We do not touch the easy handle here! */ multi->num_easy--; /* one less to care about now */ - process_pending_handles(multi); - rc = Curl_update_timer(multi); - if(rc) - return rc; + if(removed_timer) { + rc = Curl_update_timer(multi); + if(rc) + return rc; + } return CURLM_OK; } @@ -991,7 +926,7 @@ void Curl_detach_connection(struct Curl_easy *data) struct connectdata *conn = data->conn; if(conn) { Curl_conn_ev_data_detach(conn, data); - Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); + Curl_node_remove(&data->conn_queue); } data->conn = NULL; } @@ -1002,8 +937,9 @@ void Curl_detach_connection(struct Curl_easy *data) * This is the only function that should assign data->conn */ void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn) + struct connectdata *conn) { + DEBUGASSERT(data); DEBUGASSERT(!data->conn); DEBUGASSERT(conn); data->conn = conn; @@ -1016,12 +952,14 @@ void Curl_attach_connection(struct Curl_easy *data, static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) { struct connectdata *conn = data->conn; - (void)socks; - /* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes - * that *after* the connect. */ - if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) { + curl_socket_t sockfd; + + if(!conn) + return GETSOCK_BLANK; + sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { /* Default is to wait to something from the server */ - socks[0] = conn->sock[FIRSTSOCKET]; + socks[0] = sockfd; return GETSOCK_READSOCK(0); } return GETSOCK_BLANK; @@ -1030,11 +968,16 @@ static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks) { struct connectdata *conn = data->conn; - if(conn && conn->handler->proto_getsock) + curl_socket_t sockfd; + + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->proto_getsock) return conn->handler->proto_getsock(data, conn, socks); - else if(conn && conn->sockfd != CURL_SOCKET_BAD) { + sockfd = Curl_conn_get_socket(data, FIRSTSOCKET); + if(sockfd != CURL_SOCKET_BAD) { /* Default is to wait to something from the server */ - socks[0] = conn->sockfd; + socks[0] = sockfd; return GETSOCK_READSOCK(0); } return GETSOCK_BLANK; @@ -1043,9 +986,11 @@ static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks) static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks) { struct connectdata *conn = data->conn; - if(conn && conn->handler->domore_getsock) + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->domore_getsock) return conn->handler->domore_getsock(data, conn, socks); - else if(conn && conn->sockfd != CURL_SOCKET_BAD) { + else if(conn->sockfd != CURL_SOCKET_BAD) { /* Default is that we want to send something to the server */ socks[0] = conn->sockfd; return GETSOCK_WRITESOCK(0); @@ -1056,9 +1001,11 @@ static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks) static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks) { struct connectdata *conn = data->conn; - if(conn && conn->handler->doing_getsock) + if(!conn) + return GETSOCK_BLANK; + if(conn->handler->doing_getsock) return conn->handler->doing_getsock(data, conn, socks); - else if(conn && conn->sockfd != CURL_SOCKET_BAD) { + else if(conn->sockfd != CURL_SOCKET_BAD) { /* Default is that we want to send something to the server */ socks[0] = conn->sockfd; return GETSOCK_WRITESOCK(0); @@ -1069,7 +1016,6 @@ static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks) static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) { struct connectdata *conn = data->conn; - if(!conn) return GETSOCK_BLANK; else if(conn->handler->perform_getsock) @@ -1084,7 +1030,7 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) sock[sockindex] = conn->sockfd; } - if(CURL_WANT_SEND(data)) { + if(Curl_req_want_send(data)) { if((conn->sockfd != conn->writesockfd) || bitmap == GETSOCK_BLANK) { /* only if they are not the same socket and we have a readable @@ -1106,6 +1052,7 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) static void multi_getsock(struct Curl_easy *data, struct easy_pollset *ps) { + bool expect_sockets = TRUE; /* The no connection case can happen when this is called from curl_multi_remove_handle() => singlesocket() => multi_getsock(). */ @@ -1119,11 +1066,14 @@ static void multi_getsock(struct Curl_easy *data, case MSTATE_SETUP: case MSTATE_CONNECT: /* nothing to poll for yet */ + expect_sockets = FALSE; break; case MSTATE_RESOLVING: Curl_pollset_add_socks(data, ps, Curl_resolv_getsock); - /* connection filters are not involved in this phase */ + /* connection filters are not involved in this phase. It's ok if we get no + * sockets to wait for. Resolving can wake up from other sources. */ + expect_sockets = FALSE; break; case MSTATE_CONNECTING: @@ -1157,19 +1107,29 @@ static void multi_getsock(struct Curl_easy *data, case MSTATE_RATELIMITING: /* we need to let time pass, ignore socket(s) */ + expect_sockets = FALSE; break; case MSTATE_DONE: case MSTATE_COMPLETED: case MSTATE_MSGSENT: /* nothing more to poll for */ + expect_sockets = FALSE; break; default: failf(data, "multi_getsock: unexpected multi state %d", data->mstate); DEBUGASSERT(0); + expect_sockets = FALSE; break; } + + if(expect_sockets && !ps->num && + !(data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) && + Curl_conn_is_ip_connected(data, FIRSTSOCKET)) { + infof(data, "WARNING: no socket in pollset, transfer may stall!"); + DEBUGASSERT(0); + } } CURLMcode curl_multi_fdset(struct Curl_multi *multi, @@ -1179,10 +1139,8 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, /* Scan through all the easy handles to get the file descriptors set. Some easy handles may not have connected to the remote host yet, and then we must make sure that is done. */ - struct Curl_easy *data; int this_max_fd = -1; - struct easy_pollset ps; - unsigned int i; + struct Curl_llist_node *e; (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) @@ -1191,20 +1149,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - memset(&ps, 0, sizeof(ps)); - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); + for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); + unsigned int i; + + multi_getsock(data, &data->last_poll); - for(i = 0; i < ps.num; i++) { - if(!FDSET_SOCK(ps.sockets[i])) - /* pretend it doesn't exist */ + for(i = 0; i < data->last_poll.num; i++) { + if(!FDSET_SOCK(data->last_poll.sockets[i])) + /* pretend it does not exist */ continue; - if(ps.actions[i] & CURL_POLL_IN) - FD_SET(ps.sockets[i], read_fd_set); - if(ps.actions[i] & CURL_POLL_OUT) - FD_SET(ps.sockets[i], write_fd_set); - if((int)ps.sockets[i] > this_max_fd) - this_max_fd = (int)ps.sockets[i]; + if(data->last_poll.actions[i] & CURL_POLL_IN) + FD_SET(data->last_poll.sockets[i], read_fd_set); + if(data->last_poll.actions[i] & CURL_POLL_OUT) + FD_SET(data->last_poll.sockets[i], write_fd_set); + if((int)data->last_poll.sockets[i] > this_max_fd) + this_max_fd = (int)data->last_poll.sockets[i]; } } @@ -1218,13 +1178,9 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi, unsigned int size, unsigned int *fd_count) { - struct Curl_easy *data; - unsigned int nfds = 0; - struct easy_pollset ps; - unsigned int i; + struct curl_waitfds cwfds; CURLMcode result = CURLM_OK; - struct curl_waitfd *ufd; - unsigned int j; + struct Curl_llist_node *e; if(!ufds) return CURLM_BAD_FUNCTION_ARGUMENT; @@ -1235,48 +1191,29 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - memset(&ps, 0, sizeof(ps)); - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); - - for(i = 0; i < ps.num; i++) { - if(nfds < size) { - curl_socket_t fd = ps.sockets[i]; - int fd_idx = -1; - - /* Simple linear search to skip an already added descriptor */ - for(j = 0; j < nfds; j++) { - if(ufds[j].fd == fd) { - fd_idx = (int)j; - break; - } - } - - if(fd_idx < 0) { - ufd = &ufds[nfds++]; - ufd->fd = ps.sockets[i]; - ufd->events = 0; - } - else - ufd = &ufds[fd_idx]; - - if(ps.actions[i] & CURL_POLL_IN) - ufd->events |= CURL_WAIT_POLLIN; - if(ps.actions[i] & CURL_POLL_OUT) - ufd->events |= CURL_WAIT_POLLOUT; - } - else - return CURLM_OUT_OF_MEMORY; + Curl_waitfds_init(&cwfds, ufds, size); + for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); + multi_getsock(data, &data->last_poll); + if(Curl_waitfds_add_ps(&cwfds, &data->last_poll)) { + result = CURLM_OUT_OF_MEMORY; + goto out; } } + if(Curl_cpool_add_waitfds(&multi->cpool, &cwfds)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + +out: if(fd_count) - *fd_count = nfds; + *fd_count = cwfds.n; return result; } #ifdef USE_WINSOCK -/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't +/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets cannot * be reset this way because an empty datagram would be sent. #9203 * * "On Windows the internal state of FD_WRITE as returned from @@ -1291,29 +1228,6 @@ static void reset_socket_fdwrite(curl_socket_t s) } #endif -static CURLMcode ufds_increase(struct pollfd **pfds, unsigned int *pfds_len, - unsigned int inc, bool *is_malloced) -{ - struct pollfd *new_fds, *old_fds = *pfds; - unsigned int new_len = *pfds_len + inc; - - new_fds = calloc(new_len, sizeof(struct pollfd)); - if(!new_fds) { - if(*is_malloced) - free(old_fds); - *pfds = NULL; - *pfds_len = 0; - return CURLM_OUT_OF_MEMORY; - } - memcpy(new_fds, old_fds, (*pfds_len) * sizeof(struct pollfd)); - if(*is_malloced) - free(old_fds); - *pfds = new_fds; - *pfds_len = new_len; - *is_malloced = TRUE; - return CURLM_OK; -} - #define NUM_POLLS_ON_STACK 10 static CURLMcode multi_wait(struct Curl_multi *multi, @@ -1324,16 +1238,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi, bool extrawait, /* when no socket, wait */ bool use_wakeup) { - struct Curl_easy *data; - struct easy_pollset ps; size_t i; + struct curltime expire_time; long timeout_internal; int retcode = 0; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; - struct pollfd *ufds = &a_few_on_stack[0]; - unsigned int ufds_len = NUM_POLLS_ON_STACK; - unsigned int nfds = 0, curl_nfds = 0; /* how many ufds are in use */ - bool ufds_malloc = FALSE; + struct curl_pollfds cpfds; + unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */ + CURLMcode result = CURLM_OK; + struct Curl_llist_node *e; + #ifdef USE_WINSOCK WSANETWORKEVENTS wsa_events; DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); @@ -1351,141 +1265,108 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; - /* If the internally desired timeout is actually shorter than requested from - the outside, then use the shorter time! But only if the internal timer - is actually larger than -1! */ - (void)multi_timeout(multi, &timeout_internal); - if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) - timeout_ms = (int)timeout_internal; - - memset(ufds, 0, ufds_len * sizeof(struct pollfd)); - memset(&ps, 0, sizeof(ps)); + Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); /* Add the curl handles to our pollfds first */ - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); + for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); - for(i = 0; i < ps.num; i++) { - short events = 0; -#ifdef USE_WINSOCK - long mask = 0; -#endif - if(ps.actions[i] & CURL_POLL_IN) { -#ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; -#endif - events |= POLLIN; - } - if(ps.actions[i] & CURL_POLL_OUT) { -#ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(ps.sockets[i]); -#endif - events |= POLLOUT; - } - if(events) { - if(nfds && ps.sockets[i] == ufds[nfds-1].fd) { - ufds[nfds-1].events |= events; - } - else { - if(nfds >= ufds_len) { - if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) - return CURLM_OUT_OF_MEMORY; - } - DEBUGASSERT(nfds < ufds_len); - ufds[nfds].fd = ps.sockets[i]; - ufds[nfds].events = events; - ++nfds; - } - } -#ifdef USE_WINSOCK - if(mask) { - if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } - } -#endif + multi_getsock(data, &data->last_poll); + if(Curl_pollfds_add_ps(&cpfds, &data->last_poll)) { + result = CURLM_OUT_OF_MEMORY; + goto out; } } - curl_nfds = nfds; /* what curl internally used in ufds */ + if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { + unsigned short events = 0; + if(extra_fds[i].events & CURL_WAIT_POLLIN) + events |= POLLIN; + if(extra_fds[i].events & CURL_WAIT_POLLPRI) + events |= POLLPRI; + if(extra_fds[i].events & CURL_WAIT_POLLOUT) + events |= POLLOUT; + if(Curl_pollfds_add_sock(&cpfds, extra_fds[i].fd, events)) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } + } + #ifdef USE_WINSOCK + /* Set the WSA events based on the collected pollds */ + for(i = 0; i < cpfds.n; i++) { long mask = 0; - if(extra_fds[i].events & CURL_WAIT_POLLIN) + if(cpfds.pfds[i].events & POLLIN) mask |= FD_READ|FD_ACCEPT|FD_CLOSE; - if(extra_fds[i].events & CURL_WAIT_POLLPRI) + if(cpfds.pfds[i].events & POLLPRI) mask |= FD_OOB; - if(extra_fds[i].events & CURL_WAIT_POLLOUT) { + if(cpfds.pfds[i].events & POLLOUT) { mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(extra_fds[i].fd); - } - if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; + reset_socket_fdwrite(cpfds.pfds[i].fd); } -#endif - if(nfds >= ufds_len) { - if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) - return CURLM_OUT_OF_MEMORY; + if(mask) { + if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { + result = CURLM_OUT_OF_MEMORY; + goto out; + } } - DEBUGASSERT(nfds < ufds_len); - ufds[nfds].fd = extra_fds[i].fd; - ufds[nfds].events = 0; - if(extra_fds[i].events & CURL_WAIT_POLLIN) - ufds[nfds].events |= POLLIN; - if(extra_fds[i].events & CURL_WAIT_POLLPRI) - ufds[nfds].events |= POLLPRI; - if(extra_fds[i].events & CURL_WAIT_POLLOUT) - ufds[nfds].events |= POLLOUT; - ++nfds; } +#endif #ifdef ENABLE_WAKEUP #ifndef USE_WINSOCK if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(nfds >= ufds_len) { - if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc)) - return CURLM_OUT_OF_MEMORY; + if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { + result = CURLM_OUT_OF_MEMORY; + goto out; } - DEBUGASSERT(nfds < ufds_len); - ufds[nfds].fd = multi->wakeup_pair[0]; - ufds[nfds].events = POLLIN; - ++nfds; } #endif #endif + /* We check the internal timeout *AFTER* we collected all sockets to + * poll. Collecting the sockets may install new timers by protocols + * and connection filters. + * Use the shorter one of the internal and the caller requested timeout. */ + (void)multi_timeout(multi, &expire_time, &timeout_internal); + if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) + timeout_ms = (int)timeout_internal; + #if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(nfds || use_wakeup) { + if(cpfds.n || use_wakeup) { #else - if(nfds) { + if(cpfds.n) { #endif int pollrc; #ifdef USE_WINSOCK - if(nfds) - pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */ + if(cpfds.n) /* just pre-check with Winsock */ + pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); else pollrc = 0; #else - pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */ + pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ #endif - if(pollrc < 0) - return CURLM_UNRECOVERABLE_POLL; + if(pollrc < 0) { + result = CURLM_UNRECOVERABLE_POLL; + goto out; + } 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); + WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, + FALSE); } - /* With WinSock, we have to run the following section unconditionally + /* With Winsock, we have to run the following section unconditionally to call WSAEventSelect(fd, event, 0) on all the sockets */ { #endif @@ -1493,7 +1374,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, struct, the bit values of the actual underlying poll() implementation may not be the same as the ones in the public libcurl API! */ for(i = 0; i < extra_nfds; i++) { - unsigned r = ufds[curl_nfds + i].revents; + unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; unsigned short mask = 0; #ifdef USE_WINSOCK curl_socket_t s = extra_fds[i].fd; @@ -1510,7 +1391,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } WSAEventSelect(s, multi->wsa_event, 0); if(!pollrc) { - extra_fds[i].revents = mask; + extra_fds[i].revents = (short)mask; continue; } #endif @@ -1520,25 +1401,25 @@ static CURLMcode multi_wait(struct Curl_multi *multi, mask |= CURL_WAIT_POLLOUT; if(r & POLLPRI) mask |= CURL_WAIT_POLLPRI; - extra_fds[i].revents = mask; + extra_fds[i].revents = (short)mask; } #ifdef USE_WINSOCK /* Count up all our own sockets that had activity, and remove them from the event. */ if(curl_nfds) { + for(e = Curl_llist_head(&multi->process); e && !result; + e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); - for(data = multi->easyp; data; data = data->next) { - multi_getsock(data, &ps); - - for(i = 0; i < ps.num; i++) { + for(i = 0; i < data->last_poll.num; i++) { wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(ps.sockets[i], NULL, + if(WSAEnumNetworkEvents(data->last_poll.sockets[i], NULL, &wsa_events) == 0) { if(ret && !pollrc && wsa_events.lNetworkEvents) retcode++; } - WSAEventSelect(ps.sockets[i], multi->wsa_event, 0); + WSAEventSelect(data->last_poll.sockets[i], multi->wsa_event, 0); } } } @@ -1547,7 +1428,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, #else #ifdef ENABLE_WAKEUP if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(ufds[curl_nfds + extra_nfds].revents & POLLIN) { + if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { char buf[64]; ssize_t nread; while(1) { @@ -1571,18 +1452,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } } - if(ufds_malloc) - free(ufds); if(ret) *ret = retcode; #if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(extrawait && !nfds && !use_wakeup) { + if(extrawait && !cpfds.n && !use_wakeup) { #else - if(extrawait && !nfds) { + if(extrawait && !cpfds.n) { #endif long sleep_ms = 0; - /* Avoid busy-looping when there's nothing particular to wait for */ + /* Avoid busy-looping when there is nothing particular to wait for */ if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { if(sleep_ms > timeout_ms) sleep_ms = timeout_ms; @@ -1594,7 +1473,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi, } } - return CURLM_OK; +out: + Curl_pollfds_cleanup(&cpfds); + return result; } CURLMcode curl_multi_wait(struct Curl_multi *multi, @@ -1623,6 +1504,15 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) it has to be careful only to access parts of the Curl_multi struct that are constant */ +#if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK) +#ifdef USE_EVENTFD + const void *buf; + const uint64_t val = 1; +#else + char buf[1]; +#endif +#endif + /* GOOD_MULTI_HANDLE can be safely called */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -1636,8 +1526,11 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) making it safe to access from another thread after the init part and before cleanup */ if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { - char buf[1]; +#ifdef USE_EVENTFD + buf = &val; +#else buf[0] = 1; +#endif while(1) { /* swrite() is not thread-safe in general, because concurrent calls can have their messages interleaved, but in this case the content @@ -1646,7 +1539,7 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) The write socket is set to non-blocking, this way this function cannot block, making it safe to call even from the same thread that will call curl_multi_wait(). If swrite() returns that it - would block, it's considered successful because it means that + would block, it is considered successful because it means that previous calls to this function will wake up the poll(). */ if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { int err = SOCKERRNO; @@ -1710,7 +1603,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, if(!rc) { struct SingleRequest *k = &data->req; - /* pass in NULL for 'conn' here since we don't want to init the + /* pass in NULL for 'conn' here since we do not want to init the connection, only this transfer */ Curl_init_do(data, NULL); @@ -1742,7 +1635,7 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) * second connection. * * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to - * DOING state there's more work to do! + * DOING state there is more work to do! */ static CURLcode multi_do_more(struct Curl_easy *data, int *complete) @@ -1776,23 +1669,23 @@ static bool multi_handle_timeout(struct Curl_easy *data, else since = data->progress.t_startop; if(data->mstate == MSTATE_RESOLVING) - failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T + failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T " milliseconds", Curl_timediff(*now, since)); else if(data->mstate == MSTATE_CONNECTING) - failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T + failf(data, "Connection timed out after %" FMT_TIMEDIFF_T " milliseconds", Curl_timediff(*now, since)); else { struct SingleRequest *k = &data->req; if(k->size != -1) { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" - CURL_FORMAT_CURL_OFF_T " bytes received", + failf(data, "Operation timed out after %" FMT_TIMEDIFF_T + " milliseconds with %" FMT_OFF_T " out of %" + FMT_OFF_T " bytes received", Curl_timediff(*now, since), k->bytecount, k->size); } else { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T - " bytes received", Curl_timediff(*now, since), k->bytecount); + failf(data, "Operation timed out after %" FMT_TIMEDIFF_T + " milliseconds with %" FMT_OFF_T " bytes received", + Curl_timediff(*now, since), k->bytecount); } } *result = CURLE_OPERATION_TIMEDOUT; @@ -1870,10 +1763,10 @@ static CURLcode protocol_connect(struct Curl_easy *data, && conn->bits.protoconnstart) { /* We already are connected, get back. This may happen when the connect worked fine in the first call, like when we connect to a local server - or proxy. Note that we don't know if the protocol is actually done. + or proxy. Note that we do not know if the protocol is actually done. - Unless this protocol doesn't have any protocol-connect callback, as - then we know we're done. */ + Unless this protocol does not have any protocol-connect callback, as + then we know we are done. */ if(!conn->handler->connecting) *protocol_done = TRUE; @@ -1890,7 +1783,7 @@ static CURLcode protocol_connect(struct Curl_easy *data, else *protocol_done = TRUE; - /* it has started, possibly even completed but that knowledge isn't stored + /* it has started, possibly even completed but that knowledge is not stored in this bit! */ if(!result) conn->bits.protoconnstart = TRUE; @@ -1904,6 +1797,20 @@ static void set_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } +/* + * posttransfer() is called immediately after a transfer ends + */ +static void multi_posttransfer(struct Curl_easy *data) +{ +#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) + /* restore the signal handler for SIGPIPE before we get back */ + if(!data->set.no_signal) + signal(SIGPIPE, data->state.prev_signal); +#else + (void)data; /* unused parameter */ +#endif +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime *nowp, struct Curl_easy *data) @@ -1926,7 +1833,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* a multi-level callback returned error before, meaning every individual transfer now has failed */ result = CURLE_ABORTED_BY_CALLBACK; - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, FALSE); multistate(data, MSTATE_COMPLETED); } @@ -1995,11 +1902,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* There was no connection available. We will go to the pending state and wait for an available connection. */ multistate(data, MSTATE_PENDING); - - /* add this handle to the list of connect-pending handles */ - Curl_llist_append(&multi->pending, data, &data->connect_queue); - /* unlink from the main list */ - unlink_easy(multi, data); + /* unlink from process list */ + Curl_node_remove(&data->multi_queue); + /* add handle to pending list */ + Curl_llist_append(&multi->pending, data, &data->multi_queue); result = CURLE_OK; break; } @@ -2009,7 +1915,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result) { *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); if(async) - /* We're now waiting for an asynchronous name lookup */ + /* We are now waiting for an asynchronous name lookup */ multistate(data, MSTATE_RESOLVING); else { /* after the connect has been sent off, go WAITCONNECT unless the @@ -2017,8 +1923,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, WAITDO or DO! */ rc = CURLM_CALL_MULTI_PERFORM; - if(connected) + if(connected) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } multistate(data, MSTATE_PROTOCONNECT); + } else { multistate(data, MSTATE_CONNECTING); } @@ -2062,7 +1974,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Update sockets here, because the socket(s) may have been closed and the application thus needs to be told, even if it is likely that the same socket(s) will again be used further - down. If the name has not yet been resolved, it is likely + down. If the name has not yet been resolved, it is likely that new sockets have been opened in an attempt to contact another resolver. */ rc = singlesocket(multi, data); @@ -2102,22 +2014,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ DEBUGASSERT(data->conn); result = Curl_http_connect(data, &protocol_connected); -#ifndef CURL_DISABLE_PROXY - if(data->conn->bits.proxy_connect_closed) { + if(!result) { rc = CURLM_CALL_MULTI_PERFORM; - /* connect back to proxy again */ - result = CURLE_OK; - multi_done(data, CURLE_OK, FALSE); - multistate(data, MSTATE_CONNECT); + /* initiate protocol connect phase */ + multistate(data, MSTATE_PROTOCONNECT); } else -#endif - if(!result) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, MSTATE_PROTOCONNECT); - } - else stream_error = TRUE; break; #endif @@ -2127,12 +2029,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, DEBUGASSERT(data->conn); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); if(connected && !result) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } rc = CURLM_CALL_MULTI_PERFORM; multistate(data, MSTATE_PROTOCONNECT); } else if(result) { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; break; @@ -2162,7 +2069,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; } @@ -2178,7 +2085,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else if(result) { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; } @@ -2198,9 +2105,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_set_in_callback(data, false); if(prereq_rc != CURL_PREREQFUNC_OK) { failf(data, "operation aborted by pre-request callback"); - /* failure in pre-request callback - don't do any other processing */ + /* failure in pre-request callback - do not do any other + processing */ result = CURLE_ABORTED_BY_CALLBACK; - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, FALSE); stream_error = TRUE; break; @@ -2230,7 +2138,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* skip some states if it is important */ multi_done(data, CURLE_OK, FALSE); - /* if there's no connection left, skip the DONE state */ + /* if there is no connection left, skip the DONE state */ multistate(data, data->conn ? MSTATE_DONE : MSTATE_COMPLETED); rc = CURLM_CALL_MULTI_PERFORM; @@ -2246,13 +2154,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* after DO, go DO_DONE... or DO_MORE */ else if(data->conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax + /* we are supposed to do more, but we need to sit down, relax and wait a little while first */ multistate(data, MSTATE_DOING_MORE); rc = CURLM_CALL_MULTI_PERFORM; } else { - /* we're done with the DO, now DID */ + /* we are done with the DO, now DID */ multistate(data, MSTATE_DID); rc = CURLM_CALL_MULTI_PERFORM; } @@ -2261,7 +2169,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, data->conn->bits.reuse) { /* * In this situation, a connection that we were trying to use - * may have unexpectedly died. If possible, send the connection + * may have unexpectedly died. If possible, send the connection * back to the CONNECT phase so we can try again. */ char *newurl = NULL; @@ -2275,7 +2183,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, stream_error = TRUE; } - Curl_posttransfer(data); + multi_posttransfer(data); drc = multi_done(data, result, FALSE); /* When set to retry the connection, we must go back to the CONNECT @@ -2295,19 +2203,19 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } else { - /* done didn't return OK or SEND_ERROR */ + /* done did not return OK or SEND_ERROR */ result = drc; } } else { - /* Have error handler disconnect conn if we can't retry */ + /* Have error handler disconnect conn if we cannot retry */ stream_error = TRUE; } free(newurl); } else { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); if(data->conn) multi_done(data, result, FALSE); stream_error = TRUE; @@ -2329,7 +2237,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, FALSE); stream_error = TRUE; } @@ -2355,7 +2263,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* failure detected */ - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, FALSE); stream_error = TRUE; } @@ -2367,7 +2275,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Check if we can move pending requests to send pipe */ process_pending_handles(multi); /* multiplexed */ - /* Only perform the transfer if there's a good socket to work with. + /* Only perform the transfer if there is a good socket to work with. Having both BAD is a signal to skip immediately to DONE */ if((data->conn->sockfd != CURL_SOCKET_BAD) || (data->conn->writesockfd != CURL_SOCKET_BAD)) @@ -2397,31 +2305,29 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, TRUE); } else { send_timeout_ms = 0; if(data->set.max_send_speed) send_timeout_ms = - Curl_pgrsLimitWaitTime(data->progress.uploaded, - data->progress.ul_limit_size, + Curl_pgrsLimitWaitTime(&data->progress.ul, data->set.max_send_speed, - data->progress.ul_limit_start, *nowp); recv_timeout_ms = 0; if(data->set.max_recv_speed) recv_timeout_ms = - Curl_pgrsLimitWaitTime(data->progress.downloaded, - data->progress.dl_limit_size, + Curl_pgrsLimitWaitTime(&data->progress.dl, data->set.max_recv_speed, - data->progress.dl_limit_start, *nowp); if(!send_timeout_ms && !recv_timeout_ms) { multistate(data, MSTATE_PERFORMING); Curl_ratelimit(data, *nowp); + /* start performing again right away */ + rc = CURLM_CALL_MULTI_PERFORM; } else if(send_timeout_ms >= recv_timeout_ms) Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); @@ -2437,19 +2343,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* check if over send speed */ send_timeout_ms = 0; if(data->set.max_send_speed) - send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, - data->progress.ul_limit_size, + send_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.ul, data->set.max_send_speed, - data->progress.ul_limit_start, *nowp); /* check if over recv speed */ recv_timeout_ms = 0; if(data->set.max_recv_speed) - recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, - data->progress.dl_limit_size, + recv_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.dl, data->set.max_recv_speed, - data->progress.dl_limit_start, *nowp); if(send_timeout_ms || recv_timeout_ms) { @@ -2463,7 +2365,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } /* read/write data if it is ready to do so */ - result = Curl_readwrite(data); + result = Curl_sendrecv(data, nowp); if(data->req.done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race @@ -2509,8 +2411,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(result) { /* * The transfer phase returned error, we mark the connection to get - * closed to prevent being reused. This is because we can't possibly - * know if the connection is in a good shape or not now. Unless it is + * closed to prevent being reused. This is because we cannot possibly + * know if the connection is in a good shape or not now. Unless it is * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. */ @@ -2519,13 +2421,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, result != CURLE_HTTP2_STREAM) streamclose(data->conn, "Transfer returned error"); - Curl_posttransfer(data); + multi_posttransfer(data); multi_done(data, result, TRUE); } else if(data->req.done && !Curl_cwriter_is_paused(data)) { /* call this even if the readwrite function returned error */ - Curl_posttransfer(data); + multi_posttransfer(data); /* When we follow redirects or is set to retry the connection, we must to go back to the CONNECT state */ @@ -2552,8 +2454,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, else { /* after the transfer is done, go DONE */ - /* but first check to see if we got a location info even though we're - not following redirects */ + /* but first check to see if we got a location info even though we + are not following redirects */ if(data->req.location) { free(newurl); newurl = data->req.location; @@ -2571,10 +2473,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } } - else if(data->state.select_bits) { + else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer - won't get stuck on this transfer at the expense of other concurrent - transfers */ + will not get stuck on this transfer at the expense of other + concurrent transfers */ Curl_expire(data, 0, EXPIRE_RUN_NOW); } free(newurl); @@ -2588,10 +2490,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->conn) { CURLcode res; - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to connection */ - process_pending_handles(multi); /* multiplexing */ - /* post-transfer command */ res = multi_done(data, result, FALSE); @@ -2610,8 +2508,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } } #endif - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the multi_done() returned! */ + /* after we have DONE what we are supposed to do, go COMPLETED, and + it does not matter what the multi_done() returned! */ multistate(data, MSTATE_COMPLETED); break; @@ -2646,7 +2544,7 @@ statemachine_end: if(data->mstate < MSTATE_COMPLETED) { if(result) { /* - * If an error was returned, and we aren't in completed state now, + * If an error was returned, and we are not in completed state now, * then we go to completed and consider this transfer aborted. */ @@ -2658,31 +2556,27 @@ statemachine_end: if(data->conn) { if(stream_error) { - /* Don't attempt to send data over a connection that timed out */ + /* Do not attempt to send data over a connection that timed out */ bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; struct connectdata *conn = data->conn; /* This is where we make sure that the conn pointer is reset. - We don't have to do this in every case block above where a + We do not have to do this in every case block above where a failure is detected */ Curl_detach_connection(data); - - /* remove connection from cache */ - Curl_conncache_remove_conn(data, conn, TRUE); - - /* disconnect properly */ - Curl_disconnect(data, conn, dead_connection); + Curl_cpool_disconnect(data, conn, dead_connection); } } else if(data->mstate == MSTATE_CONNECT) { /* Curl_connect() failed */ - (void)Curl_posttransfer(data); + multi_posttransfer(data); + Curl_pgrsUpdate_nometer(data); } multistate(data, MSTATE_COMPLETED); rc = CURLM_CALL_MULTI_PERFORM; } - /* if there's still a connection to use, call the progress function */ + /* if there is still a connection to use, call the progress function */ else if(data->conn && Curl_pgrsUpdate(data)) { /* aborted due to progress callback return code must close the connection */ @@ -2714,10 +2608,10 @@ statemachine_end: } multistate(data, MSTATE_MSGSENT); - /* add this handle to the list of msgsent handles */ - Curl_llist_append(&multi->msgsent, data, &data->connect_queue); - /* unlink from the main list */ - unlink_easy(multi, data); + /* unlink from the process list */ + Curl_node_remove(&data->multi_queue); + /* add this handle msgsent list */ + Curl_llist_append(&multi->msgsent, data, &data->multi_queue); return CURLM_OK; } } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); @@ -2729,10 +2623,12 @@ statemachine_end: CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) { - struct Curl_easy *data; CURLMcode returncode = CURLM_OK; - struct Curl_tree *t; + struct Curl_tree *t = NULL; struct curltime now = Curl_now(); + struct Curl_llist_node *e; + struct Curl_llist_node *n = NULL; + SIGPIPE_VARIABLE(pipe_st); if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -2740,31 +2636,31 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - data = multi->easyp; - if(data) { + sigpipe_init(&pipe_st); + for(e = Curl_llist_head(&multi->process); e; e = n) { + struct Curl_easy *data = Curl_node_elem(e); CURLMcode result; - bool nosig = data->set.no_signal; - SIGPIPE_VARIABLE(pipe_st); - sigpipe_ignore(data, &pipe_st); /* Do the loop and only alter the signal ignore state if the next handle has a different NO_SIGNAL state than the previous */ - do { - /* the current node might be unlinked in multi_runsingle(), get the next - pointer now */ - struct Curl_easy *datanext = data->next; - if(data->set.no_signal != nosig) { - sigpipe_restore(&pipe_st); - sigpipe_ignore(data, &pipe_st); - nosig = data->set.no_signal; - } + + /* the current node might be unlinked in multi_runsingle(), get the next + pointer now */ + n = Curl_node_next(e); + + if(data != multi->cpool.idata) { + /* connection pool handle is processed below */ + sigpipe_apply(data, &pipe_st); result = multi_runsingle(multi, &now, data); if(result) returncode = result; - data = datanext; /* operate on next handle */ - } while(data); - sigpipe_restore(&pipe_st); + } } + sigpipe_apply(multi->cpool.idata, &pipe_st); + Curl_cpool_multi_perform(multi); + + sigpipe_restore(&pipe_st); + /* * Simply remove all expired timers from the splay since handles are dealt * with unconditionally by this function and curl_multi_timeout() requires @@ -2779,7 +2675,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); if(t) { /* the removed may have another timeout in queue */ - data = t->payload; + struct Curl_easy *data = Curl_splayget(t); if(data->mstate == MSTATE_PENDING) { bool stream_unused; CURLcode result_unused; @@ -2789,11 +2685,12 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) move_pending_to_connect(multi, data); } } - (void)add_next_timeout(now, multi, t->payload); + (void)add_next_timeout(now, multi, Curl_splayget(t)); } } while(t); - *running_handles = multi->num_alive; + if(running_handles) + *running_handles = (int)multi->num_alive; if(CURLM_OK >= returncode) returncode = Curl_update_timer(multi); @@ -2801,35 +2698,45 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) return returncode; } -/* unlink_all_msgsent_handles() detaches all those easy handles from this - multi handle */ +/* unlink_all_msgsent_handles() moves all nodes back from the msgsent list to + the process list */ static void unlink_all_msgsent_handles(struct Curl_multi *multi) { - struct Curl_llist_element *e = multi->msgsent.head; - if(e) { - struct Curl_easy *data = e->ptr; - DEBUGASSERT(data->mstate == MSTATE_MSGSENT); - data->multi = NULL; + struct Curl_llist_node *e; + for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); + if(data) { + DEBUGASSERT(data->mstate == MSTATE_MSGSENT); + Curl_node_remove(&data->multi_queue); + /* put it into the process list */ + Curl_llist_append(&multi->process, data, &data->multi_queue); + } } } CURLMcode curl_multi_cleanup(struct Curl_multi *multi) { - struct Curl_easy *data; - struct Curl_easy *nextdata; - if(GOOD_MULTI_HANDLE(multi)) { + struct Curl_llist_node *e; + struct Curl_llist_node *n; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; multi->magic = 0; /* not good anymore */ + /* move the pending and msgsent entries back to process + so that there is just one list to iterate over */ unlink_all_msgsent_handles(multi); process_pending_handles(multi); + /* First remove all remaining easy handles */ - data = multi->easyp; - while(data) { - nextdata = data->next; + for(e = Curl_llist_head(&multi->process); e; e = n) { + struct Curl_easy *data = Curl_node_elem(e); + + if(!GOOD_EASY_HANDLE(data)) + return CURLM_BAD_HANDLE; + + n = Curl_node_next(e); if(!data->state.done && data->conn) /* if DONE was never called for this handle */ (void)multi_done(data, CURLE_OK, TRUE); @@ -2840,23 +2747,18 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) data->dns.hostcachetype = HCACHE_NONE; } - /* Clear the pointer to the connection cache */ - data->state.conn_cache = NULL; data->multi = NULL; /* clear the association */ #ifdef USE_LIBPSL if(data->psl == &multi->psl) data->psl = NULL; #endif - - data = nextdata; } - /* Close all the connections in the connection cache */ - Curl_conncache_close_all_connections(&multi->conn_cache); + Curl_cpool_destroy(&multi->cpool); sockhash_destroy(&multi->sockhash); - Curl_conncache_destroy(&multi->conn_cache); + Curl_hash_destroy(&multi->proto_hash); Curl_hash_destroy(&multi->hostcache); Curl_psl_destroy(&multi->psl); @@ -2865,12 +2767,10 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) #else #ifdef ENABLE_WAKEUP wakeup_close(multi->wakeup_pair[0]); +#ifndef USE_EVENTFD wakeup_close(multi->wakeup_pair[1]); #endif #endif - -#ifdef USE_SSL - Curl_free_multi_ssl_backend_data(multi->ssl_backend_data); #endif multi_xfer_bufs_free(multi); @@ -2901,15 +2801,15 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) !multi->in_callback && Curl_llist_count(&multi->msglist)) { /* there is one or more messages in the list */ - struct Curl_llist_element *e; + struct Curl_llist_node *e; /* extract the head of the list to return */ - e = multi->msglist.head; + e = Curl_llist_head(&multi->msglist); - msg = e->ptr; + msg = Curl_node_elem(e); /* remove the extracted entry */ - Curl_llist_remove(&multi->msglist, e, NULL); + Curl_node_remove(e); *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); @@ -2927,41 +2827,54 @@ static CURLMcode singlesocket(struct Curl_multi *multi, struct Curl_easy *data) { struct easy_pollset cur_poll; - unsigned int i; - struct Curl_sh_entry *entry; - curl_socket_t s; - int rc; + CURLMcode mresult; /* Fill in the 'current' struct with the state as it is now: what sockets to supervise and for what actions */ multi_getsock(data, &cur_poll); + mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll); + + if(!mresult) /* Remember for next time */ + memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll)); + return mresult; +} + +CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct easy_pollset *ps, + struct easy_pollset *last_ps) +{ + unsigned int i; + struct Curl_sh_entry *entry; + curl_socket_t s; + int rc; /* We have 0 .. N sockets already and we get to know about the 0 .. M sockets we should have from now on. Detect the differences, remove no longer supervised ones and add new ones */ /* walk over the sockets we got right now */ - for(i = 0; i < cur_poll.num; i++) { - unsigned char cur_action = cur_poll.actions[i]; + for(i = 0; i < ps->num; i++) { + unsigned char cur_action = ps->actions[i]; unsigned char last_action = 0; int comboaction; - s = cur_poll.sockets[i]; + s = ps->sockets[i]; /* get it from the hash */ entry = sh_getentry(&multi->sockhash, s); if(entry) { /* check if new for this transfer */ unsigned int j; - for(j = 0; j< data->last_poll.num; j++) { - if(s == data->last_poll.sockets[j]) { - last_action = data->last_poll.actions[j]; + for(j = 0; j< last_ps->num; j++) { + if(s == last_ps->sockets[j]) { + last_action = last_ps->actions[j]; break; } } } else { - /* this is a socket we didn't have before, add it to the hash! */ + /* this is a socket we did not have before, add it to the hash! */ entry = sh_addentry(&multi->sockhash, s); if(!entry) /* fatal */ @@ -2969,23 +2882,30 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } if(last_action && (last_action != cur_action)) { /* Socket was used already, but different action now */ - if(last_action & CURL_POLL_IN) + if(last_action & CURL_POLL_IN) { + DEBUGASSERT(entry->readers); entry->readers--; - if(last_action & CURL_POLL_OUT) + } + if(last_action & CURL_POLL_OUT) { + DEBUGASSERT(entry->writers); entry->writers--; - if(cur_action & CURL_POLL_IN) + } + if(cur_action & CURL_POLL_IN) { entry->readers++; + } if(cur_action & CURL_POLL_OUT) entry->writers++; } - else if(!last_action) { + else if(!last_action && + !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */ + sizeof(struct Curl_easy *))) { + DEBUGASSERT(entry->users < 100000); /* detect weird values */ /* a new transfer using this socket */ entry->users++; if(cur_action & CURL_POLL_IN) entry->readers++; if(cur_action & CURL_POLL_OUT) entry->writers++; - /* add 'data' to the transfer hash on this socket! */ if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ sizeof(struct Curl_easy *), data)) { @@ -3014,18 +2934,19 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } - entry->action = comboaction; /* store the current action state */ + /* store the current action state */ + entry->action = (unsigned int)comboaction; } - /* Check for last_poll.sockets that no longer appear in cur_poll.sockets. + /* Check for last_poll.sockets that no longer appear in ps->sockets. * Need to remove the easy handle from the multi->sockhash->transfers and * remove multi->sockhash entry when this was the last transfer */ - for(i = 0; i< data->last_poll.num; i++) { + for(i = 0; i < last_ps->num; i++) { unsigned int j; bool stillused = FALSE; - s = data->last_poll.sockets[i]; - for(j = 0; j < cur_poll.num; j++) { - if(s == cur_poll.sockets[j]) { + s = last_ps->sockets[i]; + for(j = 0; j < ps->num; j++) { + if(s == ps->sockets[j]) { /* this is still supervised */ stillused = TRUE; break; @@ -3038,25 +2959,29 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* if this is NULL here, the socket has been closed and notified so already by Curl_multi_closed() */ if(entry) { - unsigned char oldactions = data->last_poll.actions[i]; + unsigned char oldactions = last_ps->actions[i]; /* this socket has been removed. Decrease user count */ + DEBUGASSERT(entry->users); entry->users--; if(oldactions & CURL_POLL_OUT) entry->writers--; if(oldactions & CURL_POLL_IN) entry->readers--; if(!entry->users) { + bool dead = FALSE; if(multi->socket_cb) { set_in_callback(multi, TRUE); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->socketp); set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } + if(rc == -1) + dead = TRUE; } sh_delentry(entry, &multi->sockhash, s); + if(dead) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } } else { /* still users, but remove this handle as a user of this socket */ @@ -3068,8 +2993,6 @@ static CURLMcode singlesocket(struct Curl_multi *multi, } } /* for loop over num */ - /* Remember for next time */ - memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll)); return CURLM_OK; } @@ -3085,7 +3008,7 @@ CURLcode Curl_updatesocket(struct Curl_easy *data) * Curl_multi_closed() * * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using is about to be closed. This function will then + * sockets we were using is about to be closed. This function will then * remove it from the sockethash for this handle to make the multi_socket API * behave properly, especially for the case when libcurl will create another * socket again and it gets the same file descriptor number. @@ -3094,13 +3017,17 @@ CURLcode Curl_updatesocket(struct Curl_easy *data) void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) { if(data) { - /* if there's still an easy handle associated with this connection */ + /* if there is still an easy handle associated with this connection */ struct Curl_multi *multi = data->multi; + DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T + " multi is %p", s, (void *)multi)); if(multi) { /* this is set if this connection is part of a handle that is added to a multi handle, and only then this is necessary */ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); + DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T + " entry is %p", s, (void *)entry)); if(entry) { int rc = 0; if(multi->socket_cb) { @@ -3140,26 +3067,24 @@ static CURLMcode add_next_timeout(struct curltime now, { struct curltime *tv = &d->state.expiretime; struct Curl_llist *list = &d->state.timeoutlist; - struct Curl_llist_element *e; - struct time_node *node = NULL; + struct Curl_llist_node *e; /* move over the timeout list for this specific handle and remove all timeouts that are now passed tense and store the next pending timeout in *tv */ - for(e = list->head; e;) { - struct Curl_llist_element *n = e->next; - timediff_t diff; - node = (struct time_node *)e->ptr; - diff = Curl_timediff_us(node->time, now); + for(e = Curl_llist_head(list); e;) { + struct Curl_llist_node *n = Curl_node_next(e); + struct time_node *node = Curl_node_elem(e); + timediff_t diff = Curl_timediff_us(node->time, now); if(diff <= 0) /* remove outdated entry */ - Curl_llist_remove(list, e, NULL); + Curl_node_remove(e); else /* the list is sorted so get out on the first mismatch */ break; e = n; } - e = list->head; + e = Curl_llist_head(list); if(!e) { /* clear the expire times within the handles that we remove from the splay tree */ @@ -3167,10 +3092,11 @@ static CURLMcode add_next_timeout(struct curltime now, tv->tv_usec = 0; } else { + struct time_node *node = Curl_node_elem(e); /* copy the first entry to 'tv' */ memcpy(tv, &node->time, sizeof(*tv)); - /* Insert this node again into the splay. Keep the timer in the list in + /* Insert this node again into the splay. Keep the timer in the list in case we need to recompute future timers. */ multi->timetree = Curl_splayinsert(*tv, multi->timetree, &d->state.timenode); @@ -3178,6 +3104,59 @@ static CURLMcode add_next_timeout(struct curltime now, return CURLM_OK; } +struct multi_run_ctx { + struct Curl_multi *multi; + struct curltime now; + size_t run_xfers; + SIGPIPE_MEMBER(pipe_st); + bool run_cpool; +}; + +static CURLMcode multi_run_expired(struct multi_run_ctx *mrc) +{ + struct Curl_multi *multi = mrc->multi; + struct Curl_easy *data = NULL; + struct Curl_tree *t = NULL; + CURLMcode result = CURLM_OK; + + /* + * The loop following here will go on as long as there are expire-times left + * to process (compared to mrc->now) in the splay and 'data' will be + * re-assigned for every expired handle we deal with. + */ + while(1) { + /* Check if there is one (more) expired timer to deal with! This function + extracts a matching node if there is one */ + multi->timetree = Curl_splaygetbest(mrc->now, multi->timetree, &t); + if(!t) + goto out; + + data = Curl_splayget(t); /* assign this for next loop */ + if(!data) + continue; + + (void)add_next_timeout(mrc->now, multi, data); + if(data == multi->cpool.idata) { + mrc->run_cpool = TRUE; + continue; + } + + mrc->run_xfers++; + sigpipe_apply(data, &mrc->pipe_st); + result = multi_runsingle(multi, &mrc->now, data); + + if(CURLM_OK >= result) { + /* get the socket(s) and check if the state has been changed since + last */ + result = singlesocket(multi, data); + if(result) + goto out; + } + } + +out: + return result; +} static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, @@ -3186,39 +3165,44 @@ static CURLMcode multi_socket(struct Curl_multi *multi, { CURLMcode result = CURLM_OK; struct Curl_easy *data = NULL; - struct Curl_tree *t; - struct curltime now = Curl_now(); - bool first = FALSE; - bool nosig = FALSE; - SIGPIPE_VARIABLE(pipe_st); + struct multi_run_ctx mrc; + + (void)ev_bitmask; + memset(&mrc, 0, sizeof(mrc)); + mrc.multi = multi; + mrc.now = Curl_now(); + sigpipe_init(&mrc.pipe_st); if(checkall) { + struct Curl_llist_node *e; /* *perform() deals with running_handles on its own */ result = curl_multi_perform(multi, running_handles); /* walk through each easy handle and do the socket state change magic and callbacks */ if(result != CURLM_BAD_HANDLE) { - data = multi->easyp; - while(data && !result) { - result = singlesocket(multi, data); - data = data->next; + for(e = Curl_llist_head(&multi->process); e && !result; + e = Curl_node_next(e)) { + result = singlesocket(multi, Curl_node_elem(e)); } } - - /* or should we fall-through and do the timer-based stuff? */ - return result; + mrc.run_cpool = TRUE; + goto out; } + if(s != CURL_SOCKET_TIMEOUT) { struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - if(!entry) - /* Unmatched socket, we can't act on it but we ignore this fact. In + if(!entry) { + /* Unmatched socket, we cannot act on it but we ignore this fact. In real-world tests it has been proved that libevent can in fact give the application actions even though the socket was just previously asked to get removed, so thus we better survive stray socket actions and just move on. */ - ; + /* The socket might come from a connection that is being shut down + * by the multi's connection pool. */ + Curl_cpool_multi_socket(multi, s, ev_bitmask); + } else { struct Curl_hash_iterator iter; struct Curl_hash_element *he; @@ -3231,75 +3215,43 @@ static CURLMcode multi_socket(struct Curl_multi *multi, DEBUGASSERT(data); DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); - if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) - /* set socket event bitmask if they're not locked */ - data->state.select_bits |= (unsigned char)ev_bitmask; - - Curl_expire(data, 0, EXPIRE_RUN_NOW); + if(data == multi->cpool.idata) + mrc.run_cpool = TRUE; + else { + /* Expire with out current now, so we will get it below when + * asking the splaytree for expired transfers. */ + Curl_expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW); + } } - - /* Now we fall-through and do the timer-based stuff, since we don't want - to force the user to have to deal with timeouts as long as at least - one connection in fact has traffic. */ - - data = NULL; /* set data to NULL again to avoid calling - multi_runsingle() in case there's no need to */ - now = Curl_now(); /* get a newer time since the multi_runsingle() loop - may have taken some time */ } } - else { - /* Asked to run due to time-out. Clear the 'lastcall' variable to force - Curl_update_timer() to trigger a callback to the app again even if the - same timeout is still the one to run after this call. That handles the - case when the application asks libcurl to run the timeout - prematurely. */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - } - /* - * The loop following here will go on as long as there are expire-times left - * to process in the splay and 'data' will be re-assigned for every expired - * handle we deal with. - */ - do { - /* the first loop lap 'data' can be NULL */ - if(data) { - if(!first) { - first = TRUE; - nosig = data->set.no_signal; /* initial state */ - sigpipe_ignore(data, &pipe_st); - } - else if(data->set.no_signal != nosig) { - sigpipe_restore(&pipe_st); - sigpipe_ignore(data, &pipe_st); - nosig = data->set.no_signal; /* remember new state */ - } - result = multi_runsingle(multi, &now, data); + result = multi_run_expired(&mrc); + if(result) + goto out; - if(CURLM_OK >= result) { - /* get the socket(s) and check if the state has been changed since - last */ - result = singlesocket(multi, data); - if(result) - break; - } - } - - /* Check if there's one (more) expired timer to deal with! This function - extracts a matching node if there is one */ + if(mrc.run_xfers) { + /* Running transfers takes time. With a new timestamp, we might catch + * other expires which are due now. Instead of telling the application + * to set a 0 timeout and call us again, we run them here. + * Do that only once or it might be unfair to transfers on other + * sockets. */ + mrc.now = Curl_now(); + result = multi_run_expired(&mrc); + } - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - data = t->payload; /* assign this for next loop */ - (void)add_next_timeout(now, multi, t->payload); - } +out: + if(mrc.run_cpool) { + sigpipe_apply(multi->cpool.idata, &mrc.pipe_st); + Curl_cpool_multi_perform(multi); + } + sigpipe_restore(&mrc.pipe_st); - } while(t); - if(first) - sigpipe_restore(&pipe_st); + if(running_handles) + *running_handles = (int)multi->num_alive; - *running_handles = multi->num_alive; + if(CURLM_OK >= result) + result = Curl_update_timer(multi); return result; } @@ -3351,6 +3303,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, break; case CURLMOPT_MAX_TOTAL_CONNECTIONS: multi->max_total_connections = va_arg(param, long); + /* for now, let this also decide the max number of connections + * in shutdown handling */ + multi->max_shutdown_connections = va_arg(param, long); break; /* options formerly used for pipelining */ case CURLMOPT_MAX_PIPELINE_LENGTH: @@ -3385,39 +3340,28 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, int *running_handles) { - CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, FALSE, s, 0, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; + return multi_socket(multi, FALSE, s, 0, running_handles); } CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, int ev_bitmask, int *running_handles) { - CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; + return multi_socket(multi, FALSE, s, ev_bitmask, running_handles); } CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) { - CURLMcode result; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; + return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); } static CURLMcode multi_timeout(struct Curl_multi *multi, + struct curltime *expire_time, long *timeout_ms) { static const struct curltime tv_zero = {0, 0}; @@ -3433,20 +3377,29 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, /* splay the lowest to the bottom */ multi->timetree = Curl_splay(tv_zero, multi->timetree); - - if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { + /* this will not return NULL from a non-emtpy tree, but some compilers + * are not convinced of that. Analyzers are hard. */ + *expire_time = multi->timetree? multi->timetree->key : tv_zero; + + /* 'multi->timetree' will be non-NULL here but the compilers sometimes + yell at us if we assume so */ + if(multi->timetree && + Curl_timediff_us(multi->timetree->key, now) > 0) { /* some time left before expiration */ timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now); - /* this should be safe even on 32 bit archs, as we don't use that + /* this should be safe even on 32-bit archs, as we do not use that overly long timeouts */ *timeout_ms = (long)diff; } - else + else { /* 0 means immediately */ *timeout_ms = 0; + } } - else + else { + *expire_time = tv_zero; *timeout_ms = -1; + } return CURLM_OK; } @@ -3454,6 +3407,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, CURLMcode curl_multi_timeout(struct Curl_multi *multi, long *timeout_ms) { + struct curltime expire_time; + /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3461,56 +3416,79 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - return multi_timeout(multi, timeout_ms); + return multi_timeout(multi, &expire_time, timeout_ms); } +#define DEBUG_UPDATE_TIMER 0 + /* * Tell the application it should update its timers, if it subscribes to the * update timer callback. */ CURLMcode Curl_update_timer(struct Curl_multi *multi) { + struct curltime expire_ts; long timeout_ms; int rc; + bool set_value = FALSE; if(!multi->timer_cb || multi->dead) return CURLM_OK; - if(multi_timeout(multi, &timeout_ms)) { - return CURLM_OK; - } - if(timeout_ms < 0) { - static const struct curltime none = {0, 0}; - if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { - multi->timer_lastcall = none; - /* there's no timeout now but there was one previously, tell the app to - disable it */ - set_in_callback(multi, TRUE); - rc = multi->timer_cb(multi, -1, multi->timer_userp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } - return CURLM_OK; - } + if(multi_timeout(multi, &expire_ts, &timeout_ms)) { return CURLM_OK; } - /* When multi_timeout() is done, multi->timetree points to the node with the - * timeout we got the (relative) time-out time for. We can thus easily check - * if this is the same (fixed) time as we got in a previous call and then - * avoid calling the callback again. */ - if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) - return CURLM_OK; - - multi->timer_lastcall = multi->timetree->key; + if(timeout_ms < 0 && multi->last_timeout_ms < 0) { +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), still no timeout, no change\n"); +#endif + } + else if(timeout_ms < 0) { + /* there is no timeout now but there was one previously */ +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), remove timeout, " + " last_timeout=%ldms\n", multi->last_timeout_ms); +#endif + timeout_ms = -1; /* normalize */ + set_value = TRUE; + } + else if(multi->last_timeout_ms < 0) { +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), had no timeout, set now\n"); +#endif + set_value = TRUE; + } + else if(Curl_timediff_us(multi->last_expire_ts, expire_ts)) { + /* We had a timeout before and have one now, the absolute timestamp + * differs. The relative timeout_ms may be the same, but the starting + * point differs. Let the application restart its timer. */ +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), expire timestamp changed\n"); +#endif + set_value = TRUE; + } + else { + /* We have same expire time as previously. Our relative 'timeout_ms' + * may be different now, but the application has the timer running + * and we do not to tell it to start this again. */ +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), same expire timestamp, no change\n"); +#endif + } - set_in_callback(multi, TRUE); - rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; + if(set_value) { +#if DEBUG_UPDATE_TIMER + fprintf(stderr, "Curl_update_timer(), set timeout %ldms\n", timeout_ms); +#endif + multi->last_expire_ts = expire_ts; + multi->last_timeout_ms = timeout_ms; + set_in_callback(multi, TRUE); + rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); + set_in_callback(multi, FALSE); + if(rc == -1) { + multi->dead = TRUE; + return CURLM_ABORTED_BY_CALLBACK; + } } return CURLM_OK; } @@ -3523,13 +3501,13 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) static void multi_deltimeout(struct Curl_easy *data, expire_id eid) { - struct Curl_llist_element *e; + struct Curl_llist_node *e; struct Curl_llist *timeoutlist = &data->state.timeoutlist; /* find and remove the specific node from the list */ - for(e = timeoutlist->head; e; e = e->next) { - struct time_node *n = (struct time_node *)e->ptr; + for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) { + struct time_node *n = Curl_node_elem(e); if(n->eid == eid) { - Curl_llist_remove(timeoutlist, e, NULL); + Curl_node_remove(e); return; } } @@ -3547,9 +3525,9 @@ multi_addtimeout(struct Curl_easy *data, struct curltime *stamp, expire_id eid) { - struct Curl_llist_element *e; + struct Curl_llist_node *e; struct time_node *node; - struct Curl_llist_element *prev = NULL; + struct Curl_llist_node *prev = NULL; size_t n; struct Curl_llist *timeoutlist = &data->state.timeoutlist; @@ -3562,8 +3540,8 @@ multi_addtimeout(struct Curl_easy *data, n = Curl_llist_count(timeoutlist); if(n) { /* find the correct spot in the list */ - for(e = timeoutlist->head; e; e = e->next) { - struct time_node *check = (struct time_node *)e->ptr; + for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) { + struct time_node *check = Curl_node_elem(e); timediff_t diff = Curl_timediff(check->time, node->time); if(diff > 0) break; @@ -3589,10 +3567,12 @@ multi_addtimeout(struct Curl_easy *data, * * Expire replaces a former timeout using the same id if already set. */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) +static void Curl_expire_ex(struct Curl_easy *data, + const struct curltime *nowp, + timediff_t milli, expire_id id) { struct Curl_multi *multi = data->multi; - struct curltime *nowp = &data->state.expiretime; + struct curltime *curr_expire = &data->state.expiretime; struct curltime set; /* this is only interesting while there is still an associated multi struct @@ -3602,9 +3582,9 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) DEBUGASSERT(id < EXPIRE_LAST); - set = Curl_now(); - set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */ - set.tv_usec += (unsigned int)(milli%1000)*1000; + set = *nowp; + set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */ + set.tv_usec += (int)(milli%1000)*1000; if(set.tv_usec >= 1000000) { set.tv_sec++; @@ -3614,20 +3594,20 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) /* Remove any timer with the same id just in case. */ multi_deltimeout(data, id); - /* Add it to the timer list. It must stay in the list until it has expired + /* Add it to the timer list. It must stay in the list until it has expired in case we need to recompute the minimum timer later. */ multi_addtimeout(data, &set, id); - if(nowp->tv_sec || nowp->tv_usec) { + if(curr_expire->tv_sec || curr_expire->tv_usec) { /* This means that the struct is added as a node in the splay tree. Compare if the new time is earlier, and only remove-old/add-new if it is. */ - timediff_t diff = Curl_timediff(set, *nowp); + timediff_t diff = Curl_timediff(set, *curr_expire); int rc; if(diff > 0) { /* The current splay tree entry is sooner than this new expiry time. - We don't need to update our splay tree entry. */ + We do not need to update our splay tree entry. */ return; } @@ -3641,12 +3621,18 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) /* Indicate that we are in the splay tree and insert the new timer expiry value since it is our local minimum. */ - *nowp = set; - data->state.timenode.payload = data; - multi->timetree = Curl_splayinsert(*nowp, multi->timetree, + *curr_expire = set; + Curl_splayset(&data->state.timenode, data); + multi->timetree = Curl_splayinsert(*curr_expire, multi->timetree, &data->state.timenode); } +void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) +{ + struct curltime now = Curl_now(); + Curl_expire_ex(data, &now, milli, id); +} + /* * Curl_expire_done() * @@ -3664,7 +3650,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id) * * Clear ALL timeout values for this handle. */ -void Curl_expire_clear(struct Curl_easy *data) +bool Curl_expire_clear(struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct curltime *nowp = &data->state.expiretime; @@ -3672,7 +3658,7 @@ void Curl_expire_clear(struct Curl_easy *data) /* this is only interesting while there is still an associated multi struct remaining! */ if(!multi) - return; + return FALSE; if(nowp->tv_sec || nowp->tv_usec) { /* Since this is an cleared time, we must remove the previous entry from @@ -3685,26 +3671,25 @@ void Curl_expire_clear(struct Curl_easy *data) if(rc) infof(data, "Internal error clearing splay node = %d", rc); - /* flush the timeout list too */ - while(list->size > 0) { - Curl_llist_remove(list, list->tail, NULL); - } + /* clear the timeout list too */ + Curl_llist_destroy(list, NULL); #ifdef DEBUGBUILD infof(data, "Expire cleared"); #endif nowp->tv_sec = 0; nowp->tv_usec = 0; + return TRUE; } + return FALSE; } - - - CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, void *hashp) { struct Curl_sh_entry *there = NULL; + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; there = sh_getentry(&multi->sockhash, s); @@ -3716,52 +3701,24 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, return CURLM_OK; } -size_t Curl_multi_max_host_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_host_connections : 0; -} - -size_t Curl_multi_max_total_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_total_connections : 0; -} - -/* - * When information about a connection has appeared, call this! - */ - -void Curl_multiuse_state(struct Curl_easy *data, - int bundlestate) /* use BUNDLE_* defines */ -{ - struct connectdata *conn; - DEBUGASSERT(data); - DEBUGASSERT(data->multi); - conn = data->conn; - DEBUGASSERT(conn); - DEBUGASSERT(conn->bundle); - - conn->bundle->multiuse = bundlestate; - process_pending_handles(data->multi); -} - static void move_pending_to_connect(struct Curl_multi *multi, struct Curl_easy *data) { DEBUGASSERT(data->mstate == MSTATE_PENDING); - /* put it back into the main list */ - link_easy(multi, data); + /* Remove this node from the pending list */ + Curl_node_remove(&data->multi_queue); - multistate(data, MSTATE_CONNECT); + /* put it into the process list */ + Curl_llist_append(&multi->process, data, &data->multi_queue); - /* Remove this node from the pending list */ - Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); + multistate(data, MSTATE_CONNECT); /* Make sure that the handle will be processed soonish. */ Curl_expire(data, 0, EXPIRE_RUN_NOW); } -/* process_pending_handles() moves a handle from PENDING back into the main +/* process_pending_handles() moves a handle from PENDING back into the process list and change state to CONNECT. We do not move all transfers because that can be a significant amount. @@ -3777,9 +3734,9 @@ static void move_pending_to_connect(struct Curl_multi *multi, */ static void process_pending_handles(struct Curl_multi *multi) { - struct Curl_llist_element *e = multi->pending.head; + struct Curl_llist_node *e = Curl_llist_head(&multi->pending); if(e) { - struct Curl_easy *data = e->ptr; + struct Curl_easy *data = Curl_node_elem(e); move_pending_to_connect(multi, data); } } @@ -3807,12 +3764,12 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi) (multi->num_easy + 1)); if(a) { unsigned int i = 0; - struct Curl_easy *e = multi->easyp; - while(e) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *data = Curl_node_elem(e); DEBUGASSERT(i < multi->num_easy); - if(!e->state.internal) - a[i++] = e; - e = e->next; + if(!data->state.internal) + a[i++] = data; } a[i] = NULL; /* last entry is a NULL */ } @@ -3935,3 +3892,32 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi) multi->xfer_ulbuf_len = 0; multi->xfer_ulbuf_borrowed = FALSE; } + +struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi, + curl_off_t mid) +{ + + if(mid >= 0) { + struct Curl_easy *data; + struct Curl_llist_node *e; + + for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) { + data = Curl_node_elem(e); + if(data->mid == mid) + return data; + } + /* may be in msgsent queue */ + for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) { + data = Curl_node_elem(e); + if(data->mid == mid) + return data; + } + /* may be in pending queue */ + for(e = Curl_llist_head(&multi->pending); e; e = Curl_node_next(e)) { + data = Curl_node_elem(e); + if(data->mid == mid) + return data; + } + } + return NULL; +} diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index add9a0518..fef117c06 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h @@ -33,7 +33,7 @@ struct connectdata; struct Curl_message { - struct Curl_llist_element list; + struct Curl_llist_node list; /* the 'CURLMsg' is the part that is visible to the external user */ struct CURLMsg extmsg; }; @@ -80,30 +80,23 @@ typedef enum { /* value for MAXIMUM CONCURRENT STREAMS upper limit */ #define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) -/* Curl_multi SSL backend-specific data; declared differently by each SSL - backend */ -struct multi_ssl_backend_data; - /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ unsigned int magic; - /* We have a doubly-linked list with easy handles */ - struct Curl_easy *easyp; - struct Curl_easy *easylp; /* last node */ - unsigned int num_easy; /* amount of entries in the linked list above. */ unsigned int num_alive; /* amount of easy handles that are added but have not yet reached COMPLETE state */ struct Curl_llist msglist; /* a list of messages from completed transfers */ - struct Curl_llist pending; /* Curl_easys that are in the - MSTATE_PENDING state */ - struct Curl_llist msgsent; /* Curl_easys that are in the - MSTATE_MSGSENT state */ + /* Each added easy handle is added to ONE of these three lists */ + struct Curl_llist process; /* not in PENDING or MSGSENT */ + struct Curl_llist pending; /* in PENDING */ + struct Curl_llist msgsent; /* in MSGSENT */ + curl_off_t next_easy_mid; /* next multi-id for easy handle added */ /* callback function and user data pointer for the *socket() API */ curl_socket_callback socket_cb; @@ -132,40 +125,47 @@ struct Curl_multi { char *xfer_ulbuf; /* the actual buffer */ size_t xfer_ulbuf_len; /* the allocated length */ -#if defined(USE_SSL) - struct multi_ssl_backend_data *ssl_backend_data; -#endif - /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note the pluralis form, there can be more than one easy handle waiting on the same actual socket) */ struct Curl_hash sockhash; + /* `proto_hash` is a general key-value store for protocol implementations + * with the lifetime of the multi handle. The number of elements kept here + * should be in the order of supported protocols (and sub-protocols like + * TLS), *not* in the order of connections or current transfers! + * Elements need to be added with their own destructor to be invoked when + * the multi handle is cleaned up (see Curl_hash_add2()).*/ + struct Curl_hash proto_hash; /* Shared connection cache (bundles)*/ - struct conncache conn_cache; + struct cpool cpool; long max_host_connections; /* if >0, a fixed limit of the maximum number of connections per host */ long max_total_connections; /* if >0, a fixed limit of the maximum number of connections in total */ + long max_shutdown_connections; /* if >0, a fixed limit of the maximum number + of connections in shutdown handling */ /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; void *timer_userp; - struct curltime timer_lastcall; /* the fixed time for the timeout for the - previous callback */ + long last_timeout_ms; /* the last timeout value set via timer_cb */ + struct curltime last_expire_ts; /* timestamp of last expiry */ + #ifdef USE_WINSOCK - WSAEVENT wsa_event; /* winsock event used for waits */ + WSAEVENT wsa_event; /* Winsock event used for waits */ #else #ifdef ENABLE_WAKEUP - curl_socket_t wakeup_pair[2]; /* pipe()/socketpair() used for wakeup - 0 is used for read, 1 is used for write */ + curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for + wakeup 0 is used for read, 1 is used + for write */ #endif #endif unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of - entries we're allowed to grow the connection + entries we are allowed to grow the connection cache to */ #define IPV6_UNKNOWN 0 #define IPV6_DEAD 1 diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index 59a30646c..e5872cd6d 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h @@ -30,7 +30,7 @@ CURLcode Curl_updatesocket(struct Curl_easy *data); void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); -void Curl_expire_clear(struct Curl_easy *data); +bool Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, @@ -63,20 +63,11 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* mask for checking if read and/or write is set for index x */ #define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x)) -/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */ -size_t Curl_multi_max_host_connections(struct Curl_multi *multi); - -/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */ -size_t Curl_multi_max_total_connections(struct Curl_multi *multi); - -void Curl_multiuse_state(struct Curl_easy *data, - int bundlestate); /* use BUNDLE_* defines */ - /* * Curl_multi_closed() * * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using is about to be closed. This function will then + * sockets we were using is about to be closed. This function will then * remove it from the sockethash for this handle to make the multi_socket API * behave properly, especially for the case when libcurl will create another * socket again and it gets the same file descriptor number. @@ -84,6 +75,15 @@ void Curl_multiuse_state(struct Curl_easy *data, void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s); +/* Compare the two pollsets to notify the multi_socket API of changes + * in socket polling, e.g calling multi->socket_cb() with the changes if + * differences are seen. + */ +CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi, + struct Curl_easy *data, + struct easy_pollset *ps, + struct easy_pollset *last_ps); + /* * Add a handle and move it into PERFORM state at once. For pushed streams. */ @@ -144,4 +144,10 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, */ void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf); +/** + * Get the transfer handle for the given id. Returns NULL if not found. + */ +struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi, + curl_off_t id); + #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index cd2a2844e..490efb64c 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c @@ -237,7 +237,7 @@ static int parsenetrc(const char *host, else if(strcasecompare("password", tok)) state_password = 1; else if(strcasecompare("machine", tok)) { - /* ok, there's machine here go => */ + /* ok, there is machine here go => */ state = HOSTFOUND; state_our_login = FALSE; } @@ -277,7 +277,7 @@ out: /* * @unittest: 1304 * - * *loginp and *passwordp MUST be allocated if they aren't NULL when passed + * *loginp and *passwordp MUST be allocated if they are not NULL when passed * in. */ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, @@ -324,7 +324,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, return retcode; /* no home directory found (or possibly out of memory) */ - filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); + filealloc = aprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { free(homea); return -1; @@ -334,7 +334,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { /* fallback to the old-style "_netrc" file */ - filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); + filealloc = aprintf("%s%s_netrc", home, DIR_CHAR); if(!filealloc) { free(homea); return -1; diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h index 9f2815f3b..37c95db5e 100644 --- a/Utilities/cmcurl/lib/netrc.h +++ b/Utilities/cmcurl/lib/netrc.h @@ -27,7 +27,7 @@ #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 */ +/* returns -1 on failure, 0 if the host is found, 1 is the host is not found */ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, char *filename); /* Assume: (*passwordp)[0]=0, host[0] != 0. diff --git a/Utilities/cmcurl/lib/nonblock.c b/Utilities/cmcurl/lib/nonblock.c index f4eb65612..6dcf42a7e 100644 --- a/Utilities/cmcurl/lib/nonblock.c +++ b/Utilities/cmcurl/lib/nonblock.c @@ -47,16 +47,25 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ int nonblock /* TRUE or FALSE */) { #if defined(HAVE_FCNTL_O_NONBLOCK) - /* most recent unix versions */ + /* most recent Unix versions */ int flags; flags = sfcntl(sockfd, F_GETFL, 0); + if(flags < 0) + return -1; + /* Check if the current file status flags have already satisfied + * the request, if so, it is no need to call fcntl() to replicate it. + */ + if(!!(flags & O_NONBLOCK) == !!nonblock) + return 0; if(nonblock) - return sfcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - return sfcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + return sfcntl(sockfd, F_SETFL, flags); #elif defined(HAVE_IOCTL_FIONBIO) - /* older unix versions */ + /* older Unix versions */ int flags = nonblock ? 1 : 0; return ioctl(sockfd, FIONBIO, &flags); @@ -64,7 +73,7 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ /* Windows */ unsigned long flags = nonblock ? 1UL : 0UL; - return ioctlsocket(sockfd, FIONBIO, &flags); + return ioctlsocket(sockfd, (long)FIONBIO, &flags); #elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c index 62299e28f..dbfafc93e 100644 --- a/Utilities/cmcurl/lib/noproxy.c +++ b/Utilities/cmcurl/lib/noproxy.c @@ -79,22 +79,22 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, unsigned int bits) { #ifdef USE_IPV6 - int bytes; - int rest; + unsigned int bytes; + unsigned int rest; unsigned char address[16]; unsigned char check[16]; if(!bits) bits = 128; - bytes = bits/8; + bytes = bits / 8; rest = bits & 0x07; + if((bytes > 16) || ((bytes == 16) && rest)) + return FALSE; 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)))) @@ -119,13 +119,12 @@ enum nametype { * Checks if the host is in the noproxy list. returns TRUE if it matches and * therefore the proxy should NOT be used. ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy, - bool *spacesep) +bool Curl_check_noproxy(const char *name, const char *no_proxy) { char hostip[128]; - *spacesep = FALSE; + /* - * If we don't have a hostname at all, like for example with a FILE + * If we do not have a hostname at all, like for example with a FILE * transfer, we have nothing to interrogate the noproxy list with. */ if(!name || name[0] == '\0') @@ -143,7 +142,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, if(!strcmp("*", no_proxy)) return TRUE; - /* NO_PROXY was specified and it wasn't just an asterisk */ + /* NO_PROXY was specified and it was not just an asterisk */ if(name[0] == '[') { char *endptr; @@ -166,7 +165,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, if(1 == Curl_inet_pton(AF_INET, name, &address)) type = TYPE_IPV4; else { - /* ignore trailing dots in the host name */ + /* ignore trailing dots in the hostname */ if(name[namelen - 1] == '.') namelen--; } @@ -232,7 +231,9 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, slash = strchr(check, '/'); /* if the slash is part of this token, use it */ if(slash) { - bits = atoi(slash + 1); + /* if the bits variable gets a crazy value here, that is fine as + the value will then be rejected in the cidr function */ + bits = (unsigned int)atoi(slash + 1); *slash = 0; /* null terminate there */ } if(type == TYPE_IPV6) @@ -248,16 +249,14 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy, /* pass blanks after pattern */ while(ISBLANK(*p)) p++; - /* if not a comma! */ - if(*p && (*p != ',')) { - *spacesep = TRUE; - continue; - } + /* if not a comma, this ends the loop */ + if(*p != ',') + break; /* pass any number of commas */ while(*p == ',') p++; } /* while(*p) */ - } /* NO_PROXY was specified and it wasn't just an asterisk */ + } /* NO_PROXY was specified and it was not just an asterisk */ return FALSE; } diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h index a3a680772..71ae7eaaf 100644 --- a/Utilities/cmcurl/lib/noproxy.h +++ b/Utilities/cmcurl/lib/noproxy.h @@ -27,7 +27,7 @@ #ifndef CURL_DISABLE_PROXY -#ifdef DEBUGBUILD +#ifdef UNITTESTS UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ const char *network, /* 1.2.3.4 address */ @@ -37,9 +37,7 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, unsigned int bits); #endif -bool Curl_check_noproxy(const char *name, const char *no_proxy, - bool *spacesep); - +bool Curl_check_noproxy(const char *name, const char *no_proxy); #endif #endif /* HEADER_CURL_NOPROXY_H */ diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index 0348ef5ba..f3e13ed5c 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c @@ -921,7 +921,7 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) else { lr->msgid = msgid; data->req.p.ldap = lr; - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); *done = TRUE; } } @@ -1152,7 +1152,7 @@ ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) return 0; } -/* We don't need to do anything because libcurl does it already */ +/* We do not need to do anything because libcurl does it already */ static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) { @@ -1201,7 +1201,7 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) if(conn) { struct ldapconninfo *li = conn->proto.ldapc; CURLcode err = CURLE_SEND_ERROR; - ret = (li->send)(data, FIRSTSOCKET, buf, len, &err); + ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err); if(ret < 0 && err == CURLE_AGAIN) { SET_SOCKERRNO(EWOULDBLOCK); } diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 1a7195b16..d35b58b0d 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c @@ -244,7 +244,7 @@ static int checkmonth(const char *check, size_t len) } /* return the time zone offset between GMT and the input one, in number - of seconds or -1 if the timezone wasn't found/legal */ + of seconds or -1 if the timezone was not found/legal */ static int checktz(const char *check, size_t len) { @@ -265,7 +265,7 @@ static int checktz(const char *check, size_t len) static void skip(const char **date) { - /* skip everything that aren't letters or digits */ + /* skip everything that are not letters or digits */ while(**date && !ISALNUM(**date)) (*date)++; } @@ -277,7 +277,7 @@ enum assume { }; /* - * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to + * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to * mktime but for GMT only. */ static time_t time2epoch(int sec, int min, int hour, @@ -445,7 +445,7 @@ static int parsedate(const char *date, time_t *output) ((date[-1] == '+' || date[-1] == '-'))) { /* four digits and a value less than or equal to 1400 (to take into account all sorts of funny time zone diffs) and it is preceded - with a plus or minus. This is a time zone indication. 1400 is + with a plus or minus. This is a time zone indication. 1400 is picked since +1300 is frequently used and +1400 is mentioned as an edge number in the document "ISO C 200X Proposal: Timezone Functions" at http://david.tribble.com/text/c0xtimezone.html If @@ -521,13 +521,13 @@ static int parsedate(const char *date, time_t *output) #if (SIZEOF_TIME_T < 5) #ifdef HAVE_TIME_T_UNSIGNED - /* an unsigned 32 bit time_t can only hold dates to 2106 */ + /* an unsigned 32-bit time_t can only hold dates to 2106 */ if(yearnum > 2105) { *output = TIME_T_MAX; return PARSEDATE_LATER; } #else - /* a signed 32 bit time_t can only hold dates to the beginning of 2038 */ + /* a signed 32-bit time_t can only hold dates to the beginning of 2038 */ if(yearnum > 2037) { *output = TIME_T_MAX; return PARSEDATE_LATER; @@ -549,7 +549,7 @@ static int parsedate(const char *date, time_t *output) return PARSEDATE_FAIL; /* clearly an illegal date */ /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on - architectures that feature 64 bit 'long' but ultimately time_t is the + architectures that feature a 64 bits 'long' but ultimately time_t is the correct data type to use. */ t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum); diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index 81576c08c..817e3f69a 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c @@ -119,7 +119,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, interval_ms); if(block) { - /* if we didn't wait, we don't have to spend time on this now */ + /* if we did not wait, we do not have to spend time on this now */ if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else @@ -179,7 +179,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, DEBUGASSERT(pp->sendthis == NULL); if(!conn) - /* can't send without a connection! */ + /* cannot send without a connection! */ return CURLE_SEND_ERROR; Curl_dyn_reset(&pp->sendbuf); @@ -199,7 +199,8 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, &bytes_written); + result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, FALSE, + &bytes_written); if(result == CURLE_AGAIN) { bytes_written = 0; } @@ -285,94 +286,99 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; + ssize_t gotbytes; + char buffer[900]; *code = 0; /* 0 for errors or not done */ *size = 0; - if(pp->nfinal) { - /* a previous call left this many bytes in the beginning of the buffer as - that was the final line; now ditch that */ - size_t full = Curl_dyn_len(&pp->recvbuf); + do { + gotbytes = 0; + if(pp->nfinal) { + /* a previous call left this many bytes in the beginning of the buffer as + that was the final line; now ditch that */ + size_t full = Curl_dyn_len(&pp->recvbuf); - /* trim off the "final" leading part */ - Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal); + /* trim off the "final" leading part */ + Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal); - pp->nfinal = 0; /* now gone */ - } - if(!pp->overflow) { - ssize_t gotbytes = 0; - char buffer[900]; + pp->nfinal = 0; /* now gone */ + } + if(!pp->overflow) { + result = pingpong_read(data, sockindex, buffer, sizeof(buffer), + &gotbytes); + if(result == CURLE_AGAIN) + return CURLE_OK; - result = pingpong_read(data, sockindex, buffer, sizeof(buffer), &gotbytes); - if(result == CURLE_AGAIN) - return CURLE_OK; + if(result) + return result; - if(result) - return result; + if(gotbytes <= 0) { + failf(data, "response reading failed (errno: %d)", SOCKERRNO); + return CURLE_RECV_ERROR; + } - if(gotbytes <= 0) { - failf(data, "response reading failed (errno: %d)", SOCKERRNO); - return CURLE_RECV_ERROR; - } + result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes); + if(result) + return result; - result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes); - if(result) - return result; + data->req.headerbytecount += (unsigned int)gotbytes; - data->req.headerbytecount += (unsigned int)gotbytes; + pp->nread_resp += gotbytes; + } - pp->nread_resp += gotbytes; - } + do { + char *line = Curl_dyn_ptr(&pp->recvbuf); + char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf)); + if(nl) { + /* a newline is CRLF in pp-talk, so the CR is ignored as + the line is not really terminated until the LF comes */ + size_t length = nl - line + 1; - do { - char *line = Curl_dyn_ptr(&pp->recvbuf); - char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf)); - if(nl) { - /* a newline is CRLF in pp-talk, so the CR is ignored as - the line isn't really terminated until the LF comes */ - size_t length = nl - line + 1; - - /* output debug output if that is requested */ + /* output debug output if that is requested */ #ifdef HAVE_GSSAPI - if(!conn->sec_complete) + if(!conn->sec_complete) #endif - Curl_debug(data, CURLINFO_HEADER_IN, line, length); - - /* - * Pass all response-lines to the callback function registered for - * "headers". The response lines can be seen as a kind of headers. - */ - result = Curl_client_write(data, CLIENTWRITE_INFO, line, length); - if(result) - return result; - - if(pp->endofresp(data, conn, line, length, code)) { - /* When at "end of response", keep the endofresp line first in the - buffer since it will be accessed outside (by pingpong - parsers). Store the overflow counter to inform about additional - data in this buffer after the endofresp line. */ - pp->nfinal = length; + Curl_debug(data, CURLINFO_HEADER_IN, line, length); + + /* + * Pass all response-lines to the callback function registered for + * "headers". The response lines can be seen as a kind of headers. + */ + result = Curl_client_write(data, CLIENTWRITE_INFO, line, length); + if(result) + return result; + + if(pp->endofresp(data, conn, line, length, code)) { + /* When at "end of response", keep the endofresp line first in the + buffer since it will be accessed outside (by pingpong + parsers). Store the overflow counter to inform about additional + data in this buffer after the endofresp line. */ + pp->nfinal = length; + if(Curl_dyn_len(&pp->recvbuf) > length) + pp->overflow = Curl_dyn_len(&pp->recvbuf) - length; + else + pp->overflow = 0; + *size = pp->nread_resp; /* size of the response */ + pp->nread_resp = 0; /* restart */ + gotbytes = 0; /* force break out of outer loop */ + break; + } if(Curl_dyn_len(&pp->recvbuf) > length) - pp->overflow = Curl_dyn_len(&pp->recvbuf) - length; + /* keep the remaining piece */ + Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length); else - pp->overflow = 0; - *size = pp->nread_resp; /* size of the response */ - pp->nread_resp = 0; /* restart */ + Curl_dyn_reset(&pp->recvbuf); + } + else { + /* without a newline, there is no overflow */ + pp->overflow = 0; break; } - if(Curl_dyn_len(&pp->recvbuf) > length) - /* keep the remaining piece */ - Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length); - else - Curl_dyn_reset(&pp->recvbuf); - } - else { - /* without a newline, there is no overflow */ - pp->overflow = 0; - break; - } - } while(1); /* while there's buffer left to scan */ + } while(1); /* while there is buffer left to scan */ + + } while(gotbytes == sizeof(buffer)); pp->pending_resp = FALSE; @@ -394,6 +400,13 @@ int Curl_pp_getsock(struct Curl_easy *data, return GETSOCK_READSOCK(0); } +bool Curl_pp_needs_flush(struct Curl_easy *data, + struct pingpong *pp) +{ + (void)data; + return pp->sendleft > 0; +} + CURLcode Curl_pp_flushsend(struct Curl_easy *data, struct pingpong *pp) { @@ -401,9 +414,12 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, size_t written; CURLcode result; + if(!Curl_pp_needs_flush(data, pp)) + return CURLE_OK; + result = Curl_conn_send(data, FIRSTSOCKET, pp->sendthis + pp->sendsize - pp->sendleft, - pp->sendleft, &written); + pp->sendleft, FALSE, &written); if(result == CURLE_AGAIN) { result = CURLE_OK; written = 0; diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index 28172c728..72239ff05 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h @@ -37,7 +37,7 @@ struct connectdata; typedef enum { PPTRANSFER_BODY, /* yes do transfer a body */ PPTRANSFER_INFO, /* do still go through to get info/headers */ - PPTRANSFER_NONE /* don't get anything and don't get info */ + PPTRANSFER_NONE /* do not get anything and do not get info */ } curl_pp_transfer; /* @@ -83,7 +83,7 @@ struct pingpong { * Curl_pp_statemach() * * called repeatedly until done. Set 'wait' to make it wait a while on the - * socket if there's no traffic. + * socket if there is no traffic. */ CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp, bool block, bool disconnecting); @@ -137,6 +137,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, int *code, /* return the server code if done */ size_t *size); /* size of the response */ +bool Curl_pp_needs_flush(struct Curl_easy *data, + struct pingpong *pp); CURLcode Curl_pp_flushsend(struct Curl_easy *data, struct pingpong *pp); diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index 9a3033152..1f5334d91 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -83,6 +83,10 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef ARRAYSIZE +#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + /* Local API functions */ static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); static CURLcode pop3_do(struct Curl_easy *data, bool *done); @@ -107,6 +111,11 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); +/* This function scans the body after the end-of-body and writes everything + * until the end is found */ +static CURLcode pop3_write(struct Curl_easy *data, + const char *str, size_t nread, bool is_eos); + /* * POP3 protocol handler. */ @@ -125,7 +134,7 @@ const struct Curl_handler Curl_handler_pop3 = { ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ pop3_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ + pop3_write, /* write_resp */ ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -155,7 +164,7 @@ const struct Curl_handler Curl_handler_pop3s = { ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ pop3_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ + pop3_write, /* write_resp */ ZERO_NULL, /* write_resp_hd */ ZERO_NULL, /* connection_check */ ZERO_NULL, /* attach connection */ @@ -194,6 +203,53 @@ static void pop3_to_pop3s(struct connectdata *conn) #define pop3_to_pop3s(x) Curl_nop_stmt #endif +struct pop3_cmd { + const char *name; + unsigned short nlen; + BIT(multiline); /* response is multi-line with last '.' line */ + BIT(multiline_with_args); /* is multi-line when command has args */ +}; + +static const struct pop3_cmd pop3cmds[] = { + { "APOP", 4, FALSE, FALSE }, + { "AUTH", 4, FALSE, FALSE }, + { "CAPA", 4, TRUE, TRUE }, + { "DELE", 4, FALSE, FALSE }, + { "LIST", 4, TRUE, FALSE }, + { "MSG", 3, TRUE, TRUE }, + { "NOOP", 4, FALSE, FALSE }, + { "PASS", 4, FALSE, FALSE }, + { "QUIT", 4, FALSE, FALSE }, + { "RETR", 4, TRUE, TRUE }, + { "RSET", 4, FALSE, FALSE }, + { "STAT", 4, FALSE, FALSE }, + { "STLS", 4, FALSE, FALSE }, + { "TOP", 3, TRUE, TRUE }, + { "UIDL", 4, TRUE, FALSE }, + { "USER", 4, FALSE, FALSE }, + { "UTF8", 4, FALSE, FALSE }, + { "XTND", 4, TRUE, TRUE }, +}; + +/* Return iff a command is defined as "multi-line" (RFC 1939), + * has a response terminated by a last line with a '.'. + */ +static bool pop3_is_multiline(const char *cmdline) +{ + size_t i; + for(i = 0; i < ARRAYSIZE(pop3cmds); ++i) { + if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { + if(!cmdline[pop3cmds[i].nlen]) + return pop3cmds[i].multiline; + else if(cmdline[pop3cmds[i].nlen] == ' ') + return pop3cmds[i].multiline_with_args; + } + } + /* Unknown command, assume multi-line for backward compatibility with + * earlier curl versions that only could do multi-line responses. */ + return TRUE; +} + /*********************************************************************** * * pop3_endofresp() @@ -406,7 +462,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, CURLcode result = CURLE_OK; /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ + connect phase if we do not */ if(!data->state.aptr.user) { pop3_state(data, POP3_STOP); @@ -440,7 +496,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, char secret[2 * MD5_DIGEST_LEN + 1]; /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ + connect phase if we do not */ if(!data->state.aptr.user) { pop3_state(data, POP3_STOP); @@ -550,7 +606,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, saslprogress progress = SASL_IDLE; /* Check we have enough data to authenticate with and end the - connect phase if we don't */ + connect phase if we do not */ if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { pop3_state(data, POP3_STOP); return result; @@ -609,18 +665,20 @@ static CURLcode pop3_perform_command(struct Curl_easy *data) else command = "RETR"; + if(pop3->custom && pop3->custom[0] != '\0') + command = pop3->custom; + /* Send the command */ if(pop3->id[0] != '\0') result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s", - (pop3->custom && pop3->custom[0] != '\0' ? - pop3->custom : command), pop3->id); + command, pop3->id); else - result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", - (pop3->custom && pop3->custom[0] != '\0' ? - pop3->custom : command)); + result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", command); - if(!result) + if(!result) { pop3_state(data, POP3_COMMAND); + data->req.no_body = !pop3_is_multiline(command); + } return result; } @@ -758,7 +816,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, } } else { - /* Clear text is supported when CAPA isn't recognised */ + /* Clear text is supported when CAPA is not recognised */ if(pop3code != '+') pop3c->authtypes |= POP3_TYPE_CLEARTEXT; @@ -931,12 +989,12 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, pop3c->eob = 2; /* But since this initial CR LF pair is not part of the actual body, we set - the strip counter here so that these bytes won't be delivered. */ + the strip counter here so that these bytes will not be delivered. */ pop3c->strip = 2; if(pop3->transfer == PPTRANSFER_BODY) { /* POP3 download */ - Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); if(pp->overflow) { /* The recv buffer contains data that is actually body content so send @@ -948,8 +1006,8 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, pp->nfinal = 0; /* done */ if(!data->req.no_body) { - result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf), - Curl_dyn_len(&pp->recvbuf)); + result = pop3_write(data, Curl_dyn_ptr(&pp->recvbuf), + Curl_dyn_len(&pp->recvbuf), FALSE); if(result) return result; } @@ -1447,12 +1505,13 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data) /*********************************************************************** * - * Curl_pop3_write() + * pop3_write() * * This function scans the body after the end-of-body and writes everything * until the end is found. */ -CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) +static CURLcode pop3_write(struct Curl_easy *data, const char *str, + size_t nread, bool is_eos) { /* This code could be made into a special function in the handler struct */ CURLcode result = CURLE_OK; @@ -1462,6 +1521,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) bool strip_dot = FALSE; size_t last = 0; size_t i; + (void)is_eos; /* Search through the buffer looking for the end-of-body marker which is 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches @@ -1477,7 +1537,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) pop3c->eob++; if(i) { - /* Write out the body part that didn't match */ + /* Write out the body part that did not match */ result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], i - last); @@ -1490,7 +1550,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) else if(pop3c->eob == 3) pop3c->eob++; else - /* If the character match wasn't at position 0 or 3 then restart the + /* If the character match was not at position 0 or 3 then restart the pattern matching */ pop3c->eob = 1; break; @@ -1499,7 +1559,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) if(pop3c->eob == 1 || pop3c->eob == 4) pop3c->eob++; else - /* If the character match wasn't at position 1 or 4 then start the + /* If the character match was not at position 1 or 4 then start the search again */ pop3c->eob = 0; break; @@ -1513,7 +1573,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread) pop3c->eob = 0; } else - /* If the character match wasn't at position 2 then start the search + /* If the character match was not at position 2 then start the search again */ pop3c->eob = 0; break; diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index e8e98db04..3d08dafa1 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h @@ -90,9 +90,4 @@ extern const struct Curl_handler Curl_handler_pop3s; #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" #define POP3_EOB_LEN 5 -/* This function scans the body after the end-of-body and writes everything - * until the end is found */ -CURLcode Curl_pop3_write(struct Curl_easy *data, - const char *str, size_t nread); - #endif /* HEADER_CURL_POP3_H */ diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index d05fcc3eb..cb9829c31 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c @@ -48,8 +48,7 @@ static void time2str(char *r, curl_off_t seconds) if(h <= CURL_OFF_T_C(99)) { curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60); curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60)); - msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T - ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s); + msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T, h, m, s); } else { /* this equals to more than 99 hours, switch to a more suitable output @@ -57,10 +56,9 @@ static void time2str(char *r, curl_off_t seconds) curl_off_t d = seconds / CURL_OFF_T_C(86400); h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600); if(d <= CURL_OFF_T_C(999)) - msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T - "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h); + msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h); else - msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d); + msnprintf(r, 9, "%7" FMT_OFF_T "d", d); } } @@ -76,40 +74,40 @@ static char *max5data(curl_off_t bytes, char *max5) #define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE) if(bytes < CURL_OFF_T_C(100000)) - msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes); + msnprintf(max5, 6, "%5" FMT_OFF_T, bytes); else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE) - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE); + msnprintf(max5, 6, "%4" FMT_OFF_T "k", bytes/ONE_KILOBYTE); else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE) - /* 'XX.XM' is good as long as we're less than 100 megs */ - msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" - CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, + /* 'XX.XM' is good as long as we are less than 100 megs */ + msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" + FMT_OFF_T "M", bytes/ONE_MEGABYTE, (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) - /* 'XXXXM' is good until we're at 10000MB or above */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); + /* 'XXXXM' is good until we are at 10000MB or above */ + msnprintf(max5, 6, "%4" FMT_OFF_T "M", bytes/ONE_MEGABYTE); else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE) /* 10000 MB - 100 GB, we show it as XX.XG */ - msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" - CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE, + msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" + FMT_OFF_T "G", bytes/ONE_GIGABYTE, (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) ); else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE) /* up to 10000GB, display without decimal: XXXXG */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE); + msnprintf(max5, 6, "%4" FMT_OFF_T "G", bytes/ONE_GIGABYTE); else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE) /* up to 10000TB, display without decimal: XXXXT */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE); + msnprintf(max5, 6, "%4" FMT_OFF_T "T", bytes/ONE_TERABYTE); else /* up to 10000PB, display without decimal: XXXXP */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); + msnprintf(max5, 6, "%4" FMT_OFF_T "P", bytes/ONE_PETABYTE); - /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can + /* 16384 petabytes (16 exabytes) is the maximum a 64-bit unsigned number can hold, but our data type is signed so 8192PB will be the maximum. */ return max5; @@ -140,7 +138,7 @@ int Curl_pgrsDone(struct Curl_easy *data) if(!(data->progress.flags & PGRS_HIDE) && !data->progress.callback) - /* only output if we don't use a progress callback and we're not + /* only output if we do not use a progress callback and we are not * hidden */ fprintf(data->set.err, "\n"); @@ -204,7 +202,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, case TIMER_STARTTRANSFER: delta = &data->progress.t_starttransfer; /* prevent updating t_starttransfer unless: - * 1) this is the first time we're setting t_starttransfer + * 1) this is the first time we are setting t_starttransfer * 2) a redirect has occurred since the last time t_starttransfer was set * This prevents repeated invocations of the function from incorrectly * changing the t_starttransfer time. @@ -217,7 +215,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, break; } case TIMER_POSTRANSFER: - /* this is the normal end-of-transfer thing */ + delta = &data->progress.t_posttransfer; break; case TIMER_REDIRECT: data->progress.t_redirect = Curl_timediff_us(timestamp, @@ -252,12 +250,12 @@ void Curl_pgrsStartNow(struct Curl_easy *data) data->progress.speeder_c = 0; /* reset the progress meter display */ data->progress.start = Curl_now(); data->progress.is_t_startransfer_set = false; - data->progress.ul_limit_start = data->progress.start; - data->progress.dl_limit_start = data->progress.start; - data->progress.ul_limit_size = 0; - data->progress.dl_limit_size = 0; - data->progress.downloaded = 0; - data->progress.uploaded = 0; + data->progress.ul.limit.start = data->progress.start; + data->progress.dl.limit.start = data->progress.start; + data->progress.ul.limit.start_size = 0; + data->progress.dl.limit.start_size = 0; + data->progress.dl.cur_size = 0; + data->progress.ul.cur_size = 0; /* clear all bits except HIDE and HEADERS_OUT */ data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT; Curl_ratelimit(data, data->progress.start); @@ -265,11 +263,11 @@ void Curl_pgrsStartNow(struct Curl_easy *data) /* * This is used to handle speed limits, calculating how many milliseconds to - * wait until we're back under the speed limit, if needed. + * wait until we are back under the speed limit, if needed. * * The way it works is by having a "starting point" (time & amount of data * transferred by then) used in the speed computation, to be used instead of - * the start of the transfer. This starting point is regularly moved as + * the start of the transfer. This starting point is regularly moved as * transfer goes on, to keep getting accurate values (instead of average over * the entire transfer). * @@ -281,17 +279,15 @@ void Curl_pgrsStartNow(struct Curl_easy *data) * starting point should be reset (to current); or the number of milliseconds * to wait to get back under the speed limit. */ -timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, - curl_off_t startsize, - curl_off_t limit, - struct curltime start, +timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, + curl_off_t speed_limit, struct curltime now) { - curl_off_t size = cursize - startsize; + curl_off_t size = d->cur_size - d->limit.start_size; timediff_t minimum; timediff_t actual; - if(!limit || !size) + if(!speed_limit || !size) return 0; /* @@ -299,9 +295,9 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, * stay below 'limit'. */ if(size < CURL_OFF_T_MAX/1000) - minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit); + minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / speed_limit); else { - minimum = (timediff_t) (size / limit); + minimum = (timediff_t) (size / speed_limit); if(minimum < TIMEDIFF_T_MAX/1000) minimum *= 1000; else @@ -312,7 +308,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, * 'actual' is the time in milliseconds it took to actually download the * last 'size' bytes. */ - actual = Curl_timediff_ceil(now, start); + actual = Curl_timediff_ceil(now, d->limit.start); if(actual < minimum) { /* if it downloaded the data faster than the limit, make it wait the difference */ @@ -327,7 +323,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, */ CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) { - data->progress.downloaded = size; + data->progress.dl.cur_size = size; return CURLE_OK; } @@ -336,19 +332,19 @@ CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) */ void Curl_ratelimit(struct Curl_easy *data, struct curltime now) { - /* don't set a new stamp unless the time since last update is long enough */ + /* do not set a new stamp unless the time since last update is long enough */ if(data->set.max_recv_speed) { - if(Curl_timediff(now, data->progress.dl_limit_start) >= + if(Curl_timediff(now, data->progress.dl.limit.start) >= MIN_RATE_LIMIT_PERIOD) { - data->progress.dl_limit_start = now; - data->progress.dl_limit_size = data->progress.downloaded; + data->progress.dl.limit.start = now; + data->progress.dl.limit.start_size = data->progress.dl.cur_size; } } if(data->set.max_send_speed) { - if(Curl_timediff(now, data->progress.ul_limit_start) >= + if(Curl_timediff(now, data->progress.ul.limit.start) >= MIN_RATE_LIMIT_PERIOD) { - data->progress.ul_limit_start = now; - data->progress.ul_limit_size = data->progress.uploaded; + data->progress.ul.limit.start = now; + data->progress.ul.limit.start_size = data->progress.ul.cur_size; } } } @@ -358,17 +354,17 @@ void Curl_ratelimit(struct Curl_easy *data, struct curltime now) */ void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size) { - data->progress.uploaded = size; + data->progress.ul.cur_size = size; } void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size) { if(size >= 0) { - data->progress.size_dl = size; + data->progress.dl.total_size = size; data->progress.flags |= PGRS_DL_SIZE_KNOWN; } else { - data->progress.size_dl = 0; + data->progress.dl.total_size = 0; data->progress.flags &= ~PGRS_DL_SIZE_KNOWN; } } @@ -376,11 +372,11 @@ void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size) void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) { if(size >= 0) { - data->progress.size_ul = size; + data->progress.ul.total_size = size; data->progress.flags |= PGRS_UL_SIZE_KNOWN; } else { - data->progress.size_ul = 0; + data->progress.ul.total_size = 0; data->progress.flags &= ~PGRS_UL_SIZE_KNOWN; } } @@ -399,7 +395,7 @@ static curl_off_t trspeed(curl_off_t size, /* number of bytes */ return CURL_OFF_T_MAX; } -/* returns TRUE if it's time to show the progress meter */ +/* returns TRUE if it is time to show the progress meter */ static bool progress_calc(struct Curl_easy *data, struct curltime now) { bool timetoshow = FALSE; @@ -407,8 +403,8 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) /* The time spent so far (from the start) in microseconds */ p->timespent = Curl_timediff_us(now, p->start); - p->dlspeed = trspeed(p->downloaded, p->timespent); - p->ulspeed = trspeed(p->uploaded, p->timespent); + p->dl.speed = trspeed(p->dl.cur_size, p->timespent); + p->ul.speed = trspeed(p->ul.cur_size, p->timespent); /* Calculations done at most once a second, unless end is reached */ if(p->lastshow != now.tv_sec) { @@ -419,7 +415,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) /* Let's do the "current speed" thing, with the dl + ul speeds combined. Store the speed at entry 'nowindex'. */ - p->speeder[ nowindex ] = p->downloaded + p->uploaded; + p->speeder[ nowindex ] = p->dl.cur_size + p->ul.cur_size; /* remember the exact time for this moment */ p->speeder_time [ nowindex ] = now; @@ -431,10 +427,10 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) /* figure out how many index entries of data we have stored in our speeder array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of transfer. Imagine, after one second we have filled in two entries, - after two seconds we've filled in three entries etc. */ + after two seconds we have filled in three entries etc. */ countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1; - /* first of all, we don't do this if there's no counted seconds yet */ + /* first of all, we do not do this if there is no counted seconds yet */ if(countindex) { int checkindex; timediff_t span_ms; @@ -465,113 +461,107 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) } else /* the first second we use the average */ - p->current_speed = p->ulspeed + p->dlspeed; + p->current_speed = p->ul.speed + p->dl.speed; } /* Calculations end */ return timetoshow; } #ifndef CURL_DISABLE_PROGRESS_METER + +struct pgrs_estimate { + curl_off_t secs; + curl_off_t percent; +}; + +static curl_off_t pgrs_est_percent(curl_off_t total, curl_off_t cur) +{ + if(total > CURL_OFF_T_C(10000)) + return cur / (total/CURL_OFF_T_C(100)); + else if(total > CURL_OFF_T_C(0)) + return (cur*100) / total; + return 0; +} + +static void pgrs_estimates(struct pgrs_dir *d, + bool total_known, + struct pgrs_estimate *est) +{ + est->secs = 0; + est->percent = 0; + if(total_known && (d->speed > CURL_OFF_T_C(0))) { + est->secs = d->total_size / d->speed; + est->percent = pgrs_est_percent(d->total_size, d->cur_size); + } +} + static void progress_meter(struct Curl_easy *data) { + struct Progress *p = &data->progress; char max5[6][10]; - curl_off_t dlpercen = 0; - curl_off_t ulpercen = 0; - curl_off_t total_percen = 0; - curl_off_t total_transfer; - curl_off_t total_expected_transfer; + struct pgrs_estimate dl_estm; + struct pgrs_estimate ul_estm; + struct pgrs_estimate total_estm; + curl_off_t total_cur_size; + curl_off_t total_expected_size; char time_left[10]; char time_total[10]; char time_spent[10]; - curl_off_t ulestimate = 0; - curl_off_t dlestimate = 0; - curl_off_t total_estimate; - curl_off_t timespent = - (curl_off_t)data->progress.timespent/1000000; /* seconds */ + curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */ - if(!(data->progress.flags & PGRS_HEADERS_OUT)) { + if(!(p->flags & PGRS_HEADERS_OUT)) { if(data->state.resume_from) { fprintf(data->set.err, - "** Resuming transfer from byte position %" - CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); + "** Resuming transfer from byte position %" FMT_OFF_T "\n", + data->state.resume_from); } fprintf(data->set.err, " %% Total %% Received %% Xferd Average Speed " "Time Time Time Current\n" " Dload Upload " "Total Spent Left Speed\n"); - data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ + p->flags |= PGRS_HEADERS_OUT; /* headers are shown */ } - /* Figure out the estimated time of arrival for the upload */ - if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && - (data->progress.ulspeed > CURL_OFF_T_C(0))) { - ulestimate = data->progress.size_ul / data->progress.ulspeed; - - if(data->progress.size_ul > CURL_OFF_T_C(10000)) - ulpercen = data->progress.uploaded / - (data->progress.size_ul/CURL_OFF_T_C(100)); - else if(data->progress.size_ul > CURL_OFF_T_C(0)) - ulpercen = (data->progress.uploaded*100) / - data->progress.size_ul; - } - - /* ... and the download */ - if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && - (data->progress.dlspeed > CURL_OFF_T_C(0))) { - dlestimate = data->progress.size_dl / data->progress.dlspeed; - - if(data->progress.size_dl > CURL_OFF_T_C(10000)) - dlpercen = data->progress.downloaded / - (data->progress.size_dl/CURL_OFF_T_C(100)); - else if(data->progress.size_dl > CURL_OFF_T_C(0)) - dlpercen = (data->progress.downloaded*100) / - data->progress.size_dl; - } - - /* Now figure out which of them is slower and use that one for the - total estimate! */ - total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; + /* Figure out the estimated time of arrival for upload and download */ + pgrs_estimates(&p->ul, (p->flags & PGRS_UL_SIZE_KNOWN), &ul_estm); + pgrs_estimates(&p->dl, (p->flags & PGRS_DL_SIZE_KNOWN), &dl_estm); + /* Since both happen at the same time, total expected duration is max. */ + total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs); /* create the three time strings */ - time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); - time2str(time_total, total_estimate); - time2str(time_spent, timespent); + time2str(time_left, total_estm.secs > 0?(total_estm.secs - cur_secs):0); + time2str(time_total, total_estm.secs); + time2str(time_spent, cur_secs); /* Get the total amount of data expected to get transferred */ - total_expected_transfer = - ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? - data->progress.size_ul:data->progress.uploaded)+ - ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? - data->progress.size_dl:data->progress.downloaded); + total_expected_size = + ((p->flags & PGRS_UL_SIZE_KNOWN)? p->ul.total_size:p->ul.cur_size) + + ((p->flags & PGRS_DL_SIZE_KNOWN)? p->dl.total_size:p->dl.cur_size); /* We have transferred this much so far */ - total_transfer = data->progress.downloaded + data->progress.uploaded; + total_cur_size = p->dl.cur_size + p->ul.cur_size; /* Get the percentage of data transferred so far */ - if(total_expected_transfer > CURL_OFF_T_C(10000)) - total_percen = total_transfer / - (total_expected_transfer/CURL_OFF_T_C(100)); - else if(total_expected_transfer > CURL_OFF_T_C(0)) - total_percen = (total_transfer*100) / total_expected_transfer; + total_estm.percent = pgrs_est_percent(total_expected_size, total_cur_size); fprintf(data->set.err, "\r" - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", - total_percen, /* 3 letters */ /* total % */ - max5data(total_expected_transfer, max5[2]), /* total size */ - dlpercen, /* 3 letters */ /* rcvd % */ - max5data(data->progress.downloaded, max5[0]), /* rcvd size */ - ulpercen, /* 3 letters */ /* xfer % */ - max5data(data->progress.uploaded, max5[1]), /* xfer size */ - max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ - max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ + "%3" FMT_OFF_T " %s " + "%3" FMT_OFF_T " %s " + "%3" FMT_OFF_T " %s %s %s %s %s %s %s", + total_estm.percent, /* 3 letters */ /* total % */ + max5data(total_expected_size, max5[2]), /* total size */ + dl_estm.percent, /* 3 letters */ /* rcvd % */ + max5data(p->dl.cur_size, max5[0]), /* rcvd size */ + ul_estm.percent, /* 3 letters */ /* xfer % */ + max5data(p->ul.cur_size, max5[1]), /* xfer size */ + max5data(p->dl.speed, max5[3]), /* avrg dl speed */ + max5data(p->ul.speed, max5[4]), /* avrg ul speed */ time_total, /* 8 letters */ /* total time */ time_spent, /* 8 letters */ /* time spent */ time_left, /* 8 letters */ /* time left */ - max5data(data->progress.current_speed, max5[5]) + max5data(p->current_speed, max5[5]) ); /* we flush the output stream to make it appear as soon as possible */ @@ -587,20 +577,18 @@ static void progress_meter(struct Curl_easy *data) * Curl_pgrsUpdate() returns 0 for success or the value returned by the * progress callback! */ -int Curl_pgrsUpdate(struct Curl_easy *data) +static int pgrsupdate(struct Curl_easy *data, bool showprogress) { - struct curltime now = Curl_now(); /* what time is it */ - bool showprogress = progress_calc(data, now); if(!(data->progress.flags & PGRS_HIDE)) { if(data->set.fxferinfo) { int result; - /* There's a callback set, call that */ + /* There is a callback set, call that */ Curl_set_in_callback(data, true); result = data->set.fxferinfo(data->set.progress_client, - data->progress.size_dl, - data->progress.downloaded, - data->progress.size_ul, - data->progress.uploaded); + data->progress.dl.total_size, + data->progress.dl.cur_size, + data->progress.ul.total_size, + data->progress.ul.cur_size); Curl_set_in_callback(data, false); if(result != CURL_PROGRESSFUNC_CONTINUE) { if(result) @@ -613,10 +601,10 @@ int Curl_pgrsUpdate(struct Curl_easy *data) /* The older deprecated callback is set, call that */ Curl_set_in_callback(data, true); result = data->set.fprogress(data->set.progress_client, - (double)data->progress.size_dl, - (double)data->progress.downloaded, - (double)data->progress.size_ul, - (double)data->progress.uploaded); + (double)data->progress.dl.total_size, + (double)data->progress.dl.cur_size, + (double)data->progress.ul.total_size, + (double)data->progress.ul.cur_size); Curl_set_in_callback(data, false); if(result != CURL_PROGRESSFUNC_CONTINUE) { if(result) @@ -631,3 +619,19 @@ int Curl_pgrsUpdate(struct Curl_easy *data) return 0; } + +int Curl_pgrsUpdate(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); /* what time is it */ + bool showprogress = progress_calc(data, now); + return pgrsupdate(data, showprogress); +} + +/* + * Update all progress, do not do progress meter/callbacks. + */ +void Curl_pgrsUpdate_nometer(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); /* what time is it */ + (void)progress_calc(data, now); +} diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h index 73749419a..04a8f5bce 100644 --- a/Utilities/cmcurl/lib/progress.h +++ b/Utilities/cmcurl/lib/progress.h @@ -54,12 +54,12 @@ CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); void Curl_ratelimit(struct Curl_easy *data, struct curltime now); int Curl_pgrsUpdate(struct Curl_easy *data); +void Curl_pgrsUpdate_nometer(struct Curl_easy *data); + void Curl_pgrsResetTransferSizes(struct Curl_easy *data); struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer); -timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, - curl_off_t startsize, - curl_off_t limit, - struct curltime start, +timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, + curl_off_t speed_limit, struct curltime now); /** * Update progress timer with the elapsed time from its start to `timestamp`. diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index c62b1a403..63aebdc8f 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -48,7 +48,8 @@ #ifdef _WIN32 -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \ + !defined(CURL_WINDOWS_APP) # define HAVE_WIN_BCRYPTGENRANDOM # include # ifdef _MSC_VER @@ -99,86 +100,91 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length) } #endif -static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) +#if !defined(USE_SSL) +/* ---- possibly non-cryptographic version following ---- */ +static CURLcode weak_random(struct Curl_easy *data, + unsigned char *entropy, + size_t length) /* always 4, size of int */ { - CURLcode result = CURLE_OK; - static unsigned int randseed; - static bool seeded = FALSE; - -#ifdef CURLDEBUG - char *force_entropy = getenv("CURL_ENTROPY"); - if(force_entropy) { - if(!seeded) { - unsigned int seed = 0; - size_t elen = strlen(force_entropy); - size_t clen = sizeof(seed); - size_t min = elen < clen ? elen : clen; - memcpy((char *)&seed, force_entropy, min); - randseed = ntohl(seed); - seeded = TRUE; - } - else - randseed++; - *rnd = randseed; - return CURLE_OK; - } -#endif - - /* data may be NULL! */ - result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd)); - if(result != CURLE_NOT_BUILT_IN) - /* only if there is no random function in the TLS backend do the non crypto - version, otherwise return result */ - return result; - - /* ---- non-cryptographic version following ---- */ + unsigned int r; + DEBUGASSERT(length == sizeof(int)); + /* Trying cryptographically secure functions first */ #ifdef _WIN32 - if(!seeded) { - result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd)); + (void)data; + { + CURLcode result = Curl_win32_random(entropy, length); if(result != CURLE_NOT_BUILT_IN) return result; } #endif -#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL) - if(!seeded) { - *rnd = (unsigned int)arc4random(); - return CURLE_OK; +#if defined(HAVE_ARC4RANDOM) + (void)data; + r = (unsigned int)arc4random(); + memcpy(entropy, &r, length); +#else + infof(data, "WARNING: using weak random seed"); + { + static unsigned int randseed; + static bool seeded = FALSE; + unsigned int rnd; + if(!seeded) { + struct curltime now = Curl_now(); + randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + randseed = randseed * 1103515245 + 12345; + seeded = TRUE; + } + + /* Return an unsigned 32-bit pseudo-random number. */ + r = randseed = randseed * 1103515245 + 12345; + rnd = (r << 16) | ((r >> 16) & 0xFFFF); + memcpy(entropy, &rnd, length); } +#endif + return CURLE_OK; +} +#endif + +#ifdef USE_SSL +#define _random(x,y,z) Curl_ssl_random(x,y,z) +#else +#define _random(x,y,z) weak_random(x,y,z) #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); - if(fd > -1) { - /* read random data into the randseed variable */ - ssize_t nread = read(fd, &randseed, sizeof(randseed)); - if(nread == sizeof(randseed)) +static CURLcode randit(struct Curl_easy *data, unsigned int *rnd, + bool env_override) +{ +#ifdef DEBUGBUILD + if(env_override) { + char *force_entropy = getenv("CURL_ENTROPY"); + if(force_entropy) { + static unsigned int randseed; + static bool seeded = FALSE; + + if(!seeded) { + unsigned int seed = 0; + size_t elen = strlen(force_entropy); + size_t clen = sizeof(seed); + size_t min = elen < clen ? elen : clen; + memcpy((char *)&seed, force_entropy, min); + randseed = ntohl(seed); seeded = TRUE; - close(fd); + } + else + randseed++; + *rnd = randseed; + return CURLE_OK; } } +#else + (void)env_override; #endif - if(!seeded) { - struct curltime now = Curl_now(); - infof(data, "WARNING: using weak random seed"); - randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; - randseed = randseed * 1103515245 + 12345; - randseed = randseed * 1103515245 + 12345; - randseed = randseed * 1103515245 + 12345; - seeded = TRUE; - } - - { - unsigned int r; - /* Return an unsigned 32-bit pseudo-random number. */ - r = randseed = randseed * 1103515245 + 12345; - *rnd = (r << 16) | ((r >> 16) & 0xFFFF); - } - return CURLE_OK; + /* data may be NULL! */ + return _random(data, (unsigned char *)rnd, sizeof(*rnd)); } /* @@ -186,7 +192,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) * 'rnd' points to. * * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (rustls or mbedTLS), this function will use "weak" + * proper random API (Rustls or mbedTLS), this function will use "weak" * random. * * When built *with* TLS support and a backend that offers strong random, it @@ -197,9 +203,16 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) * */ -CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num) +CURLcode Curl_rand_bytes(struct Curl_easy *data, +#ifdef DEBUGBUILD + bool env_override, +#endif + unsigned char *rnd, size_t num) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef DEBUGBUILD + const bool env_override = FALSE; +#endif DEBUGASSERT(num); @@ -207,7 +220,7 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num) unsigned int r; size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int); - result = randit(data, &r); + result = randit(data, &r, env_override); if(result) return result; @@ -269,7 +282,7 @@ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num) { CURLcode result = CURLE_OK; - const int alnumspace = sizeof(alnum) - 1; + const unsigned int alnumspace = sizeof(alnum) - 1; unsigned int r; DEBUGASSERT(num > 1); @@ -277,12 +290,12 @@ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, while(num) { do { - result = randit(data, &r); + result = randit(data, &r, TRUE); if(result) return result; } while(r >= (UINT_MAX - UINT_MAX % alnumspace)); - *rnd++ = alnum[r % alnumspace]; + *rnd++ = (unsigned char)alnum[r % alnumspace]; num--; } *rnd = 0; diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index bc05239e4..2ba60e729 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -24,7 +24,17 @@ * ***************************************************************************/ -CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num); +CURLcode Curl_rand_bytes(struct Curl_easy *data, +#ifdef DEBUGBUILD + bool allow_env_override, +#endif + unsigned char *rnd, size_t num); + +#ifdef DEBUGBUILD +#define Curl_rand(a,b,c) Curl_rand_bytes((a), TRUE, (b), (c)) +#else +#define Curl_rand(a,b,c) Curl_rand_bytes((a), (b), (c)) +#endif /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c index 4c8869806..8715a4306 100644 --- a/Utilities/cmcurl/lib/rename.c +++ b/Utilities/cmcurl/lib/rename.c @@ -41,7 +41,7 @@ int Curl_rename(const char *oldpath, const char *newpath) { #ifdef _WIN32 - /* rename() on Windows doesn't overwrite, so we can't use it here. + /* rename() on Windows does not overwrite, so we cannot use it here. MoveFileEx() will overwrite and is usually atomic, however it fails when there are open handles to the file. */ const int max_wait_ms = 1000; diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c index 26741fa6c..1ddbdc9d0 100644 --- a/Utilities/cmcurl/lib/request.c +++ b/Utilities/cmcurl/lib/request.c @@ -52,8 +52,13 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->done = FALSE; req->upload_done = FALSE; + req->upload_aborted = FALSE; req->download_done = FALSE; + req->eos_written = FALSE; + req->eos_read = FALSE; + req->eos_sent = FALSE; req->ignorebody = FALSE; + req->shutdown = FALSE; req->bytecount = 0; req->writebytecount = 0; req->header = TRUE; /* assume header */ @@ -99,6 +104,9 @@ CURLcode Curl_req_done(struct SingleRequest *req, if(!aborted) (void)req_flush(data); Curl_client_reset(data); +#ifndef CURL_DISABLE_DOH + Curl_doh_close(data); +#endif return CURLE_OK; } @@ -108,17 +116,14 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) /* This is a bit ugly. `req->p` is a union and we assume we can * free this safely without leaks. */ - Curl_safefree(req->p.http); + Curl_safefree(req->p.ftp); Curl_safefree(req->newurl); Curl_client_reset(data); if(req->sendbuf_init) Curl_bufq_reset(&req->sendbuf); #ifndef CURL_DISABLE_DOH - if(req->doh) { - Curl_close(&req->doh->probe[0].easy); - Curl_close(&req->doh->probe[1].easy); - } + Curl_doh_close(data); #endif /* Can no longer memset() this struct as we need to keep some state */ req->size = -1; @@ -135,7 +140,6 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->keepon = 0; req->upgr101 = UPGR101_INIT; req->timeofdoc = 0; - req->bodywrites = 0; req->location = NULL; req->newurl = NULL; #ifndef CURL_DISABLE_COOKIES @@ -146,6 +150,7 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->download_done = FALSE; req->eos_written = FALSE; req->eos_read = FALSE; + req->eos_sent = FALSE; req->upload_done = FALSE; req->upload_aborted = FALSE; req->ignorebody = FALSE; @@ -156,27 +161,24 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->getheader = FALSE; req->no_body = data->set.opt_no_body; req->authneg = FALSE; + req->shutdown = FALSE; +#ifdef USE_HYPER + req->bodywritten = FALSE; +#endif } void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { /* This is a bit ugly. `req->p` is a union and we assume we can * free this safely without leaks. */ - Curl_safefree(req->p.http); + Curl_safefree(req->p.ftp); Curl_safefree(req->newurl); if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); Curl_client_cleanup(data); #ifndef CURL_DISABLE_DOH - if(req->doh) { - Curl_close(&req->doh->probe[0].easy); - Curl_close(&req->doh->probe[1].easy); - Curl_dyn_free(&req->doh->probe[0].serverdoh); - Curl_dyn_free(&req->doh->probe[1].serverdoh); - curl_slist_free_all(req->doh->headers); - Curl_safefree(req->doh); - } + Curl_doh_cleanup(data); #endif } @@ -185,22 +187,24 @@ static CURLcode xfer_send(struct Curl_easy *data, size_t hds_len, size_t *pnwritten) { CURLcode result = CURLE_OK; + bool eos = FALSE; *pnwritten = 0; -#ifdef CURLDEBUG + DEBUGASSERT(hds_len <= blen); +#ifdef DEBUGBUILD { /* Allow debug builds to override this logic to force short initial - sends - */ + sends */ + size_t body_len = blen - hds_len; char *p = getenv("CURL_SMALLREQSEND"); if(p) { - size_t altsize = (size_t)strtoul(p, NULL, 10); - if(altsize && altsize < blen) - blen = altsize; + size_t body_small = (size_t)strtoul(p, NULL, 10); + if(body_small && body_small < body_len) + blen = hds_len + body_small; } } #endif - /* Make sure this doesn't send more body bytes than what the max send + /* Make sure this does not send more body bytes than what the max send speed says. The headers do not count to the max speed. */ if(data->set.max_send_speed) { size_t body_bytes = blen - hds_len; @@ -208,16 +212,26 @@ static CURLcode xfer_send(struct Curl_easy *data, blen = hds_len + (size_t)data->set.max_send_speed; } - result = Curl_xfer_send(data, buf, blen, pnwritten); - if(!result && *pnwritten) { - if(hds_len) - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, - CURLMIN(hds_len, *pnwritten)); - if(*pnwritten > hds_len) { - size_t body_len = *pnwritten - hds_len; - Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len); - data->req.writebytecount += body_len; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + if(data->req.eos_read && + (Curl_bufq_is_empty(&data->req.sendbuf) || + Curl_bufq_len(&data->req.sendbuf) == blen)) { + DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen)); + eos = TRUE; + } + result = Curl_xfer_send(data, buf, blen, eos, pnwritten); + if(!result) { + if(eos && (blen == *pnwritten)) + data->req.eos_sent = TRUE; + if(*pnwritten) { + if(hds_len) + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf, + CURLMIN(hds_len, *pnwritten)); + if(*pnwritten > hds_len) { + size_t body_len = *pnwritten - hds_len; + Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len); + data->req.writebytecount += body_len; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + } } } return result; @@ -247,28 +261,32 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data) return result; } -static CURLcode req_set_upload_done(struct Curl_easy *data) +CURLcode Curl_req_set_upload_done(struct Curl_easy *data) { DEBUGASSERT(!data->req.upload_done); data->req.upload_done = TRUE; - data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we're done sending */ + data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */ + Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); if(data->req.upload_aborted) { + Curl_bufq_reset(&data->req.sendbuf); if(data->req.writebytecount) - infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T - " bytes", data->req.writebytecount); + infof(data, "abort upload after having sent %" FMT_OFF_T " bytes", + data->req.writebytecount); else infof(data, "abort upload"); } else if(data->req.writebytecount) - infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T - " bytes", data->req.writebytecount); - else if(!data->req.download_done) + infof(data, "upload completely sent off: %" FMT_OFF_T " bytes", + data->req.writebytecount); + else if(!data->req.download_done) { + DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf)); infof(data, Curl_creader_total_length(data)? "We are completely uploaded and fine" : "Request completely sent off"); + } return Curl_xfer_send_close(data); } @@ -285,13 +303,36 @@ static CURLcode req_flush(struct Curl_easy *data) if(result) return result; if(!Curl_bufq_is_empty(&data->req.sendbuf)) { + DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN", + Curl_bufq_len(&data->req.sendbuf))); return CURLE_AGAIN; } } + else if(Curl_xfer_needs_flush(data)) { + DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending")); + return Curl_xfer_flush(data); + } - if(!data->req.upload_done && data->req.eos_read && - Curl_bufq_is_empty(&data->req.sendbuf)) { - return req_set_upload_done(data); + if(data->req.eos_read && !data->req.eos_sent) { + char tmp; + size_t nwritten; + result = xfer_send(data, &tmp, 0, 0, &nwritten); + if(result) + return result; + DEBUGASSERT(data->req.eos_sent); + } + + if(!data->req.upload_done && data->req.eos_read && data->req.eos_sent) { + DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf)); + if(data->req.shutdown) { + bool done; + result = Curl_xfer_send_shutdown(data, &done); + if(result) + return result; + if(!done) + return CURLE_AGAIN; + } + return Curl_req_set_upload_done(data); } return CURLE_OK; } @@ -365,18 +406,26 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req) } #endif /* !USE_HYPER */ +bool Curl_req_sendbuf_empty(struct Curl_easy *data) +{ + return !data->req.sendbuf_init || Curl_bufq_is_empty(&data->req.sendbuf); +} + bool Curl_req_want_send(struct Curl_easy *data) { - return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf); + /* Not done and + * - KEEP_SEND and not PAUSEd. + * - or request has buffered data to send + * - or transfer connection has pending data to send */ + return !data->req.done && + (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) || + !Curl_req_sendbuf_empty(data) || + Curl_xfer_needs_flush(data)); } bool Curl_req_done_sending(struct Curl_easy *data) { - if(data->req.upload_done) { - DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf)); - return TRUE; - } - return FALSE; + return data->req.upload_done && !Curl_req_want_send(data); } CURLcode Curl_req_send_more(struct Curl_easy *data) @@ -384,7 +433,10 @@ CURLcode Curl_req_send_more(struct Curl_easy *data) CURLcode result; /* Fill our send buffer if more from client can be read. */ - if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) { + if(!data->req.upload_aborted && + !data->req.eos_read && + !(data->req.keepon & KEEP_SEND_PAUSE) && + !Curl_bufq_is_full(&data->req.sendbuf)) { ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0, add_from_client, data, &result); if(nread < 0 && result != CURLE_AGAIN) @@ -403,7 +455,18 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data) if(!data->req.upload_done) { Curl_bufq_reset(&data->req.sendbuf); data->req.upload_aborted = TRUE; - return req_set_upload_done(data); + /* no longer KEEP_SEND and KEEP_SEND_PAUSE */ + data->req.keepon &= ~KEEP_SENDBITS; + return Curl_req_set_upload_done(data); } return CURLE_OK; } + +CURLcode Curl_req_stop_send_recv(struct Curl_easy *data) +{ + /* stop receiving and ALL sending as well, including PAUSE and HOLD. + * We might still be paused on receive client writes though, so + * keep those bits around. */ + data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS); + return Curl_req_abort_sending(data); +} diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h index f9be6f299..c53c3eb5a 100644 --- a/Utilities/cmcurl/lib/request.h +++ b/Utilities/cmcurl/lib/request.h @@ -32,6 +32,9 @@ /* forward declarations */ struct UserDefined; +#ifndef CURL_DISABLE_DOH +struct doh_probes; +#endif enum expect100 { EXP100_SEND_DATA, /* enough waiting, just send the body now */ @@ -51,10 +54,10 @@ enum upgrade101 { /* - * Request specific data in the easy handle (Curl_easy). Previously, + * Request specific data in the easy handle (Curl_easy). Previously, * these members were on the connectdata struct but since a conn struct may * now be shared between different Curl_easys, we store connection-specific - * data here. This struct only keeps stuff that's interesting for *this* + * data here. This struct only keeps stuff that is interesting for *this* * request, as it will be cleared between multiple ones */ struct SingleRequest { @@ -68,7 +71,7 @@ struct SingleRequest { unsigned int headerbytecount; /* received server headers (not CONNECT headers) */ unsigned int allheadercount; /* all received headers (server + CONNECT) */ - unsigned int deductheadercount; /* this amount of bytes doesn't count when + unsigned int deductheadercount; /* this amount of bytes does not count when we check if anything has been transferred at the end of a connection. We use this counter to make only a 100 reply (without @@ -93,7 +96,6 @@ struct SingleRequest { struct bufq sendbuf; /* data which needs to be send to the server */ size_t sendbuf_hds_len; /* amount of header bytes in sendbuf */ time_t timeofdoc; - long bodywrites; char *location; /* This points to an allocated version of the Location: header data */ char *newurl; /* Set to the new URL to use when a redirect or a retry is @@ -104,7 +106,6 @@ struct SingleRequest { union { struct FILEPROTO *file; struct FTP *ftp; - struct HTTP *http; struct IMAP *imap; struct ldapreqinfo *ldap; struct MQTT *mqtt; @@ -116,7 +117,7 @@ struct SingleRequest { struct TELNET *telnet; } p; #ifndef CURL_DISABLE_DOH - struct dohdata *doh; /* DoH specific data for this request */ + struct doh_probes *doh; /* DoH specific data for this request */ #endif #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; @@ -129,6 +130,7 @@ struct SingleRequest { BIT(download_done); /* set to TRUE when download is complete */ BIT(eos_written); /* iff EOS has been written to client */ BIT(eos_read); /* iff EOS has been read from the client */ + BIT(eos_sent); /* iff EOS has been sent to the server */ BIT(rewind_read); /* iff reader needs rewind at next start */ BIT(upload_done); /* set to TRUE when all request data has been sent */ BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also @@ -137,6 +139,7 @@ struct SingleRequest { BIT(http_bodyless); /* HTTP response status code is between 100 and 199, 204 or 304 */ BIT(chunk); /* if set, this is a chunked transfer-encoding */ + BIT(resp_trailer); /* response carried 'Trailer:' header field */ BIT(ignore_cl); /* ignore content-length */ BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding on upload */ @@ -147,6 +150,10 @@ struct SingleRequest { but it is not the final request in the auth negotiation. */ BIT(sendbuf_init); /* sendbuf is initialized */ + BIT(shutdown); /* request end will shutdown connection */ +#ifdef USE_HYPER + BIT(bodywritten); +#endif }; /** @@ -218,10 +225,26 @@ CURLcode Curl_req_send_more(struct Curl_easy *data); */ bool Curl_req_want_send(struct Curl_easy *data); +/** + * TRUE iff the request has no buffered bytes yet to send. + */ +bool Curl_req_sendbuf_empty(struct Curl_easy *data); + /** * Stop sending any more request data to the server. * Will clear the send buffer and mark request sending as done. */ CURLcode Curl_req_abort_sending(struct Curl_easy *data); +/** + * Stop sending and receiving any more request data. + * Will abort sending if not done. + */ +CURLcode Curl_req_stop_send_recv(struct Curl_easy *data); + +/** + * Invoked when all request data has been uploaded. + */ +CURLcode Curl_req_set_upload_done(struct Curl_easy *data); + #endif /* HEADER_CURL_REQUEST_H */ diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 1f162daa0..c9b1bc0d6 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c @@ -79,7 +79,7 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data, unsigned int checks_to_perform); /* this returns the socket to wait for in the DO and DOING state for the multi - interface and then we're always _sending_ a request and thus we wait for + interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks) @@ -261,7 +261,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. **/ - data->req.no_body = TRUE; /* most requests don't contain a body */ + data->req.no_body = TRUE; /* most requests do not contain a body */ switch(rtspreq) { default: failf(data, "Got invalid RTSP request"); @@ -310,13 +310,15 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } if(rtspreq == RTSPREQ_RECEIVE) { - Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, -1, TRUE); goto out; } p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; if(!p_session_id && - (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { + (rtspreq & ~(Curl_RtspReq)(RTSPREQ_OPTIONS | + RTSPREQ_DESCRIBE | + RTSPREQ_SETUP))) { failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", p_request); result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -531,8 +533,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * actually set a custom Content-Length in the headers */ if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { result = - Curl_dyn_addf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", + Curl_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n", req_clen); if(result) goto out; @@ -576,7 +577,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) if(result) goto out; - Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE); /* issue the request */ result = Curl_req_send(data, &req_buffer); @@ -852,7 +853,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, * In which case we write out the left over bytes, letting the client * writer deal with it (it will report EXCESS and fail the transfer). */ DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d " - " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")", + " rtspc->state=%d, req.size=%" FMT_OFF_T ")", blen, rtspc->in_header, data->req.done, rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { @@ -950,7 +951,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) /* Find the end of Session ID * * Allow any non whitespace content, up to the field separator or end of - * line. RFC 2326 isn't 100% clear on the session ID and for example + * line. RFC 2326 is not 100% clear on the session ID and for example * gstreamer does url-encoded session ID's not covered by the standard. */ end = start; diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index b1ffa5c7e..41b09503f 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h @@ -62,16 +62,6 @@ struct rtsp_conn { * RTSP unique setup ***************************************************************************/ struct RTSP { - /* - * http_wrapper MUST be the first element of this structure for the wrap - * logic to work. In this way, we get a cheap polymorphism because - * &(data->state.proto.rtsp) == &(data->state.proto.http) per the C spec - * - * HTTP functions can safely treat this as an HTTP struct, but RTSP aware - * functions can also index into the later elements. - */ - struct HTTP http_wrapper; /* wrap HTTP to do the heavy lifting */ - long CSeq_sent; /* CSeq of this request */ long CSeq_recv; /* CSeq received */ }; diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index d92e745a7..dae736b01 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -33,7 +33,7 @@ #endif #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE) -#error "We can't compile without select() or poll() support." +#error "We cannot compile without select() or poll() support." #endif #ifdef MSDOS @@ -47,12 +47,16 @@ #include "select.h" #include "timediff.h" #include "warnless.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" /* * Internal function used for waiting a specific amount of ms * in Curl_socket_check() and Curl_poll() when no file descriptor * is provided to wait on, just being used to delay execution. - * WinSock select() and poll() timeout mechanisms need a valid + * Winsock select() and poll() timeout mechanisms need a valid * socket descriptor in a not null file descriptor set to work. * Waiting indefinitely with this function is not allowed, a * zero or negative timeout value will return immediately. @@ -81,7 +85,7 @@ int Curl_wait_ms(timediff_t timeout_ms) #if TIMEDIFF_T_MAX >= ULONG_MAX if(timeout_ms >= ULONG_MAX) timeout_ms = ULONG_MAX-1; - /* don't use ULONG_MAX, because that is equal to INFINITE */ + /* do not use ULONG_MAX, because that is equal to INFINITE */ #endif Sleep((ULONG)timeout_ms); #else @@ -131,7 +135,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ struct timeval *ptimeout; #ifdef USE_WINSOCK - /* WinSock select() can't handle zero events. See the comment below. */ + /* Winsock select() cannot handle zero events. See the comment below. */ if((!fds_read || fds_read->fd_count == 0) && (!fds_write || fds_write->fd_count == 0) && (!fds_err || fds_err->fd_count == 0)) { @@ -143,16 +147,16 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ ptimeout = curlx_mstotv(&pending_tv, timeout_ms); #ifdef USE_WINSOCK - /* WinSock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also can't be called + /* Winsock select() must not be called with an fd_set that contains zero + fd flags, or it will return WSAEINVAL. But, it also cannot be called with no fd_sets at all! From the documentation: Any two of the parameters, readfds, writefds, or exceptfds, can be given as null. At least one must be non-null, and any non-null descriptor set must contain at least one handle to a socket. - It is unclear why WinSock doesn't just handle this for us instead of - calling this an error. Luckily, with WinSock, we can _also_ ask how + It is unclear why Winsock does not just handle this for us instead of + calling this an error. Luckily, with Winsock, we can _also_ ask how many bits are set on an fd_set. So, let's just check it beforehand. */ return select((int)maxfd + 1, @@ -169,7 +173,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ /* * Wait for read or write events on a set of file descriptors. It uses poll() * when a fine poll() is available, in order to avoid limits with FD_SETSIZE, - * otherwise select() is used. An error is returned if select() is being used + * otherwise select() is used. An error is returned if select() is being used * and a file descriptor is too large for FD_SETSIZE. * * A negative timeout value makes this function wait indefinitely, @@ -226,7 +230,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ num++; } - r = Curl_poll(pfd, num, timeout_ms); + r = Curl_poll(pfd, (unsigned int)num, timeout_ms); if(r <= 0) return r; @@ -257,8 +261,8 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ } /* - * This is a wrapper around poll(). If poll() does not exist, then - * select() is used instead. An error is returned if select() is + * This is a wrapper around poll(). If poll() does not exist, then + * select() is used instead. An error is returned if select() is * being used and a file descriptor is too large for FD_SETSIZE. * A negative timeout value makes this function wait indefinitely, * unless no valid file descriptor is given, when this happens the @@ -357,8 +361,8 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) } /* - Note also that WinSock ignores the first argument, so we don't worry - about the fact that maxfd is computed incorrectly with WinSock (since + Note also that Winsock ignores the first argument, so we do not worry + about the fact that maxfd is computed incorrectly with Winsock (since curl_socket_t is unsigned in such cases and thus -1 is the largest value). */ @@ -401,3 +405,147 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) return r; } + +void Curl_pollfds_init(struct curl_pollfds *cpfds, + struct pollfd *static_pfds, + unsigned int static_count) +{ + DEBUGASSERT(cpfds); + memset(cpfds, 0, sizeof(*cpfds)); + if(static_pfds && static_count) { + cpfds->pfds = static_pfds; + cpfds->count = static_count; + } +} + +void Curl_pollfds_cleanup(struct curl_pollfds *cpfds) +{ + DEBUGASSERT(cpfds); + if(cpfds->allocated_pfds) { + free(cpfds->pfds); + } + memset(cpfds, 0, sizeof(*cpfds)); +} + +static CURLcode cpfds_increase(struct curl_pollfds *cpfds, unsigned int inc) +{ + struct pollfd *new_fds; + unsigned int new_count = cpfds->count + inc; + + new_fds = calloc(new_count, sizeof(struct pollfd)); + if(!new_fds) + return CURLE_OUT_OF_MEMORY; + + memcpy(new_fds, cpfds->pfds, cpfds->count * sizeof(struct pollfd)); + if(cpfds->allocated_pfds) + free(cpfds->pfds); + cpfds->pfds = new_fds; + cpfds->count = new_count; + cpfds->allocated_pfds = TRUE; + return CURLE_OK; +} + +static CURLcode cpfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events, bool fold) +{ + int i; + + if(fold && cpfds->n <= INT_MAX) { + for(i = (int)cpfds->n - 1; i >= 0; --i) { + if(sock == cpfds->pfds[i].fd) { + cpfds->pfds[i].events |= events; + return CURLE_OK; + } + } + } + /* not folded, add new entry */ + if(cpfds->n >= cpfds->count) { + if(cpfds_increase(cpfds, 100)) + return CURLE_OUT_OF_MEMORY; + } + cpfds->pfds[cpfds->n].fd = sock; + cpfds->pfds[cpfds->n].events = events; + ++cpfds->n; + return CURLE_OK; +} + +CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events) +{ + return cpfds_add_sock(cpfds, sock, events, FALSE); +} + +CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, + struct easy_pollset *ps) +{ + size_t i; + + DEBUGASSERT(cpfds); + DEBUGASSERT(ps); + for(i = 0; i < ps->num; i++) { + short events = 0; + if(ps->actions[i] & CURL_POLL_IN) + events |= POLLIN; + if(ps->actions[i] & CURL_POLL_OUT) + events |= POLLOUT; + if(events) { + if(cpfds_add_sock(cpfds, ps->sockets[i], events, TRUE)) + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} + +void Curl_waitfds_init(struct curl_waitfds *cwfds, + struct curl_waitfd *static_wfds, + unsigned int static_count) +{ + DEBUGASSERT(cwfds); + DEBUGASSERT(static_wfds); + memset(cwfds, 0, sizeof(*cwfds)); + cwfds->wfds = static_wfds; + cwfds->count = static_count; +} + +static CURLcode cwfds_add_sock(struct curl_waitfds *cwfds, + curl_socket_t sock, short events) +{ + int i; + + if(cwfds->n <= INT_MAX) { + for(i = (int)cwfds->n - 1; i >= 0; --i) { + if(sock == cwfds->wfds[i].fd) { + cwfds->wfds[i].events |= events; + return CURLE_OK; + } + } + } + /* not folded, add new entry */ + if(cwfds->n >= cwfds->count) + return CURLE_OUT_OF_MEMORY; + cwfds->wfds[cwfds->n].fd = sock; + cwfds->wfds[cwfds->n].events = events; + ++cwfds->n; + return CURLE_OK; +} + +CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, + struct easy_pollset *ps) +{ + size_t i; + + DEBUGASSERT(cwfds); + DEBUGASSERT(ps); + for(i = 0; i < ps->num; i++) { + short events = 0; + if(ps->actions[i] & CURL_POLL_IN) + events |= CURL_WAIT_POLLIN; + if(ps->actions[i] & CURL_POLL_OUT) + events |= CURL_WAIT_POLLOUT; + if(events) { + if(cwfds_add_sock(cwfds, ps->sockets[i], events)) + return CURLE_OUT_OF_MEMORY; + } + } + return CURLE_OK; +} diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h index 5b1ca23eb..f01acbdef 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h @@ -111,4 +111,37 @@ int Curl_wait_ms(timediff_t timeout_ms); } while(0) #endif +struct curl_pollfds { + struct pollfd *pfds; + unsigned int n; + unsigned int count; + BIT(allocated_pfds); +}; + +void Curl_pollfds_init(struct curl_pollfds *cpfds, + struct pollfd *static_pfds, + unsigned int static_count); + +void Curl_pollfds_cleanup(struct curl_pollfds *cpfds); + +CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds, + struct easy_pollset *ps); + +CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds, + curl_socket_t sock, short events); + +struct curl_waitfds { + struct curl_waitfd *wfds; + unsigned int n; + unsigned int count; +}; + +void Curl_waitfds_init(struct curl_waitfds *cwfds, + struct curl_waitfd *static_wfds, + unsigned int static_count); + +CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds, + struct easy_pollset *ps); + + #endif /* HEADER_CURL_SELECT_H */ diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 68a8bf3b6..6f566622f 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -289,6 +289,13 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(nwrite == wmax) { data->req.download_done = TRUE; } + + if((type & CLIENTWRITE_EOS) && !data->req.no_body && + (data->req.maxdownload > data->req.bytecount)) { + failf(data, "end of response with %" FMT_OFF_T " bytes missing", + data->req.maxdownload - data->req.bytecount); + return CURLE_PARTIAL_FILE; + } } /* Error on too large filesize is handled below, after writing @@ -309,7 +316,9 @@ static CURLcode cw_download_write(struct Curl_easy *data, } /* Update stats, write and report progress */ data->req.bytecount += nwrite; - ++data->req.bodywrites; +#ifdef USE_HYPER + data->req.bodywritten = TRUE; +#endif result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); if(result) return result; @@ -319,18 +328,17 @@ static CURLcode cw_download_write(struct Curl_easy *data, infof(data, "Excess found writing body:" " excess = %zu" - ", size = %" CURL_FORMAT_CURL_OFF_T - ", maxdownload = %" CURL_FORMAT_CURL_OFF_T - ", bytecount = %" CURL_FORMAT_CURL_OFF_T, + ", size = %" FMT_OFF_T + ", maxdownload = %" FMT_OFF_T + ", bytecount = %" FMT_OFF_T, excess_len, data->req.size, data->req.maxdownload, data->req.bytecount); connclose(data->conn, "excess found in a read"); } } - else if(nwrite < nbytes) { + else if((nwrite < nbytes) && !data->req.ignorebody) { failf(data, "Exceeded the maximum allowed file size " - "(%" CURL_FORMAT_CURL_OFF_T ") with %" - CURL_FORMAT_CURL_OFF_T " bytes", + "(%" FMT_OFF_T ") with %" FMT_OFF_T " bytes", data->set.max_filesize, data->req.bytecount); return CURLE_FILESIZE_EXCEEDED; } @@ -597,6 +605,14 @@ CURLcode Curl_creader_def_unpause(struct Curl_easy *data, return CURLE_OK; } +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + (void)data; + (void)reader; + return FALSE; +} + void Curl_creader_def_done(struct Curl_easy *data, struct Curl_creader *reader, int premature) { @@ -615,6 +631,7 @@ struct cr_in_ctx { BIT(seen_eos); BIT(errored); BIT(has_used_cb); + BIT(is_paused); }; static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader) @@ -637,6 +654,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, struct cr_in_ctx *ctx = reader->ctx; size_t nread; + ctx->is_paused = FALSE; + /* Once we have errored, we will return the same error forever */ if(ctx->errored) { *pnread = 0; @@ -668,8 +687,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client read function EOF fail, " - "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T - " of needed bytes read", ctx->read_len, ctx->total_len); + "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", + ctx->read_len, ctx->total_len); return CURLE_READ_ERROR; } *pnread = 0; @@ -688,12 +707,14 @@ static CURLcode cr_in_read(struct Curl_easy *data, case CURL_READFUNC_PAUSE: if(data->conn->handler->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the transfer - isn't done using the "normal" procedure. */ + actually only FILE:// just now, and it cannot pause since the transfer + is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); return CURLE_READ_ERROR; } /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ + CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE"); + ctx->is_paused = TRUE; data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ *pnread = 0; *peos = FALSE; @@ -716,8 +737,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, *peos = ctx->seen_eos; break; } - CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T - ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, nread=%zu, eos=%d", + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T + ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d", blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos); return CURLE_OK; @@ -764,7 +785,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, failf(data, "Could not seek stream"); return CURLE_READ_ERROR; } - /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { char scratch[4*1024]; size_t readthisamountnow = @@ -782,8 +803,8 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, if((actuallyread == 0) || (actuallyread > readthisamountnow)) { /* this checks for greater-than only to make sure that the CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T - " bytes from the input", passed); + failf(data, "Could only read %" FMT_OFF_T " bytes from the input", + passed); return CURLE_READ_ERROR; } } while(passed < offset); @@ -798,7 +819,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, return CURLE_PARTIAL_FILE; } } - /* we've passed, proceed as normal */ + /* we have passed, proceed as normal */ return CURLE_OK; } @@ -850,12 +871,28 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, } /* no callback set or failure above, makes us fail at once */ - failf(data, "necessary data rewind wasn't possible"); + failf(data, "necessary data rewind was not possible"); return CURLE_SEND_FAIL_REWIND; } return CURLE_OK; } +static CURLcode cr_in_unpause(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + ctx->is_paused = FALSE; + return CURLE_OK; +} + +static bool cr_in_is_paused(struct Curl_easy *data, + struct Curl_creader *reader) +{ + struct cr_in_ctx *ctx = reader->ctx; + (void)data; + return ctx->is_paused; +} static const struct Curl_crtype cr_in = { "cr-in", @@ -866,7 +903,8 @@ static const struct Curl_crtype cr_in = { cr_in_total_length, cr_in_resume_from, cr_in_rewind, - Curl_creader_def_unpause, + cr_in_unpause, + cr_in_is_paused, Curl_creader_def_done, sizeof(struct cr_in_ctx) }; @@ -911,6 +949,7 @@ struct cr_lc_ctx { struct bufq buf; BIT(read_eos); /* we read an EOS from the next reader */ BIT(eos); /* we have returned an EOS */ + BIT(prev_cr); /* the last byte was a CR */ }; static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader) @@ -967,10 +1006,15 @@ static CURLcode cr_lc_read(struct Curl_easy *data, goto out; } - /* at least one \n needs conversion to '\r\n', place into ctx->buf */ + /* at least one \n might need conversion to '\r\n', place into ctx->buf */ for(i = start = 0; i < nread; ++i) { - if(buf[i] != '\n') + /* if this byte is not an LF character, or if the preceding character is + a CR (meaning this already is a CRLF pair), go to next */ + if((buf[i] != '\n') || ctx->prev_cr) { + ctx->prev_cr = (buf[i] == '\r'); continue; + } + ctx->prev_cr = false; /* on a soft limit bufq, we do not need to check length */ result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); if(!result) @@ -979,13 +1023,19 @@ static CURLcode cr_lc_read(struct Curl_easy *data, return result; start = i + 1; if(!data->set.crlf && (data->state.infilesize != -1)) { - /* we're here only because FTP is in ASCII mode... + /* we are here only because FTP is in ASCII mode... bump infilesize for the LF we just added */ data->state.infilesize++; /* comment: this might work for FTP, but in HTTP we could not change * the content length after having started the request... */ } } + + if(start < i) { /* leftover */ + result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); + if(result) + return result; + } } DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf)); @@ -1022,6 +1072,7 @@ static const struct Curl_crtype cr_lc = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_lc_ctx) }; @@ -1057,7 +1108,7 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data, /* if we do not have 0 length init, and crlf conversion is wanted, * add the reader for it */ if(clen && (data->set.crlf -#ifdef CURL_DO_LINEEND_CONV +#ifdef CURL_PREFER_LF_LINEENDS || data->state.prefer_ascii #endif )) { @@ -1084,8 +1135,8 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add fread reader, len=%"CURL_FORMAT_CURL_OFF_T - " -> %d", len, result); + CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d", + len, result); return result; } @@ -1195,6 +1246,7 @@ static const struct Curl_crtype cr_null = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct Curl_creader) }; @@ -1294,6 +1346,7 @@ static const struct Curl_crtype cr_buf = { cr_buf_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_buf_ctx) }; @@ -1356,6 +1409,18 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) return result; } +bool Curl_creader_is_paused(struct Curl_easy *data) +{ + struct Curl_creader *reader = data->req.reader_stack; + + while(reader) { + if(reader->crt->is_paused(data, reader)) + return TRUE; + reader = reader->next; + } + return FALSE; +} + void Curl_creader_done(struct Curl_easy *data, int premature) { struct Curl_creader *reader = data->req.reader_stack; diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index 82a290257..dc1b82edf 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -218,6 +218,7 @@ struct Curl_crtype { struct Curl_creader *reader, curl_off_t offset); CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader); CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader); + bool (*is_paused)(struct Curl_easy *data, struct Curl_creader *reader); void (*done)(struct Curl_easy *data, struct Curl_creader *reader, int premature); size_t creader_size; /* sizeof() allocated struct Curl_creader */ @@ -268,6 +269,8 @@ CURLcode Curl_creader_def_rewind(struct Curl_easy *data, struct Curl_creader *reader); CURLcode Curl_creader_def_unpause(struct Curl_easy *data, struct Curl_creader *reader); +bool Curl_creader_def_is_paused(struct Curl_easy *data, + struct Curl_creader *reader); void Curl_creader_def_done(struct Curl_easy *data, struct Curl_creader *reader, int premature); @@ -375,6 +378,11 @@ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); */ CURLcode Curl_creader_unpause(struct Curl_easy *data); +/** + * Return TRUE iff any of the installed readers is paused. + */ +bool Curl_creader_is_paused(struct Curl_easy *data); + /** * Tell all client readers that they are done. */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index e8b25454b..5b9a4cbae 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -139,8 +139,38 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) return CURLE_OK; } +static CURLcode setstropt_interface(char *option, char **devp, + char **ifacep, char **hostp) +{ + char *dev = NULL; + char *iface = NULL; + char *host = NULL; + CURLcode result; + + DEBUGASSERT(devp); + DEBUGASSERT(ifacep); + DEBUGASSERT(hostp); + + if(option) { + /* Parse the interface details if set, otherwise clear them all */ + result = Curl_parse_interface(option, &dev, &iface, &host); + if(result) + return result; + } + free(*devp); + *devp = dev; + + free(*ifacep); + *ifacep = iface; + + free(*hostp); + *hostp = host; + + return CURLE_OK; +} + #define C_SSLVERSION_VALUE(x) (x & 0xffff) -#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000) +#define C_SSLVERSION_MAX_VALUE(x) ((unsigned long)x & 0xffff0000) static CURLcode protocol2num(const char *str, curl_prot_t *val) { @@ -203,27 +233,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.dns_cache_timeout = (int)arg; break; case CURLOPT_CA_CACHE_TIMEOUT: - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; + if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { + arg = va_arg(param, long); + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; - data->set.general_ssl.ca_cache_timeout = (int)arg; + data->set.general_ssl.ca_cache_timeout = (int)arg; + } + else + return CURLE_NOT_BUILT_IN; break; case CURLOPT_DNS_USE_GLOBAL_CACHE: /* deprecated */ break; case CURLOPT_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], - va_arg(param, char *)); + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { + /* set a list of cipher we want to use in the SSL connection */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], + va_arg(param, char *)); + } + else + return CURLE_NOT_BUILT_IN; break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], - va_arg(param, char *)); + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { + /* set a list of cipher we want to use in the SSL connection for proxy */ + result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], + va_arg(param, char *)); + } + else + return CURLE_NOT_BUILT_IN; break; #endif case CURLOPT_TLS13_CIPHERS: @@ -312,7 +354,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_FAILONERROR: /* - * Don't output the >=400 error code HTML-page, but instead only + * Do not output the >=400 error code HTML-page, but instead only * return error. */ data->set.http_fail_on_error = (0 != va_arg(param, long)); @@ -383,8 +425,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * TFTP option that specifies the block size to use for data transmission. */ arg = va_arg(param, long); - if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(arg < TFTP_BLKSIZE_MIN) + arg = 512; + else if(arg > TFTP_BLKSIZE_MAX) + arg = TFTP_BLKSIZE_MAX; data->set.tftp_blksize = arg; break; #endif @@ -461,7 +505,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = va_arg(param, long); version = C_SSLVERSION_VALUE(arg); - version_max = C_SSLVERSION_MAX_VALUE(arg); + version_max = (long)C_SSLVERSION_MAX_VALUE(arg); if(version < CURL_SSLVERSION_DEFAULT || version == CURL_SSLVERSION_SSLv2 || @@ -582,7 +626,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * * If the encoding is set to "" we use an Accept-Encoding header that * encompasses all the encodings we support. - * If the encoding is set to NULL we don't send an Accept-Encoding header + * If the encoding is set to NULL we do not send an Accept-Encoding header * and ignore an received Content-Encoding header. * */ @@ -646,7 +690,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_POST: /* Does this option serve a purpose anymore? Yes it does, when - CURLOPT_POSTFIELDS isn't used and the POST data is read off the + CURLOPT_POSTFIELDS is not used and the POST data is read off the callback! */ if(va_arg(param, long)) { data->set.method = HTTPREQ_POST; @@ -749,7 +793,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* general protection against mistakes and abuse */ if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) return CURLE_BAD_FUNCTION_ARGUMENT; - /* append the cookie file name to the list of file names, and deal with + /* append the cookie filename to the list of filenames, and deal with them later */ cl = curl_slist_append(data->state.cookielist, argptr); if(!cl) { @@ -765,7 +809,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->state.cookielist = NULL; if(!data->share || !data->share->cookies) { - /* throw away all existing cookies if this isn't a shared cookie + /* throw away all existing cookies if this is not a shared cookie container */ Curl_cookie_clearall(data->cookies); Curl_cookie_cleanup(data->cookies); @@ -777,7 +821,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_COOKIEJAR: /* - * Set cookie file name to dump all cookies to when we're done. + * Set cookie filename to dump all cookies to when we are done. */ result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], va_arg(param, char *)); @@ -928,7 +972,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_HTTP09_ALLOWED: - arg = va_arg(param, unsigned long); + arg = (long)va_arg(param, unsigned long); if(arg > 1L) return CURLE_BAD_FUNCTION_ARGUMENT; #ifdef USE_HYPER @@ -1007,7 +1051,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ } - /* switch off bits we can't support */ + /* switch off bits we cannot support */ #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM support */ #endif @@ -1039,7 +1083,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], va_arg(param, char *)); - /* we don't set + /* we do not set data->set.method = HTTPREQ_CUSTOM; here, we continue as if we were using the already set type and this just changes the actual request keyword */ @@ -1085,7 +1129,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) auth |= CURLAUTH_DIGEST; /* set standard digest bit */ auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ } - /* switch off bits we can't support */ + /* switch off bits we cannot support */ #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM support */ #endif @@ -1115,7 +1159,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set proxy server:port to use as proxy. * * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we don't want to use a proxy + * we explicitly say that we do not want to use a proxy * (even though there might be environment variables saying so). * * Setting it to NULL, means no proxy but allows the environment variables @@ -1129,7 +1173,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Set proxy server:port to use as SOCKS proxy. * - * If the proxy is set to "" or NULL we explicitly say that we don't want + * If the proxy is set to "" or NULL we explicitly say that we do not want * to use the socks proxy. */ result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY], @@ -1500,7 +1544,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_USERNAME: /* - * authentication user name to use in the operation + * authentication username to use in the operation */ result = Curl_setstropt(&data->set.str[STRING_USERNAME], va_arg(param, char *)); @@ -1541,7 +1585,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Prefix the HOST with dash (-) to _remove_ the entry from the cache. * * This API can remove any entry from the DNS cache, but only entries - * that aren't actually in use right now will be pruned immediately. + * that are not actually in use right now will be pruned immediately. */ data->set.resolve = va_arg(param, struct curl_slist *); data->state.resolve = data->set.resolve; @@ -1598,7 +1642,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_PROXYUSERNAME: /* - * authentication user name to use in the operation + * authentication username to use in the operation */ result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], va_arg(param, char *)); @@ -1650,7 +1694,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.fdebug = va_arg(param, curl_debug_callback); /* - * if the callback provided is NULL, it'll use the default callback + * if the callback provided is NULL, it will use the default callback */ break; case CURLOPT_DEBUGDATA: @@ -1723,7 +1767,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SSLCERT: /* - * String that holds file name of the SSL certificate to use + * String that holds filename of the SSL certificate to use */ result = Curl_setstropt(&data->set.str[STRING_CERT], va_arg(param, char *)); @@ -1738,7 +1782,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLCERT: /* - * String that holds file name of the SSL certificate to use for proxy + * String that holds filename of the SSL certificate to use for proxy */ result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], va_arg(param, char *)); @@ -1769,7 +1813,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_SSLKEY: /* - * String that holds file name of the SSL key to use + * String that holds filename of the SSL key to use */ result = Curl_setstropt(&data->set.str[STRING_KEY], va_arg(param, char *)); @@ -1784,7 +1828,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLKEY: /* - * String that holds file name of the SSL key to use for proxy + * String that holds filename of the SSL key to use for proxy */ result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], va_arg(param, char *)); @@ -1877,8 +1921,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Set what interface or address/hostname to bind the socket to when * performing an operation and thus what from-IP your connection will use. */ - result = Curl_setstropt(&data->set.str[STRING_DEVICE], - va_arg(param, char *)); + result = setstropt_interface(va_arg(param, char *), + &data->set.str[STRING_DEVICE], + &data->set.str[STRING_INTERFACE], + &data->set.str[STRING_BINDHOST]); break; #ifndef CURL_DISABLE_BINDLOCAL case CURLOPT_LOCALPORT: @@ -1931,7 +1977,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Enable peer SSL verifying for proxy. */ data->set.proxy_ssl.primary.verifypeer = - (0 != va_arg(param, long))?TRUE:FALSE; + (0 != va_arg(param, long)); /* Update the current connection proxy_ssl_config. */ Curl_ssl_conn_config_update(data, TRUE); @@ -1939,12 +1985,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_SSL_VERIFYHOST: /* - * Enable verification of the host name in the peer certificate + * Enable verification of the hostname in the peer certificate */ arg = va_arg(param, long); /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. + this argument took a boolean when it was not and misused it. Treat 1 and 2 the same */ data->set.ssl.primary.verifyhost = !!(arg & 3); @@ -1954,7 +2000,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_DOH case CURLOPT_DOH_SSL_VERIFYHOST: /* - * Enable verification of the host name in the peer certificate for DoH + * Enable verification of the hostname in the peer certificate for DoH */ arg = va_arg(param, long); @@ -1965,12 +2011,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_VERIFYHOST: /* - * Enable verification of the host name in the peer certificate for proxy + * Enable verification of the hostname in the peer certificate for proxy */ arg = va_arg(param, long); /* Treat both 1 and 2 as TRUE */ - data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); + data->set.proxy_ssl.primary.verifyhost = !!(arg & 3); /* Update the current connection proxy_ssl_config. */ Curl_ssl_conn_config_update(data, TRUE); break; @@ -2046,7 +2092,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_PINNEDPUBLICKEY: /* * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. + * Specify filename of the public key in DER format. */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) @@ -2060,7 +2106,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_PROXY_PINNEDPUBLICKEY: /* * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. + * Specify filename of the public key in DER format. */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) @@ -2073,7 +2119,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_CAINFO: /* - * Set CA info for SSL connection. Specify file name of the CA certificate + * Set CA info for SSL connection. Specify filename of the CA certificate */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], va_arg(param, char *)); @@ -2095,7 +2141,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_CAINFO: /* - * Set CA info SSL connection for proxy. Specify file name of the + * Set CA info SSL connection for proxy. Specify filename of the * CA certificate */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], @@ -2123,7 +2169,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) - /* This does not work on windows. */ + /* This does not work on Windows. */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], va_arg(param, char *)); else @@ -2138,7 +2184,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) - /* This does not work on windows. */ + /* This does not work on Windows. */ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], va_arg(param, char *)); else @@ -2148,7 +2194,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #endif case CURLOPT_CRLFILE: /* - * Set CRL file info for SSL connection. Specify file name of the CRL + * Set CRL file info for SSL connection. Specify filename of the CRL * to check certificates revocation */ result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE], @@ -2157,7 +2203,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_CRLFILE: /* - * Set CRL file info for SSL connection for proxy. Specify file name of the + * Set CRL file info for SSL connection for proxy. Specify filename of the * CRL to check certificates revocation */ result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], @@ -2207,7 +2253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_BUFFERSIZE: /* * The application kindly asks for a differently sized receive buffer. - * If it seems reasonable, we'll use it. + * If it seems reasonable, we will use it. */ arg = va_arg(param, long); @@ -2495,16 +2541,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.sessionid = (0 != va_arg(param, long)); + data->set.ssl.primary.cache_session = (0 != va_arg(param, long)); #ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; + data->set.proxy_ssl.primary.cache_session = + data->set.ssl.primary.cache_session; #endif break; #ifdef USE_SSH /* we only include SSH options if explicitly built to support SSH */ case CURLOPT_SSH_AUTH_TYPES: - data->set.ssh_auth_types = (unsigned int)va_arg(param, long); + data->set.ssh_auth_types = (int)va_arg(param, long); break; case CURLOPT_SSH_PUBLIC_KEYFILE: @@ -2533,7 +2580,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SSH_KNOWNHOSTS: /* - * Store the file name to read known hosts from. + * Store the filename to read known hosts from. */ result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], va_arg(param, char *)); @@ -2575,7 +2622,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_SSH_COMPRESSION: - data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE; + data->set.ssh_compression = (0 != va_arg(param, long)); break; #endif /* USE_SSH */ @@ -2587,7 +2634,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.http_te_skip = (0 == va_arg(param, long)); break; #else - return CURLE_NOT_BUILT_IN; /* hyper doesn't support */ + return CURLE_NOT_BUILT_IN; /* hyper does not support */ #endif case CURLOPT_HTTP_CONTENT_DECODING: @@ -2625,7 +2672,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Use this scope id when using IPv6 * We always get longs when passed plain numericals so we should check - * that the value fits into an unsigned 32 bit integer. + * that the value fits into an unsigned 32-bit integer. */ uarg = va_arg(param, unsigned long); #if SIZEOF_LONG > 4 @@ -2653,22 +2700,32 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_PROTOCOLS_STR: { argptr = va_arg(param, char *); - result = protocol2num(argptr, &data->set.allowed_protocols); - if(result) - return result; + if(argptr) { + result = protocol2num(argptr, &data->set.allowed_protocols); + if(result) + return result; + } + else + /* make a NULL argument reset to default */ + data->set.allowed_protocols = (curl_prot_t) CURLPROTO_ALL; break; } case CURLOPT_REDIR_PROTOCOLS_STR: { argptr = va_arg(param, char *); - result = protocol2num(argptr, &data->set.redir_protocols); - if(result) - return result; + if(argptr) { + result = protocol2num(argptr, &data->set.redir_protocols); + if(result) + return result; + } + else + /* make a NULL argument reset to default */ + data->set.redir_protocols = (curl_prot_t) CURLPROTO_REDIR; break; } case CURLOPT_DEFAULT_PROTOCOL: - /* Set the protocol to use when the URL doesn't include any protocol */ + /* Set the protocol to use when the URL does not include any protocol */ result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], va_arg(param, char *)); break; @@ -2918,10 +2975,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) arg = INT_MAX; data->set.tcp_keepintvl = (int)arg; break; + case CURLOPT_TCP_KEEPCNT: + arg = va_arg(param, long); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepcnt = (int)arg; + break; case CURLOPT_TCP_FASTOPEN: #if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ defined(TCP_FASTOPEN_CONNECT) - data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE; + data->set.tcp_fastopen = (0 != va_arg(param, long)); #else result = CURLE_NOT_BUILT_IN; #endif @@ -2973,7 +3038,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.connect_to = va_arg(param, struct curl_slist *); break; case CURLOPT_SUPPRESS_CONNECT_HEADERS: - data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE; + data->set.suppress_connect_headers = (0 != va_arg(param, long)); break; case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: uarg = va_arg(param, unsigned long); @@ -2993,7 +3058,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_DOH_URL: result = Curl_setstropt(&data->set.str[STRING_DOH], va_arg(param, char *)); - data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE; + data->set.doh = !!(data->set.str[STRING_DOH]); break; #endif case CURLOPT_UPKEEP_INTERVAL_MS: @@ -3049,7 +3114,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); if(result) return result; - /* this needs to build a list of file names to read from, so that it can + /* this needs to build a list of filenames to read from, so that it can read them later, as we might get a shared HSTS handle to load them into */ h = curl_slist_append(data->state.hstslist, argptr); @@ -3135,8 +3200,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) argptr = va_arg(param, char *); if(!argptr) { data->set.tls_ech = CURLECH_DISABLE; - result = CURLE_BAD_FUNCTION_ARGUMENT; - return result; + return CURLE_OK; } plen = strlen(argptr); if(plen > CURL_MAX_INPUT_LENGTH) { diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index 53e91777e..ef7baca67 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h @@ -38,6 +38,15 @@ typedef unsigned long u_int32_t; #define isatty(fd) 0 +/* Workaround bug in IBM QADRT runtime library: + * function puts() does not output the implicit trailing newline. + */ + +#include /* Be sure it is loaded. */ +#undef puts +#define puts(s) (fputs((s), stdout) == EOF? EOF: putchar('\n')) + + /* System API wrapper prototypes & definitions to support ASCII parameters. */ #include @@ -46,6 +55,8 @@ typedef unsigned long u_int32_t; #include #include +#ifdef BUILDING_LIBCURL + extern int Curl_getaddrinfo_a(const char *nodename, const char *servname, const struct addrinfo *hints, @@ -141,4 +152,6 @@ extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen); #define inflateEnd Curl_os400_inflateEnd #endif +#endif /* BUILDING_LIBCURL */ + #endif /* HEADER_CURL_SETUP_OS400_H */ diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index ea3936c70..33b74db35 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h @@ -101,7 +101,7 @@ static char *vms_translate_path(const char *path) } } # else - /* VMS translate path is actually not needed on the current 64 bit */ + /* VMS translate path is actually not needed on the current 64-bit */ /* VMS platforms, so instead of figuring out the pointer settings */ /* Change it to a noop */ # define vms_translate_path(__path) __path @@ -144,7 +144,7 @@ static struct passwd *vms_getpwuid(uid_t uid) { struct passwd *my_passwd; -/* Hack needed to support 64 bit builds, decc_getpwnam is 32 bit only */ +/* Hack needed to support 64-bit builds, decc_getpwnam is 32-bit only */ #ifdef __DECC # if __INITIAL_POINTER_SIZE __char_ptr32 unix_path; diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h index d7e2e6be1..a297bdcff 100644 --- a/Utilities/cmcurl/lib/setup-win32.h +++ b/Utilities/cmcurl/lib/setup-win32.h @@ -62,11 +62,11 @@ #endif /* - * Include header files for windows builds before redefining anything. + * Include header files for Windows builds before redefining anything. * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h or ws2tcpip.h. Any other windows thing belongs - * to any other further and independent block. Under Cygwin things work - * just as under linux (e.g. ) and the winsock headers should + * winsock2.h or ws2tcpip.h. Any other Windows thing belongs + * to any other further and independent block. Under Cygwin things work + * just as under Linux (e.g. ) and the Winsock headers should * never be included when __CYGWIN__ is defined. */ @@ -78,7 +78,7 @@ # error "_UNICODE is defined but UNICODE is not defined" # endif /* - * Don't include unneeded stuff in Windows headers to avoid compiler + * Do not include unneeded stuff in Windows headers to avoid compiler * warnings and macro clashes. * Make sure to define this macro before including any Windows headers. */ diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index 4a02045d2..91dc95016 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c @@ -100,10 +100,10 @@ #if defined(USE_OPENSSL_SHA256) -struct sha256_ctx { +struct ossl_sha256_ctx { EVP_MD_CTX *openssl_ctx; }; -typedef struct sha256_ctx my_sha256_ctx; +typedef struct ossl_sha256_ctx my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) { @@ -247,7 +247,7 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); - if(length == SHA256_DIGEST_LENGTH) + if(length == CURL_SHA256_DIGEST_LENGTH) CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); if(ctx->hHash) @@ -334,14 +334,14 @@ static const unsigned long K[64] = { #define RORc(x, y) \ (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) RORc((x), (n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#define Sha256_Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Sha256_Maj(x,y,z) (((x | y) & z) | (x & y)) +#define Sha256_S(x, n) RORc((x), (n)) +#define Sha256_R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (Sha256_S(x, 2) ^ Sha256_S(x, 13) ^ Sha256_S(x, 22)) +#define Sigma1(x) (Sha256_S(x, 6) ^ Sha256_S(x, 11) ^ Sha256_S(x, 25)) +#define Gamma0(x) (Sha256_S(x, 7) ^ Sha256_S(x, 18) ^ Sha256_R(x, 3)) +#define Gamma1(x) (Sha256_S(x, 17) ^ Sha256_S(x, 19) ^ Sha256_R(x, 10)) /* Compress 512-bits */ static int sha256_compress(struct sha256_state *md, @@ -364,12 +364,12 @@ static int sha256_compress(struct sha256_state *md, } /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i) \ - do { \ - unsigned long t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ - unsigned long t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; \ +#define RND(a,b,c,d,e,f,g,h,i) \ + do { \ + unsigned long t0 = h + Sigma1(e) + Sha256_Ch(e, f, g) + K[i] + W[i]; \ + unsigned long t1 = Sigma0(a) + Sha256_Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; \ } while(0) for(i = 0; i < 64; ++i) { @@ -467,7 +467,7 @@ static int my_sha256_final(unsigned char *out, md->buf[md->curlen++] = (unsigned char)0x80; /* If the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length + * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if(md->curlen > 56) { @@ -542,4 +542,4 @@ const struct HMAC_params Curl_HMAC_SHA256[] = { }; -#endif /* AWS, DIGEST, or libSSH2 */ +#endif /* AWS, DIGEST, or libssh2 */ diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c index 8fa5cda00..2ddaba6d7 100644 --- a/Utilities/cmcurl/lib/share.c +++ b/Utilities/cmcurl/lib/share.c @@ -26,10 +26,12 @@ #include #include "urldata.h" +#include "connect.h" #include "share.h" #include "psl.h" #include "vtls/vtls.h" #include "hsts.h" +#include "url.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -64,7 +66,7 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) return CURLSHE_INVALID; if(share->dirty) - /* don't allow setting options while one or more handles are already + /* do not allow setting options while one or more handles are already using this share */ return CURLSHE_IN_USE; @@ -119,8 +121,12 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) break; case CURL_LOCK_DATA_CONNECT: - if(Curl_conncache_init(&share->conn_cache, 103)) - res = CURLSHE_NOMEM; + /* It is safe to set this option several times on a share. */ + if(!share->cpool.idata) { + if(Curl_cpool_init(&share->cpool, Curl_on_disconnect, + NULL, share, 103)) + res = CURLSHE_NOMEM; + } break; case CURL_LOCK_DATA_PSL: @@ -223,8 +229,9 @@ curl_share_cleanup(struct Curl_share *share) return CURLSHE_IN_USE; } - Curl_conncache_close_all_connections(&share->conn_cache); - Curl_conncache_destroy(&share->conn_cache); + if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { + Curl_cpool_destroy(&share->cpool); + } Curl_hash_destroy(&share->hostcache); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) @@ -268,7 +275,7 @@ Curl_share_lock(struct Curl_easy *data, curl_lock_data type, if(share->lockfunc) /* only call this if set! */ share->lockfunc(data, type, accesstype, share->clientdata); } - /* else if we don't share this, pretend successful lock */ + /* else if we do not share this, pretend successful lock */ return CURLSHE_OK; } diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h index 632d9198f..124f7049f 100644 --- a/Utilities/cmcurl/lib/share.h +++ b/Utilities/cmcurl/lib/share.h @@ -34,7 +34,10 @@ #define CURL_GOOD_SHARE 0x7e117a1e #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) -/* this struct is libcurl-private, don't export details */ +#define CURL_SHARE_KEEP_CONNECT(s) \ + ((s) && ((s)->specifier & (1<< CURL_LOCK_DATA_CONNECT))) + +/* this struct is libcurl-private, do not export details */ struct Curl_share { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; @@ -43,7 +46,7 @@ struct Curl_share { curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; - struct conncache conn_cache; + struct cpool cpool; struct Curl_hash hostcache; #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) struct CookieInfo *cookies; diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h index 9b29403c2..c57580f43 100644 --- a/Utilities/cmcurl/lib/sigpipe.h +++ b/Utilities/cmcurl/lib/sigpipe.h @@ -35,6 +35,13 @@ struct sigpipe_ignore { }; #define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x +#define SIGPIPE_MEMBER(x) struct sigpipe_ignore x + +static void sigpipe_init(struct sigpipe_ignore *ig) +{ + memset(ig, 0, sizeof(*ig)); + ig->no_signal = TRUE; +} /* * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl @@ -70,11 +77,23 @@ static void sigpipe_restore(struct sigpipe_ignore *ig) sigaction(SIGPIPE, &ig->old_pipe_act, NULL); } +static void sigpipe_apply(struct Curl_easy *data, + struct sigpipe_ignore *ig) +{ + if(data->set.no_signal != ig->no_signal) { + sigpipe_restore(ig); + sigpipe_ignore(data, ig); + } +} + #else /* for systems without sigaction */ #define sigpipe_ignore(x,y) Curl_nop_stmt +#define sigpipe_apply(x,y) Curl_nop_stmt +#define sigpipe_init(x) Curl_nop_stmt #define sigpipe_restore(x) Curl_nop_stmt #define SIGPIPE_VARIABLE(x) +#define SIGPIPE_MEMBER(x) bool x #endif #endif /* HEADER_CURL_SIGPIPE_H */ diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index cab1e757f..f4fff9e61 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -559,7 +559,7 @@ static void smb_format_message(struct Curl_easy *data, struct smb_header *h, h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME); h->uid = smb_swap16(smbc->uid); h->tid = smb_swap16(req->tid); - pid = getpid(); + pid = (unsigned int)getpid(); h->pid_high = smb_swap16((unsigned short)(pid >> 16)); h->pid = smb_swap16((unsigned short) pid); } @@ -572,7 +572,7 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len, size_t bytes_written; CURLcode result; - result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written); + result = Curl_xfer_send(data, smbc->send_buf, len, FALSE, &bytes_written); if(result) return result; @@ -597,7 +597,7 @@ static CURLcode smb_flush(struct Curl_easy *data) if(!smbc->send_size) return CURLE_OK; - result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, + result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, FALSE, &bytes_written); if(result) return result; @@ -642,9 +642,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data) unsigned char nt_hash[21]; unsigned char nt[24]; - size_t byte_count = sizeof(lm) + sizeof(nt); - byte_count += strlen(smbc->user) + strlen(smbc->domain); - byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ + const size_t byte_count = sizeof(lm) + sizeof(nt) + + strlen(smbc->user) + strlen(smbc->domain) + + strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; @@ -653,7 +653,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); - memset(&msg, 0, sizeof(msg)); + memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); msg.word_count = SMB_WC_SETUP_ANDX; msg.andx.command = SMB_COM_NO_ANDX_COMMAND; msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE); @@ -671,7 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) MSGCATNULL(smbc->domain); MSGCATNULL(OS); MSGCATNULL(CLIENTNAME); - byte_count = p - msg.bytes; + DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg, @@ -685,12 +685,12 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data) struct smb_conn *smbc = &conn->proto.smbc; char *p = msg.bytes; - size_t byte_count = strlen(conn->host.name) + strlen(smbc->share); - byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ + const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) + + strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - memset(&msg, 0, sizeof(msg)); + memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); msg.word_count = SMB_WC_TREE_CONNECT_ANDX; msg.andx.command = SMB_COM_NO_ANDX_COMMAND; msg.pw_len = 0; @@ -699,7 +699,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data) MSGCAT("\\"); MSGCATNULL(smbc->share); MSGCATNULL(SERVICENAME); /* Match any type of service */ - byte_count = p - msg.bytes; + DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg, @@ -710,16 +710,15 @@ static CURLcode smb_send_open(struct Curl_easy *data) { struct smb_request *req = data->req.p.smb; struct smb_nt_create msg; - size_t byte_count; + const size_t byte_count = strlen(req->path) + 1; - if((strlen(req->path) + 1) > sizeof(msg.bytes)) + if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - memset(&msg, 0, sizeof(msg)); + memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); msg.word_count = SMB_WC_NT_CREATE_ANDX; msg.andx.command = SMB_COM_NO_ANDX_COMMAND; - byte_count = strlen(req->path); - msg.name_length = smb_swap16((unsigned short)byte_count); + msg.name_length = smb_swap16((unsigned short)(byte_count - 1)); msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL); if(data->state.upload) { msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE); @@ -729,7 +728,7 @@ static CURLcode smb_send_open(struct Curl_easy *data) msg.access = smb_swap32(SMB_GENERIC_READ); msg.create_disposition = smb_swap32(SMB_FILE_OPEN); } - msg.byte_count = smb_swap16((unsigned short) ++byte_count); + msg.byte_count = smb_swap16((unsigned short) byte_count); strcpy(msg.bytes, req->path); return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg, @@ -924,7 +923,7 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) /* * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601) - * to Posix time. Cap the output to fit within a time_t. + * to POSIX time. Cap the output to fit within a time_t. */ static void get_posix_time(time_t *out, curl_off_t timestamp) { @@ -1071,7 +1070,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) break; case SMB_CLOSE: - /* We don't care if the close failed, proceed to tree disconnect anyway */ + /* We do not care if the close failed, proceed to tree disconnect anyway */ next_state = SMB_TREE_DISCONNECT; break; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index dd231a521..3c5893284 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -288,7 +288,7 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) static void smtp_state(struct Curl_easy *data, smtpstate newstate) { struct smtp_conn *smtpc = &data->conn->proto.smtpc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) /* for debug purposes */ static const char * const names[] = { "STOP", @@ -308,8 +308,8 @@ static void smtp_state(struct Curl_easy *data, smtpstate newstate) }; if(smtpc->state != newstate) - infof(data, "SMTP %p state change from %s to %s", - (void *)smtpc, names[smtpc->state], names[newstate]); + CURL_TRC_SMTP(data, "state change from %s to %s", + names[smtpc->state], names[newstate]); #endif smtpc->state = newstate; @@ -534,16 +534,16 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) if(smtp->rcpt) { /* We notify the server we are sending UTF-8 data if a) it supports the SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in - either the local address or host name parts. This is regardless of - whether the host name is encoded using IDN ACE */ + either the local address or hostname parts. This is regardless of + whether the hostname is encoded using IDN ACE */ bool utf8 = FALSE; if((!smtp->custom) || (!smtp->custom[0])) { char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; - /* Parse the mailbox to verify into the local address and host name - parts, converting the host name to an IDN A-label if necessary */ + /* Parse the mailbox to verify into the local address and hostname + parts, converting the hostname to an IDN A-label if necessary */ result = smtp_parse_address(smtp->rcpt->data, &address, &host); if(result) @@ -555,7 +555,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data) ((host.encalloc) || (!Curl_is_ASCII_name(address)) || (!Curl_is_ASCII_name(host.name))); - /* Send the VRFY command (Note: The host name part may be absent when the + /* Send the VRFY command (Note: The hostname part may be absent when the host is a local system) */ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s", address, @@ -607,8 +607,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* We notify the server we are sending UTF-8 data if a) it supports the SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in - either the local address or host name parts. This is regardless of - whether the host name is encoded using IDN ACE */ + either the local address or hostname parts. This is regardless of + whether the hostname is encoded using IDN ACE */ bool utf8 = FALSE; /* Calculate the FROM parameter */ @@ -616,8 +616,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; - /* Parse the FROM mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ + /* Parse the FROM mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], &address, &host); if(result) @@ -635,8 +635,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we'll simply let the server worry - about that and reply with a 501 error */ + /* An invalid mailbox was provided but we will simply let the server + worry about that and reply with a 501 error */ from = aprintf("<%s>", address); free(address); @@ -656,8 +656,8 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; - /* Parse the AUTH mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ + /* Parse the AUTH mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], &address, &host); if(result) @@ -676,7 +676,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we'll simply let the server + /* An invalid mailbox was provided but we will simply let the server worry about it */ auth = aprintf("<%s>", address); free(address); @@ -695,7 +695,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* Prepare the mime data if some. */ if(data->set.mimepost.kind != MIMEKIND_NONE) { /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~MIME_BODY_ONLY; + data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; /* Add external headers and mime version. */ curl_mime_headers(&data->set.mimepost, data->set.headers, 0); @@ -723,7 +723,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) /* Calculate the optional SIZE parameter */ if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) { - size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); + size = aprintf("%" FMT_OFF_T, data->state.infilesize); if(!size) { result = CURLE_OUT_OF_MEMORY; @@ -731,7 +731,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) } } - /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8 + /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8 based address then quickly scan through the recipient list and check if any there do, as we need to correctly identify our support for SMTPUTF8 in the envelope, as per RFC-6531 sect. 3.4 */ @@ -740,7 +740,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data) struct curl_slist *rcpt = smtp->rcpt; while(rcpt && !utf8) { - /* Does the host name contain non-ASCII characters? */ + /* Does the hostname contain non-ASCII characters? */ if(!Curl_is_ASCII_name(rcpt->data)) utf8 = TRUE; @@ -790,8 +790,8 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) char *address = NULL; struct hostname host = { NULL, NULL, NULL, NULL }; - /* Parse the recipient mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ + /* Parse the recipient mailbox into the local address and hostname parts, + converting the hostname to an IDN A-label if necessary */ result = smtp_parse_address(smtp->rcpt->data, &address, &host); if(result) @@ -802,7 +802,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", address, host.name); else - /* An invalid mailbox was provided but we'll simply let the server worry + /* An invalid mailbox was provided but we will simply let the server worry about that and reply with a 501 error */ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>", address); @@ -958,7 +958,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, if(smtpcode != 1) { if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - /* We don't have a SSL/TLS connection yet, but SSL is requested */ + /* We do not have a SSL/TLS connection yet, but SSL is requested */ if(smtpc->tls_supported) /* Switch to TLS connection now */ result = smtp_perform_starttls(data, conn); @@ -1102,7 +1102,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE; - /* If there's multiple RCPT TO to be issued, it's possible to ignore errors + /* If there is multiple RCPT TO to be issued, it is possible to ignore errors and proceed with only the valid addresses. */ is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE; @@ -1129,7 +1129,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, /* Send the next RCPT TO command */ result = smtp_perform_rcpt_to(data); else { - /* We weren't able to issue a successful RCPT TO command while going + /* We were not able to issue a successful RCPT TO command while going over recipients (potentially multiple). Sending back last error. */ if(!smtp->rcpt_had_ok) { failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error); @@ -1164,7 +1164,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode, Curl_pgrsSetUploadSize(data, data->state.infilesize); /* SMTP upload */ - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* End of DO phase */ smtp_state(data, SMTP_STOP); @@ -1202,6 +1202,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data, size_t nread = 0; /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ +upgrade_tls: if(smtpc->state == SMTP_UPGRADETLS) return smtp_perform_upgrade_tls(data); @@ -1238,6 +1239,10 @@ static CURLcode smtp_statemachine(struct Curl_easy *data, case SMTP_STARTTLS: result = smtp_state_starttls_resp(data, smtpcode, smtpc->state); + /* During UPGRADETLS, leave the read loop as we need to connect + * (e.g. TLS handshake) before we continue sending/receiving. */ + if(!result && (smtpc->state == SMTP_UPGRADETLS)) + goto upgrade_tls; break; case SMTP_AUTH: @@ -1417,7 +1422,8 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, /* Clear the transfer mode for the next request */ smtp->transfer = PPTRANSFER_BODY; - + CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d", + status, premature, result); return result; } @@ -1435,7 +1441,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, CURLcode result = CURLE_OK; struct SMTP *smtp = data->req.p.smtp; - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_SMTP(data, "smtp_perform(), start"); if(data->req.no_body) { /* Requested no body means no transfer */ @@ -1447,10 +1453,10 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, /* Store the first recipient (or NULL if not specified) */ smtp->rcpt = data->set.mail_rcpt; - /* Track of whether we've successfully sent at least one RCPT TO command */ + /* Track of whether we have successfully sent at least one RCPT TO command */ smtp->rcpt_had_ok = FALSE; - /* Track of the last error we've received by sending RCPT TO command */ + /* Track of the last error we have received by sending RCPT TO command */ smtp->rcpt_last_error = 0; /* Initial data character is the first character in line: it is implicitly @@ -1467,16 +1473,16 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, result = smtp_perform_command(data); if(result) - return result; + goto out; /* Run the state-machine */ result = smtp_multi_statemach(data, dophase_done); *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - +out: + CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d", + result, *connected, *dophase_done); return result; } @@ -1502,7 +1508,7 @@ static CURLcode smtp_do(struct Curl_easy *data, bool *done) return result; result = smtp_regular_transfer(data, done); - + CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); return result; } @@ -1537,6 +1543,7 @@ static CURLcode smtp_disconnect(struct Curl_easy *data, /* Cleanup our connection based variables */ Curl_safefree(smtpc->domain); + CURL_TRC_SMTP(data, "smtp_disconnect(), finished"); return CURLE_OK; } @@ -1550,7 +1557,7 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected) if(smtp->transfer != PPTRANSFER_BODY) /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); return CURLE_OK; } @@ -1568,6 +1575,7 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) DEBUGF(infof(data, "DO phase is complete")); } + CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); return result; } @@ -1602,6 +1610,8 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data, if(!result && *dophase_done) result = smtp_dophase_done(data, connected); + CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", + result, *dophase_done); return result; } @@ -1615,10 +1625,8 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, /* Initialise the SMTP layer */ result = smtp_init(data); - if(result) - return result; - - return CURLE_OK; + CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); + return result; } /*********************************************************************** @@ -1708,7 +1716,7 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data) * smtp_parse_address() * * Parse the fully qualified mailbox address into a local address part and the - * host name, converting the host name to an IDN A-label, as per RFC-5890, if + * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if * necessary. * * Parameters: @@ -1719,8 +1727,8 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data) * address [in/out] - A new allocated buffer which holds the local * address part of the mailbox. This buffer must be * free'ed by the caller. - * host [in/out] - The host name structure that holds the original, - * and optionally encoded, host name. + * host [in/out] - The hostname structure that holds the original, + * and optionally encoded, hostname. * Curl_free_idnconverted_hostname() must be called * once the caller has finished with the structure. * @@ -1728,14 +1736,14 @@ static CURLcode smtp_parse_custom_request(struct Curl_easy *data) * * Notes: * - * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor + * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor * that conversion then we shall return success. This allow the caller to send * the data to the server as a U-label (as per RFC-6531 sect. 3.2). * * If an mailbox '@' separator cannot be located then the mailbox is considered * to be either a local mailbox or an invalid mailbox (depending on what the * calling function deems it to be) then the input will simply be returned in - * the address part with the host name being NULL. + * the address part with the hostname being NULL. */ static CURLcode smtp_parse_address(const char *fqma, char **address, struct hostname *host) @@ -1744,7 +1752,7 @@ static CURLcode smtp_parse_address(const char *fqma, char **address, size_t length; /* Duplicate the fully qualified email address so we can manipulate it, - ensuring it doesn't contain the delimiters if specified */ + ensuring it does not contain the delimiters if specified */ char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma); if(!dup) return CURLE_OUT_OF_MEMORY; @@ -1755,17 +1763,17 @@ static CURLcode smtp_parse_address(const char *fqma, char **address, dup[length - 1] = '\0'; } - /* Extract the host name from the address (if we can) */ + /* Extract the hostname from the address (if we can) */ host->name = strpbrk(dup, "@"); if(host->name) { *host->name = '\0'; host->name = host->name + 1; - /* Attempt to convert the host name to IDN ACE */ + /* Attempt to convert the hostname to IDN ACE */ (void) Curl_idnconvert_hostname(host); /* If Curl_idnconvert_hostname() fails then we shall attempt to continue - and send the host name using UTF-8 rather than as 7-bit ACE (which is + and send the hostname using UTF-8 rather than as 7-bit ACE (which is our preference) */ } @@ -1925,6 +1933,7 @@ static const struct Curl_crtype cr_eob = { Curl_creader_def_resume_from, Curl_creader_def_rewind, Curl_creader_def_unpause, + Curl_creader_def_is_paused, Curl_creader_def_done, sizeof(struct cr_eob_ctx) }; diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c index d7e3afd88..b14f5a5f1 100644 --- a/Utilities/cmcurl/lib/socketpair.c +++ b/Utilities/cmcurl/lib/socketpair.c @@ -27,14 +27,31 @@ #include "urldata.h" #include "rand.h" -#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) +#if defined(USE_EVENTFD) +#ifdef HAVE_SYS_EVENTFD_H +#include +#endif + +int Curl_eventfd(curl_socket_t socks[2], bool nonblocking) +{ + int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); + if(efd == -1) { + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } + socks[0] = socks[1] = efd; + return 0; +} +#elif defined(HAVE_PIPE) +#ifdef HAVE_FCNTL #include +#endif -int Curl_pipe(curl_socket_t socks[2]) +int Curl_pipe(curl_socket_t socks[2], bool nonblocking) { if(pipe(socks)) return -1; - +#ifdef HAVE_FCNTL if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || fcntl(socks[1], F_SETFD, FD_CLOEXEC) ) { close(socks[0]); @@ -42,13 +59,45 @@ int Curl_pipe(curl_socket_t socks[2]) socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } +#endif + if(nonblocking) { + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) { + close(socks[0]); + close(socks[1]); + socks[0] = socks[1] = CURL_SOCKET_BAD; + return -1; + } + } return 0; } #endif -#if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) +#ifndef CURL_DISABLE_SOCKETPAIR +#ifdef HAVE_SOCKETPAIR +int Curl_socketpair(int domain, int type, int protocol, + curl_socket_t socks[2], bool nonblocking) +{ +#ifdef SOCK_NONBLOCK + type = nonblocking ? type | SOCK_NONBLOCK : type; +#endif + if(socketpair(domain, type, protocol, socks)) + return -1; +#ifndef SOCK_NONBLOCK + if(nonblocking) { + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) { + close(socks[0]); + close(socks[1]); + return -1; + } + } +#endif + return 0; +} +#else /* !HAVE_SOCKETPAIR */ #ifdef _WIN32 /* * This is a socketpair() implementation for Windows. @@ -80,7 +129,7 @@ int Curl_pipe(curl_socket_t socks[2]) #include "memdebug.h" int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2]) + curl_socket_t socks[2], bool nonblocking) { union { struct sockaddr_in inaddr; @@ -106,7 +155,7 @@ int Curl_socketpair(int domain, int type, int protocol, socks[0] = socks[1] = CURL_SOCKET_BAD; #if defined(_WIN32) || defined(__CYGWIN__) - /* don't set SO_REUSEADDR on Windows */ + /* do not set SO_REUSEADDR on Windows */ (void)reuse; #ifdef SO_EXCLUSIVEADDRUSE { @@ -134,7 +183,7 @@ int Curl_socketpair(int domain, int type, int protocol, if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) goto error; - /* use non-blocking accept to make sure we don't block forever */ + /* use non-blocking accept to make sure we do not block forever */ if(curlx_nonblock(listener, TRUE) < 0) goto error; pfd[0].fd = listener; @@ -168,7 +217,7 @@ int Curl_socketpair(int domain, int type, int protocol, nread = sread(socks[1], p, s); if(nread == -1) { int sockerr = SOCKERRNO; - /* Don't block forever */ + /* Do not block forever */ if(Curl_timediff(Curl_now(), start) > (60 * 1000)) goto error; if( @@ -198,6 +247,10 @@ int Curl_socketpair(int domain, int type, int protocol, } while(1); } + if(nonblocking) + if(curlx_nonblock(socks[0], TRUE) < 0 || + curlx_nonblock(socks[1], TRUE) < 0) + goto error; sclose(listener); return 0; @@ -207,5 +260,5 @@ error: sclose(socks[1]); return -1; } - -#endif /* ! HAVE_SOCKETPAIR */ +#endif +#endif /* !CURL_DISABLE_SOCKETPAIR */ diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index ddd44374a..3044f1122 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h @@ -26,21 +26,44 @@ #include "curl_setup.h" -#ifdef HAVE_PIPE +#if defined(HAVE_EVENTFD) && \ + defined(__x86_64__) && \ + defined(__aarch64__) && \ + defined(__ia64__) && \ + defined(__ppc64__) && \ + defined(__mips64) && \ + defined(__sparc64__) && \ + defined(__riscv_64e) && \ + defined(__s390x__) + +/* Use eventfd only with 64-bit CPU architectures because eventfd has a + * stringent rule of requiring the 8-byte buffer when calling read(2) and + * write(2) on it. In some rare cases, the C standard library implementation + * on a 32-bit system might choose to define uint64_t as a 32-bit type for + * various reasons (memory limitations, compatibility with older code), + * which makes eventfd broken. + */ +#define USE_EVENTFD 1 #define wakeup_write write #define wakeup_read read #define wakeup_close close -#define wakeup_create(p) Curl_pipe(p) +#define wakeup_create(p,nb) Curl_eventfd(p,nb) -#ifdef HAVE_FCNTL #include -int Curl_pipe(curl_socket_t socks[2]); -#else -#define Curl_pipe(p) pipe(p) -#endif +int Curl_eventfd(curl_socket_t socks[2], bool nonblocking); + +#elif defined(HAVE_PIPE) + +#define wakeup_write write +#define wakeup_read read +#define wakeup_close close +#define wakeup_create(p,nb) Curl_pipe(p,nb) + +#include +int Curl_pipe(curl_socket_t socks[2], bool nonblocking); -#else /* HAVE_PIPE */ +#else /* !USE_EVENTFD && !HAVE_PIPE */ #define wakeup_write swrite #define wakeup_read sread @@ -51,7 +74,7 @@ int Curl_pipe(curl_socket_t socks[2]); #elif !defined(HAVE_SOCKETPAIR) #define SOCKETPAIR_FAMILY 0 /* not used */ #else -#error "unsupported unix domain and socketpair build combo" +#error "unsupported Unix domain and socketpair build combo" #endif #ifdef SOCK_CLOEXEC @@ -60,19 +83,16 @@ int Curl_pipe(curl_socket_t socks[2]); #define SOCKETPAIR_TYPE SOCK_STREAM #endif -#define wakeup_create(p)\ -Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p) - -#endif /* HAVE_PIPE */ +#define wakeup_create(p,nb)\ +Curl_socketpair(SOCKETPAIR_FAMILY, SOCKETPAIR_TYPE, 0, p, nb) +#endif /* USE_EVENTFD */ -#ifndef HAVE_SOCKETPAIR +#ifndef CURL_DISABLE_SOCKETPAIR #include int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2]); -#else -#define Curl_socketpair(a,b,c,d) socketpair(a,b,c,d) + curl_socket_t socks[2], bool nonblocking); #endif #endif /* HEADER_CURL_SOCKETPAIR_H */ diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index 4ade4eb02..1f2b7b609 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c @@ -125,7 +125,7 @@ int Curl_blockread_all(struct Curl_cfilter *cf, } nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); if(nread <= 0) { - result = err; + result = (int)err; if(CURLE_AGAIN == err) continue; if(err) { @@ -194,7 +194,7 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data, (void)data; if(oldstate == state) - /* don't bother when the new state is the same as the old state */ + /* do not bother when the new state is the same as the old state */ return; sx->state = state; @@ -217,7 +217,7 @@ static CURLproxycode socks_state_send(struct Curl_cfilter *cf, CURLcode result; nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, - sx->outstanding, &result); + sx->outstanding, FALSE, &result); if(nwritten <= 0) { if(CURLE_AGAIN == result) { return CURLPX_OK; @@ -335,7 +335,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, goto CONNECT_RESOLVED; } - /* socks4a doesn't resolve anything locally */ + /* socks4a does not resolve anything locally */ sxstate(sx, data, CONNECT_REQ_INIT); goto CONNECT_REQ_INIT; @@ -365,7 +365,7 @@ CONNECT_RESOLVED: { struct Curl_addrinfo *hp = NULL; /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It + * We cannot use 'hostent' as a struct that Curl_resolv() returns. It * returns a Curl_addrinfo pointer that may not always look the same. */ if(dns) { @@ -388,7 +388,7 @@ CONNECT_RESOLVED: infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf); - Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ } else failf(data, "SOCKS4 connection to %s not supported", sx->hostname); @@ -413,7 +413,7 @@ CONNECT_REQ_INIT: /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely that a longer field is either a mistake or malicious input */ - failf(data, "Too long SOCKS proxy user name"); + failf(data, "Too long SOCKS proxy username"); return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ @@ -440,7 +440,7 @@ CONNECT_REQ_INIT: (packetsize + hostnamelen < sizeof(sx->buffer))) strcpy((char *)socksreq + packetsize, sx->hostname); else { - failf(data, "SOCKS4: too long host name"); + failf(data, "SOCKS4: too long hostname"); return CURLPX_LONG_HOSTNAME; } packetsize += hostnamelen; @@ -516,7 +516,7 @@ CONNECT_REQ_INIT: break; case 91: failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected or failed.", socksreq[4], socksreq[5], socksreq[6], socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), @@ -524,7 +524,7 @@ CONNECT_REQ_INIT: return CURLPX_REQUEST_FAILED; case 92: failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", socksreq[4], socksreq[5], socksreq[6], socksreq[7], @@ -533,7 +533,7 @@ CONNECT_REQ_INIT: return CURLPX_IDENTD; case 93: failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", request rejected because the client program and identd " "report different user-ids.", socksreq[4], socksreq[5], socksreq[6], socksreq[7], @@ -542,7 +542,7 @@ CONNECT_REQ_INIT: return CURLPX_IDENTD_DIFFER; default: failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" ", Unknown.", socksreq[4], socksreq[5], socksreq[6], socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), @@ -562,7 +562,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, struct Curl_easy *data) { /* - According to the RFC1928, section "6. Replies". This is what a SOCK5 + According to the RFC1928, section "6. Replies". This is what a SOCK5 replies: +----+-----+-------+------+----------+----------+ @@ -714,7 +714,7 @@ CONNECT_SOCKS_READ_INIT: CONNECT_AUTH_INIT: case CONNECT_AUTH_INIT: { - /* Needs user name and password */ + /* Needs username and password */ size_t proxy_user_len, proxy_password_len; if(sx->proxy_user && sx->proxy_password) { proxy_user_len = strlen(sx->proxy_user); @@ -738,7 +738,7 @@ CONNECT_AUTH_INIT: if(sx->proxy_user && proxy_user_len) { /* the length must fit in a single byte */ if(proxy_user_len > 255) { - failf(data, "Excessive user name length for proxy auth"); + failf(data, "Excessive username length for proxy auth"); return CURLPX_LONG_USER; } memcpy(socksreq + len, sx->proxy_user, proxy_user_len); @@ -893,7 +893,7 @@ CONNECT_RESOLVED: failf(data, "SOCKS5 connection to %s not supported", dest); } - Curl_resolv_unlock(data, dns); /* not used anymore from now on */ + Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ goto CONNECT_REQ_SEND; } CONNECT_RESOLVE_REMOTE: @@ -990,7 +990,7 @@ CONNECT_REQ_SEND: else if(socksreq[1]) { /* Anything besides 0 is an error */ CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = socksreq[1]; - failf(data, "Can't complete SOCKS5 connection to %s. (%d)", + failf(data, "cannot complete SOCKS5 connection to %s. (%d)", sx->hostname, (unsigned char)socksreq[1]); if(code < 9) { /* RFC 1928 section 6 lists: */ @@ -1120,7 +1120,7 @@ static void socks_proxy_cf_free(struct Curl_cfilter *cf) } /* After a TCP connection to the proxy has been verified, this function does - the next magic steps. If 'done' isn't set TRUE, it is not done yet and + the next magic steps. If 'done' is not set TRUE, it is not done yet and must be called again. Note: this function's sub-functions call failf() @@ -1249,6 +1249,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { socks_proxy_cf_destroy, socks_proxy_cf_connect, socks_proxy_cf_close, + Curl_cf_def_shutdown, socks_cf_get_host, socks_cf_adjust_pollset, Curl_cf_def_data_pending, diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index c0b42b871..f83db977a 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c @@ -172,7 +172,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there's no */ + /* As long as we need to keep sending some context info, and there is no */ /* errors, keep sending it... */ for(;;) { gss_major_status = Curl_gss_init_sec_context(data, @@ -201,10 +201,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(gss_send_token.length) { socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 1; /* authentication message type */ - us_length = htons((short)gss_send_token.length); + us_length = htons((unsigned short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, + FALSE, &code); if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); @@ -216,7 +217,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, nwritten = Curl_conn_cf_send(cf->next, data, (char *)gss_send_token.value, - gss_send_token.length, &code); + gss_send_token.length, FALSE, &code); if(code || ((ssize_t)gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); @@ -306,7 +307,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_minor_status, "gss_inquire_context")) { gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); - failf(data, "Failed to determine user name."); + failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; } gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, @@ -316,7 +317,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); - failf(data, "Failed to determine user name."); + failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; } user = malloc(gss_send_token.length + 1); @@ -377,7 +378,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ + * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ @@ -406,11 +407,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } gss_release_buffer(&gss_status, &gss_send_token); - us_length = htons((short)gss_w_token.length); + us_length = htons((unsigned short)gss_w_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); } - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &code); if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); @@ -420,7 +422,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, + &code); if(code || ( 1 != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -430,7 +433,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, else { nwritten = Curl_conn_cf_send(cf->next, data, (char *)gss_w_token.value, - gss_w_token.length, &code); + gss_w_token.length, FALSE, &code); if(code || ((ssize_t)gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index 2baae2c2b..a76d26180 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c @@ -139,7 +139,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, cred_handle.dwLower = 0; cred_handle.dwUpper = 0; - status = s_pSecFn->AcquireCredentialsHandle(NULL, + status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT("Kerberos"), SECPKG_CRED_OUTBOUND, NULL, @@ -152,13 +152,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { failf(data, "Failed to acquire credentials."); free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); return CURLE_COULDNT_CONNECT; } (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there's no */ + /* As long as we need to keep sending some context info, and there is no */ /* errors, keep sending it... */ for(;;) { TCHAR *sname; @@ -167,7 +167,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!sname) return CURLE_OUT_OF_MEMORY; - status = s_pSecFn->InitializeSecurityContext(&cred_handle, + status = Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle, sname, ISC_REQ_MUTUAL_AUTH | @@ -186,17 +186,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, curlx_unicodefree(sname); if(sspi_recv_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); sspi_recv_token.pvBuffer = NULL; sspi_recv_token.cbBuffer = 0; } if(check_sspi_err(data, status, "InitializeSecurityContext")) { free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); failf(data, "Failed to initialise security context."); return CURLE_COULDNT_CONNECT; } @@ -204,47 +204,48 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(sspi_send_token.cbBuffer) { socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 1; /* authentication message type */ - us_length = htons((short)sspi_send_token.cbBuffer); + us_length = htons((unsigned short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); free(service_name); if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } written = Curl_conn_cf_send(cf->next, data, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &code); + sspi_send_token.cbBuffer, FALSE, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI authentication token."); free(service_name); if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } } if(sspi_send_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); sspi_send_token.pvBuffer = NULL; } sspi_send_token.cbBuffer = 0; if(sspi_recv_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); sspi_recv_token.pvBuffer = NULL; } sspi_recv_token.cbBuffer = 0; @@ -266,8 +267,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -276,8 +277,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -285,8 +286,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid SSPI authentication response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -298,8 +299,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!sspi_recv_token.pvBuffer) { free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, @@ -309,9 +310,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to receive SSPI authentication token."); free(service_name); if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -321,14 +322,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, free(service_name); /* Everything is good so far, user was authenticated! */ - status = s_pSecFn->QueryCredentialsAttributes(&cred_handle, + status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, SECPKG_CRED_ATTR_NAMES, &names); - s_pSecFn->FreeCredentialsHandle(&cred_handle); + Curl_pSecFn->FreeCredentialsHandle(&cred_handle); if(check_sspi_err(data, status, "QueryCredentialAttributes")) { - s_pSecFn->DeleteSecurityContext(&sspi_context); - s_pSecFn->FreeContextBuffer(names.sUserName); - failf(data, "Failed to determine user name."); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(names.sUserName); + failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; } else { @@ -338,7 +339,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, (user_utf8 ? user_utf8 : "(unknown)")); curlx_unicodefree(user_utf8); #endif - s_pSecFn->FreeContextBuffer(names.sUserName); + Curl_pSecFn->FreeContextBuffer(names.sUserName); } /* Do encryption */ @@ -383,21 +384,21 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, * * The token is produced by encapsulating an octet containing the * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ + * set to FALSE. The token is verified using gss_unseal()/ * gss_unwrap(). * */ if(data->set.socks5_gssapi_nec) { - us_length = htons((short)1); + us_length = htons((unsigned short)1); memcpy(socksreq + 2, &us_length, sizeof(short)); } else { - status = s_pSecFn->QueryContextAttributes(&sspi_context, + status = Curl_pSecFn->QueryContextAttributes(&sspi_context, SECPKG_ATTR_SIZES, &sspi_sizes); if(check_sspi_err(data, status, "QueryContextAttributes")) { - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); failf(data, "Failed to query security context attributes."); return CURLE_COULDNT_CONNECT; } @@ -407,15 +408,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer); if(!sspi_w_token[0].pvBuffer) { - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } sspi_w_token[1].cbBuffer = 1; sspi_w_token[1].pvBuffer = malloc(1); if(!sspi_w_token[1].pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } @@ -424,20 +425,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize); if(!sspi_w_token[2].pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } - status = s_pSecFn->EncryptMessage(&sspi_context, + status = Curl_pSecFn->EncryptMessage(&sspi_context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); if(check_sspi_err(data, status, "EncryptMessage")) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); failf(data, "Failed to query security context attributes."); return CURLE_COULDNT_CONNECT; } @@ -446,10 +447,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + sspi_w_token[2].cbBuffer; sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer); if(!sspi_send_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } @@ -462,57 +463,59 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + sspi_w_token[1].cbBuffer, sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); sspi_w_token[0].pvBuffer = NULL; sspi_w_token[0].cbBuffer = 0; - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); sspi_w_token[1].pvBuffer = NULL; sspi_w_token[1].cbBuffer = 0; - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); sspi_w_token[2].pvBuffer = NULL; sspi_w_token[2].cbBuffer = 0; - us_length = htons((short)sspi_send_token.cbBuffer); + us_length = htons((unsigned short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); } - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &code); if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code); + written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, + &code); if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } } else { written = Curl_conn_cf_send(cf->next, data, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &code); + sspi_send_token.cbBuffer, FALSE, &code); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); } result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -520,14 +523,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid SSPI encryption response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -537,7 +540,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[0].cbBuffer = us_length; sspi_w_token[0].pvBuffer = malloc(us_length); if(!sspi_w_token[0].pvBuffer) { - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_OUT_OF_MEMORY; } @@ -546,8 +549,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } @@ -559,17 +562,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_w_token[1].cbBuffer = 0; sspi_w_token[1].pvBuffer = NULL; - status = s_pSecFn->DecryptMessage(&sspi_context, + status = Curl_pSecFn->DecryptMessage(&sspi_context, &wrap_desc, 0, &qop); if(check_sspi_err(data, status, "DecryptMessage")) { if(sspi_w_token[0].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); if(sspi_w_token[1].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); failf(data, "Failed to query security context attributes."); return CURLE_COULDNT_CONNECT; } @@ -578,27 +581,27 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[1].cbBuffer); if(sspi_w_token[0].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); if(sspi_w_token[1].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); } else { if(sspi_w_token[0].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[0].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); } (void)curlx_nonblock(sock, TRUE); @@ -611,7 +614,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[0] != 0) conn->socks5_sspi_context = sspi_context; else { - s_pSecFn->DeleteSecurityContext(&sspi_context); + Curl_pSecFn->DeleteSecurityContext(&sspi_context); conn->socks5_sspi_context = sspi_context; } */ diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c index 48e079b32..5e27b08a6 100644 --- a/Utilities/cmcurl/lib/splay.c +++ b/Utilities/cmcurl/lib/splay.c @@ -24,6 +24,7 @@ #include "curl_setup.h" +#include "timeval.h" #include "splay.h" /* @@ -33,7 +34,7 @@ * zero : when i is equal to j * positive when : when i is larger than j */ -#define compare(i,j) Curl_splaycomparekeys((i),(j)) +#define compare(i,j) Curl_timediff_us(i,j) /* * Splay using the key i (which may or may not be in the tree.) The starting @@ -45,12 +46,12 @@ struct Curl_tree *Curl_splay(struct curltime i, struct Curl_tree N, *l, *r, *y; if(!t) - return t; + return NULL; N.smaller = N.larger = NULL; l = r = &N; for(;;) { - long comp = compare(i, t->key); + timediff_t comp = compare(i, t->key); if(comp < 0) { if(!t->smaller) break; @@ -93,7 +94,7 @@ struct Curl_tree *Curl_splay(struct curltime i, return t; } -/* Insert key i into the tree t. Return a pointer to the resulting tree or +/* Insert key i into the tree t. Return a pointer to the resulting tree or * NULL if something went wrong. * * @unittest: 1309 @@ -106,11 +107,11 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, ~0, -1 }; /* will *NEVER* appear */ - if(!node) - return t; + DEBUGASSERT(node); if(t) { t = Curl_splay(i, t); + DEBUGASSERT(t); if(compare(i, t->key) == 0) { /* There already exists a node in the tree with the very same key. Build a doubly-linked circular list of nodes. We add the new 'node' struct @@ -150,7 +151,7 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, } /* Finds and deletes the best-fit node from the tree. Return a pointer to the - resulting tree. best-fit means the smallest node if it is not larger than + resulting tree. best-fit means the smallest node if it is not larger than the key */ struct Curl_tree *Curl_splaygetbest(struct curltime i, struct Curl_tree *t, @@ -166,6 +167,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, /* find smallest */ t = Curl_splay(tv_zero, t); + DEBUGASSERT(t); if(compare(i, t->key) < 0) { /* even the smallest is too big */ *removed = NULL; @@ -197,13 +199,13 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, } -/* Deletes the very node we point out from the tree if it's there. Stores a +/* Deletes the very node we point out from the tree if it is there. Stores a * pointer to the new resulting tree in 'newroot'. * * Returns zero on success and non-zero on errors! * When returning error, it does not touch the 'newroot' pointer. * - * NOTE: when the last node of the tree is removed, there's no tree left so + * NOTE: when the last node of the tree is removed, there is no tree left so * 'newroot' will be made to point to NULL. * * @unittest: 1309 @@ -217,9 +219,11 @@ int Curl_splayremove(struct Curl_tree *t, }; /* will *NEVER* appear */ struct Curl_tree *x; - if(!t || !removenode) + if(!t) return 1; + DEBUGASSERT(removenode); + if(compare(KEY_NOTUSED, removenode->key) == 0) { /* Key set to NOTUSED means it is a subnode within a 'same' linked list and thus we can unlink it easily. */ @@ -238,10 +242,11 @@ int Curl_splayremove(struct Curl_tree *t, } t = Curl_splay(removenode->key, t); + DEBUGASSERT(t); /* First make sure that we got the same root node as the one we want to remove, as otherwise we might be trying to remove a node that - isn't actually in the tree. + is not actually in the tree. We cannot just compare the keys here as a double remove in quick succession of a node with key != KEY_NOTUSED && same != NULL @@ -249,7 +254,7 @@ int Curl_splayremove(struct Curl_tree *t, if(t != removenode) return 2; - /* Check if there is a list with identical sizes, as then we're trying to + /* Check if there is a list with identical sizes, as then we are trying to remove the root node of a list of nodes with identical keys. */ x = t->samen; if(x != t) { @@ -268,6 +273,7 @@ int Curl_splayremove(struct Curl_tree *t, x = t->larger; else { x = Curl_splay(removenode->key, t->smaller); + DEBUGASSERT(x); x->larger = t->larger; } } @@ -276,3 +282,16 @@ int Curl_splayremove(struct Curl_tree *t, return 0; } + +/* set and get the custom payload for this tree node */ +void Curl_splayset(struct Curl_tree *node, void *payload) +{ + DEBUGASSERT(node); + node->ptr = payload; +} + +void *Curl_splayget(struct Curl_tree *node) +{ + DEBUGASSERT(node); + return node->ptr; +} diff --git a/Utilities/cmcurl/lib/splay.h b/Utilities/cmcurl/lib/splay.h index dd1d07ac2..b8c9360e5 100644 --- a/Utilities/cmcurl/lib/splay.h +++ b/Utilities/cmcurl/lib/splay.h @@ -26,13 +26,14 @@ #include "curl_setup.h" #include "timeval.h" +/* only use function calls to access this struct */ struct Curl_tree { struct Curl_tree *smaller; /* smaller node */ struct Curl_tree *larger; /* larger node */ struct Curl_tree *samen; /* points to the next node with identical key */ struct Curl_tree *samep; /* points to the prev node with identical key */ - struct curltime key; /* this node's "sort" key */ - void *payload; /* data the splay code doesn't care about */ + struct curltime key; /* this node's "sort" key */ + void *ptr; /* data the splay code does not care about */ }; struct Curl_tree *Curl_splay(struct curltime i, @@ -50,9 +51,8 @@ int Curl_splayremove(struct Curl_tree *t, struct Curl_tree *removenode, struct Curl_tree **newroot); -#define Curl_splaycomparekeys(i,j) ( ((i.tv_sec) < (j.tv_sec)) ? -1 : \ - ( ((i.tv_sec) > (j.tv_sec)) ? 1 : \ - ( ((i.tv_usec) < (j.tv_usec)) ? -1 : \ - ( ((i.tv_usec) > (j.tv_usec)) ? 1 : 0)))) +/* set and get the custom payload for this tree node */ +void Curl_splayset(struct Curl_tree *node, void *payload); +void *Curl_splayget(struct Curl_tree *node); #endif /* HEADER_CURL_SPLAY_H */ diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c index 14d76f785..b22dd31fc 100644 --- a/Utilities/cmcurl/lib/strcase.c +++ b/Utilities/cmcurl/lib/strcase.c @@ -93,12 +93,12 @@ static int casecompare(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 */ + /* get out of the loop as soon as they do not match */ return 0; first++; second++; } - /* If we're here either the strings are the same or the length is different. + /* If we are 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. */ @@ -141,8 +141,8 @@ int curl_strnequal(const char *first, const char *second, size_t max) /* if both pointers are NULL then treat them as equal if max is non-zero */ return (NULL == first && NULL == second && max); } -/* Copy an upper case version of the string from src to dest. The - * strings may overlap. No more than n characters of the string are copied +/* Copy an upper case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied * (including any NUL) and the destination string will NOT be * NUL-terminated if that limit is reached. */ @@ -156,8 +156,8 @@ void Curl_strntoupper(char *dest, const char *src, size_t n) } while(*src++ && --n); } -/* Copy a lower case version of the string from src to dest. The - * strings may overlap. No more than n characters of the string are copied +/* Copy a lower case version of the string from src to dest. The + * strings may overlap. No more than n characters of the string are copied * (including any NUL) and the destination string will NOT be * NUL-terminated if that limit is reached. */ diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index f142cf181..76a8ba2d7 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -74,13 +74,13 @@ curl_easy_strerror(CURLcode error) " this libcurl due to a build-time decision."; case CURLE_COULDNT_RESOLVE_PROXY: - return "Couldn't resolve proxy name"; + return "Could not resolve proxy name"; case CURLE_COULDNT_RESOLVE_HOST: - return "Couldn't resolve host name"; + return "Could not resolve hostname"; case CURLE_COULDNT_CONNECT: - return "Couldn't connect to server"; + return "Could not connect to server"; case CURLE_WEIRD_SERVER_REPLY: return "Weird server reply"; @@ -107,19 +107,19 @@ curl_easy_strerror(CURLcode error) return "FTP: unknown 227 response format"; case CURLE_FTP_CANT_GET_HOST: - return "FTP: can't figure out the host in the PASV response"; + return "FTP: cannot figure out the host in the PASV response"; case CURLE_HTTP2: return "Error in the HTTP2 framing layer"; case CURLE_FTP_COULDNT_SET_TYPE: - return "FTP: couldn't set file type"; + return "FTP: could not set file type"; case CURLE_PARTIAL_FILE: return "Transferred a partial file"; case CURLE_FTP_COULDNT_RETR_FILE: - return "FTP: couldn't retrieve (RETR failed) the specified file"; + return "FTP: could not retrieve (RETR failed) the specified file"; case CURLE_QUOTE_ERROR: return "Quote command returned error"; @@ -158,10 +158,10 @@ curl_easy_strerror(CURLcode error) return "SSL connect error"; case CURLE_BAD_DOWNLOAD_RESUME: - return "Couldn't resume download"; + return "Could not resume download"; case CURLE_FILE_COULDNT_READ_FILE: - return "Couldn't read a file:// file"; + return "Could not read a file:// file"; case CURLE_LDAP_CANNOT_BIND: return "LDAP: cannot bind"; @@ -212,7 +212,7 @@ curl_easy_strerror(CURLcode error) return "Problem with the local SSL certificate"; case CURLE_SSL_CIPHER: - return "Couldn't use specified SSL cipher"; + return "Could not use specified SSL cipher"; case CURLE_PEER_FAILED_VERIFICATION: return "SSL peer certificate or SSH remote key was not OK"; @@ -345,16 +345,15 @@ curl_easy_strerror(CURLcode error) /* * By using a switch, gcc -Wall will complain about enum values * which do not appear, helping keep this function up-to-date. - * By using gcc -Wall -Werror, you can't forget. + * By using gcc -Wall -Werror, you cannot forget. * - * A table would not have the same benefit. Most compilers will - * generate code very similar to a table in any case, so there - * is little performance gain from a table. And something is broken - * for the user's application, anyways, so does it matter how fast - * it _doesn't_ work? + * A table would not have the same benefit. Most compilers will generate + * code very similar to a table in any case, so there is little performance + * gain from a table. Something is broken for the user's application, + * anyways, so does it matter how fast it _does not_ work? * - * The line number for the error will be near this comment, which - * is why it is here, and not at the start of the switch. + * The line number for the error will be near this comment, which is why it + * is here, and not at the start of the switch. */ return "Unknown error"; #else @@ -795,7 +794,7 @@ get_winapi_error(int err, char *buf, size_t buflen) expect the local codepage (eg fprintf, failf, infof). FormatMessageW -> wcstombs is used for Windows CE compatibility. */ if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, + FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err, LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { size_t written = wcstombs(buf, wbuf, buflen - 1); if(written != (size_t)-1) @@ -823,9 +822,9 @@ get_winapi_error(int err, char *buf, size_t buflen) * The 'err' argument passed in to this function MUST be a true errno number * as reported on this system. We do no range checking on the number before * we pass it to the "number-to-message" conversion function and there might - * be systems that don't do proper range checking in there themselves. + * be systems that do not do proper range checking in there themselves. * - * We don't do range checking (on systems other than Windows) since there is + * We do not do range checking (on systems other than Windows) since there is * no good reliable and portable way to do it. * * On Windows different types of error codes overlap. This function has an @@ -865,7 +864,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen) #ifdef USE_WINSOCK !get_winsock_error(err, buf, buflen) && #endif - !get_winapi_error((DWORD)err, buf, buflen)) + !get_winapi_error(err, buf, buflen)) msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err); } #else /* not Windows coming up */ @@ -944,7 +943,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) *buf = '\0'; #ifndef CURL_DISABLE_VERBOSE_STRINGS - if(!get_winapi_error(err, buf, buflen)) { + if(!get_winapi_error((int)err, buf, buflen)) { msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err); } #else diff --git a/Utilities/cmcurl/lib/strtok.c b/Utilities/cmcurl/lib/strtok.c index d8e1e8183..d2cc71c47 100644 --- a/Utilities/cmcurl/lib/strtok.c +++ b/Utilities/cmcurl/lib/strtok.c @@ -65,4 +65,4 @@ Curl_strtok_r(char *ptr, const char *sep, char **end) return NULL; } -#endif /* this was only compiled if strtok_r wasn't present */ +#endif /* this was only compiled if strtok_r was not present */ diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c index 580fd23bf..f1c7ba271 100644 --- a/Utilities/cmcurl/lib/strtoofft.c +++ b/Utilities/cmcurl/lib/strtoofft.c @@ -31,7 +31,7 @@ * NOTE: * * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we - * could use in case strtoll() doesn't exist... See + * could use in case strtoll() does not exist... See * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html */ @@ -73,7 +73,7 @@ static const char valchars[] = static int get_char(char c, int base); /** - * Custom version of the strtooff function. This extracts a curl_off_t + * Custom version of the strtooff function. This extracts a curl_off_t * value from the given input string and returns it. */ static curl_off_t strtooff(const char *nptr, char **endptr, int base) @@ -120,8 +120,8 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base) } } - /* Matching strtol, if the base is 0 and it doesn't look like - * the number is octal or hex, we assume it's base 10. + /* Matching strtol, if the base is 0 and it does not look like + * the number is octal or hex, we assume it is base 10. */ if(base == 0) { base = 10; @@ -168,7 +168,7 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base) * @param c the character to interpret according to base * @param base the base in which to interpret c * - * @return the value of c in base, or -1 if c isn't in range + * @return the value of c in base, or -1 if c is not in range */ static int get_char(char c, int base) { @@ -204,10 +204,10 @@ static int get_char(char c, int base) return value; } -#endif /* Only present if we need strtoll, but don't have it. */ +#endif /* Only present if we need strtoll, but do not have it. */ /* - * Parse a *positive* up to 64 bit number written in ascii. + * Parse a *positive* up to 64-bit number written in ASCII. */ CURLofft curlx_strtoofft(const char *str, char **endp, int base, curl_off_t *num) @@ -222,7 +222,7 @@ CURLofft curlx_strtoofft(const char *str, char **endp, int base, str++; if(('-' == *str) || (ISSPACE(*str))) { if(endp) - *endp = (char *)str; /* didn't actually move */ + *endp = (char *)str; /* did not actually move */ return CURL_OFFT_INVAL; /* nothing parsed */ } number = strtooff(str, &end, base); diff --git a/Utilities/cmcurl/lib/strtoofft.h b/Utilities/cmcurl/lib/strtoofft.h index 34d293ba3..71808b719 100644 --- a/Utilities/cmcurl/lib/strtoofft.h +++ b/Utilities/cmcurl/lib/strtoofft.h @@ -30,7 +30,7 @@ * Determine which string to integral data type conversion function we use * to implement string conversion to our curl_off_t integral data type. * - * Notice that curl_off_t might be 64 or 32 bit wide, and that it might use + * Notice that curl_off_t might be 64 or 32 bits wide, and that it might use * an underlying data type which might be 'long', 'int64_t', 'long long' or * '__int64' and more remotely other data types. * diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c index d2862de92..f4dbe0310 100644 --- a/Utilities/cmcurl/lib/system_win32.c +++ b/Utilities/cmcurl/lib/system_win32.c @@ -38,25 +38,18 @@ LARGE_INTEGER Curl_freq; bool Curl_isVistaOrGreater; -bool Curl_isWindows8OrGreater; /* Handle of iphlpapp.dll */ static HMODULE s_hIpHlpApiDll = NULL; -/* Function pointers */ +/* Pointer to the if_nametoindex function */ IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; -FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW = NULL; -GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel = NULL; -GETADDRINFOEXW_FN Curl_GetAddrInfoExW = NULL; -/* Curl_win32_init() performs win32 global initialization */ +/* Curl_win32_init() performs Win32 global initialization */ CURLcode Curl_win32_init(long flags) { -#ifdef USE_WINSOCK - HMODULE ws2_32Dll; -#endif /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which - is just for Winsock at the moment. Any required win32 initialization + is just for Winsock at the moment. Any required Win32 initialization should take place after this block. */ if(flags & CURL_GLOBAL_WIN32) { #ifdef USE_WINSOCK @@ -68,7 +61,7 @@ CURLcode Curl_win32_init(long flags) res = WSAStartup(wVersionRequested, &wsaData); if(res) - /* Tell the user that we couldn't find a usable */ + /* Tell the user that we could not find a usable */ /* winsock.dll. */ return CURLE_FAILED_INIT; @@ -80,7 +73,7 @@ CURLcode Curl_win32_init(long flags) if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) { - /* Tell the user that we couldn't find a usable */ + /* Tell the user that we could not find a usable */ /* winsock.dll. */ WSACleanup(); @@ -111,18 +104,6 @@ CURLcode Curl_win32_init(long flags) Curl_if_nametoindex = pIfNameToIndex; } -#ifdef USE_WINSOCK - ws2_32Dll = GetModuleHandleA("ws2_32"); - if(ws2_32Dll) { - Curl_FreeAddrInfoExW = CURLX_FUNCTION_CAST(FREEADDRINFOEXW_FN, - GetProcAddress(ws2_32Dll, "FreeAddrInfoExW")); - Curl_GetAddrInfoExCancel = CURLX_FUNCTION_CAST(GETADDRINFOEXCANCEL_FN, - GetProcAddress(ws2_32Dll, "GetAddrInfoExCancel")); - Curl_GetAddrInfoExW = CURLX_FUNCTION_CAST(GETADDRINFOEXW_FN, - GetProcAddress(ws2_32Dll, "GetAddrInfoExW")); - } -#endif - /* curlx_verify_windows_version must be called during init at least once because it has its own initialization routine. */ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, @@ -132,13 +113,6 @@ CURLcode Curl_win32_init(long flags) else Curl_isVistaOrGreater = FALSE; - if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - Curl_isWindows8OrGreater = TRUE; - } - else - Curl_isWindows8OrGreater = FALSE; - QueryPerformanceFrequency(&Curl_freq); return CURLE_OK; } @@ -146,9 +120,6 @@ CURLcode Curl_win32_init(long flags) /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ void Curl_win32_cleanup(long init_flags) { - Curl_FreeAddrInfoExW = NULL; - Curl_GetAddrInfoExCancel = NULL; - Curl_GetAddrInfoExW = NULL; if(s_hIpHlpApiDll) { FreeLibrary(s_hIpHlpApiDll); s_hIpHlpApiDll = NULL; @@ -208,7 +179,7 @@ HMODULE Curl_load_library(LPCTSTR filename) HMODULE hModule = NULL; LOADLIBRARYEX_FN pLoadLibraryEx = NULL; - /* Get a handle to kernel32 so we can access it's functions at runtime */ + /* Get a handle to kernel32 so we can access its functions at runtime */ HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32")); if(!hKernel32) return NULL; @@ -219,7 +190,7 @@ HMODULE Curl_load_library(LPCTSTR filename) CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN, (GetProcAddress(hKernel32, LOADLIBARYEX))); - /* Detect if there's already a path in the filename and load the library if + /* Detect if there is already a path in the filename and load the library if there is. Note: Both back slashes and forward slashes have been supported since the earlier days of DOS at an API level although they are not supported by command prompt */ @@ -261,7 +232,7 @@ HMODULE Curl_load_library(LPCTSTR filename) } return hModule; #else - /* the Universal Windows Platform (UWP) can't do this */ + /* the Universal Windows Platform (UWP) cannot do this */ (void)filename; return NULL; #endif diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index bd490cabc..024d959f3 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -26,11 +26,12 @@ #include "curl_setup.h" -#ifdef _WIN32 +#if defined(_WIN32) + +#include extern LARGE_INTEGER Curl_freq; extern bool Curl_isVistaOrGreater; -extern bool Curl_isWindows8OrGreater; CURLcode Curl_win32_init(long flags); void Curl_win32_cleanup(long init_flags); @@ -41,33 +42,6 @@ typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); /* This is used instead of if_nametoindex if available on Windows */ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; -/* Identical copy of addrinfoexW/ADDRINFOEXW */ -typedef struct addrinfoexW_ -{ - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - PWSTR ai_canonname; - struct sockaddr *ai_addr; - void *ai_blob; - size_t ai_bloblen; - LPGUID ai_provider; - struct addrinfoexW_ *ai_next; -} ADDRINFOEXW_; - -typedef void (CALLBACK *LOOKUP_COMPLETION_FN)(DWORD, DWORD, LPWSAOVERLAPPED); -typedef void (WSAAPI *FREEADDRINFOEXW_FN)(ADDRINFOEXW_*); -typedef int (WSAAPI *GETADDRINFOEXCANCEL_FN)(LPHANDLE); -typedef int (WSAAPI *GETADDRINFOEXW_FN)(PCWSTR, PCWSTR, DWORD, LPGUID, - const ADDRINFOEXW_*, ADDRINFOEXW_**, struct timeval*, LPOVERLAPPED, - LOOKUP_COMPLETION_FN, LPHANDLE); - -extern FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW; -extern GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel; -extern GETADDRINFOEXW_FN Curl_GetAddrInfoExW; - /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); #else /* _WIN32 */ diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 227a166f2..8cd19b1b0 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -798,12 +798,12 @@ static CURLcode check_telnet_options(struct Curl_easy *data) struct TELNET *tn = data->req.p.telnet; CURLcode result = CURLE_OK; - /* Add the user name as an environment variable if it + /* Add the username as an environment variable if it was given on the command line */ if(data->state.aptr.user) { char buffer[256]; if(str_is_nonascii(data->conn->user)) { - DEBUGF(infof(data, "set a non ASCII user name in telnet")); + DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); @@ -1191,12 +1191,12 @@ process_iac: if(c != CURL_SE) { if(c != CURL_IAC) { /* - * This is an error. We only expect to get "IAC IAC" or "IAC SE". - * Several things may have happened. An IAC was not doubled, the + * This is an error. We only expect to get "IAC IAC" or "IAC SE". + * Several things may have happened. An IAC was not doubled, the * IAC SE was left off, or another option got inserted into the - * suboption are all possibilities. If we assume that the IAC was + * suboption are all possibilities. If we assume that the IAC was * not doubled, and really the IAC SE was left off, we could get - * into an infinite loop here. So, instead, we terminate the + * into an infinite loop here. So, instead, we terminate the * suboption, and process the partial suboption if we can. */ CURL_SB_ACCUM(tn, CURL_IAC); @@ -1276,7 +1276,7 @@ static CURLcode send_telnet_data(struct Curl_easy *data, default: /* write! */ bytes_written = 0; result = Curl_xfer_send(data, outbuf + total_written, - outlen - total_written, &bytes_written); + outlen - total_written, FALSE, &bytes_written); total_written += bytes_written; break; } @@ -1342,7 +1342,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) #ifdef USE_WINSOCK /* We want to wait for both stdin and the socket. Since - ** the select() function in winsock only works on sockets + ** the select() function in Winsock only works on sockets ** we have to use the WaitForMultipleObjects() call. */ @@ -1353,7 +1353,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - /* Tell winsock what events we want to listen to */ + /* Tell Winsock what events we want to listen to */ if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { WSACloseEvent(event_handle); return CURLE_OK; @@ -1370,7 +1370,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) else use the old WaitForMultipleObjects() way */ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || data->set.is_fread_set) { - /* Don't wait for stdin_handle, just wait for event_handle */ + /* Do not wait for stdin_handle, just wait for event_handle */ obj_count = 1; /* Check stdin_handle per 100 milliseconds */ wait_timeout = 100; @@ -1470,7 +1470,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) if(events.lNetworkEvents & FD_READ) { /* read data from network */ result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); - /* read would've blocked. Loop again */ + /* read would have blocked. Loop again */ if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ @@ -1492,7 +1492,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* Negotiate if the peer has started negotiating, - otherwise don't. We don't want to speak telnet with + otherwise do not. We do not want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(data); @@ -1544,7 +1544,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) while(keepon) { DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt)); - switch(Curl_poll(pfd, poll_cnt, interval_ms)) { + switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; continue; @@ -1556,7 +1556,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) if(pfd[0].revents & POLLIN) { /* read data from network */ result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); - /* read would've blocked. Loop again */ + /* read would have blocked. Loop again */ if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ @@ -1588,7 +1588,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* Negotiate if the peer has started negotiating, - otherwise don't. We don't want to speak telnet with + otherwise do not. We do not want to speak telnet with non-telnet servers, like POP or SMTP. */ if(tn->please_negotiate && !tn->already_negotiated) { negotiate(data); @@ -1645,7 +1645,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } #endif /* mark this as "no further transfer wanted" */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); return result; } diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index 310a0faba..dbae202d5 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c @@ -240,12 +240,11 @@ static CURLcode tftp_set_timeouts(struct tftp_state_data *state) state->retry_time = 1; infof(state->data, - "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T - ", retry %d maxtry %d", + "set timeouts for state %d; Total % " FMT_OFF_T ", retry %d maxtry %d", (int)state->state, timeout_ms, state->retry_time, state->retry_max); /* init RX time */ - time(&state->rx_time); + state->rx_time = time(NULL); return CURLE_OK; } @@ -315,7 +314,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, const char *tmp = ptr; struct Curl_easy *data = state->data; - /* if OACK doesn't contain blksize option, the default (512) must be used */ + /* if OACK does not contain blksize option, the default (512) must be used */ state->blksize = TFTP_BLKSIZE_DEFAULT; while(tmp < ptr + len) { @@ -349,7 +348,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, return CURLE_TFTP_ILLEGAL; } else if(blksize > state->requested_blksize) { - /* could realloc pkt buffers here, but the spec doesn't call out + /* could realloc pkt buffers here, but the spec does not call out * support for the server requesting a bigger blksize than the client * requests */ failf(data, "%s (%ld)", @@ -434,7 +433,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, struct Curl_easy *data = state->data; CURLcode result = CURLE_OK; - /* Set ascii mode if -B flag was used */ + /* Set ASCII mode if -B flag was used */ if(data->state.prefer_ascii) mode = "netascii"; @@ -461,7 +460,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, setpacketevent(&state->spacket, TFTP_EVENT_RRQ); } /* As RFC3617 describes the separator slash is not actually part of the - file name so we skip the always-present first letter of the path + filename so we skip the always-present first letter of the path string. */ result = Curl_urldecode(&state->data->state.up.path[1], 0, &filename, NULL, REJECT_ZERO); @@ -469,9 +468,9 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, return result; if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { - failf(data, "TFTP file name too long"); + failf(data, "TFTP filename too long"); free(filename); - return CURLE_TFTP_ILLEGAL; /* too long file name field */ + return CURLE_TFTP_ILLEGAL; /* too long filename field */ } msnprintf((char *)state->spacket.data + 2, @@ -484,7 +483,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, char buf[64]; /* add tsize option */ if(data->state.upload && (data->state.infilesize != -1)) - msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T, + msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, data->state.infilesize); else strcpy(buf, "0"); /* the destination is large enough */ @@ -528,7 +527,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, senddata = sendto(state->sockfd, (void *)state->spacket.data, (SEND_TYPE_ARG3)sbytes, 0, &data->conn->remote_addr->sa_addr, - data->conn->remote_addr->addrlen); + (curl_socklen_t)data->conn->remote_addr->addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); @@ -590,7 +589,7 @@ static CURLcode tftp_rx(struct tftp_state_data *state, /* Is this the block we expect? */ rblock = getrpacketblock(&state->rpacket); if(NEXT_BLOCKNUM(state->block) == rblock) { - /* This is the expected block. Reset counters and ACK it. */ + /* This is the expected block. Reset counters and ACK it. */ state->retries = 0; } else if(state->block == rblock) { @@ -626,7 +625,7 @@ static CURLcode tftp_rx(struct tftp_state_data *state, else { state->state = TFTP_STATE_RX; } - time(&state->rx_time); + state->rx_time = time(NULL); break; case TFTP_EVENT_OACK: @@ -644,16 +643,16 @@ static CURLcode tftp_rx(struct tftp_state_data *state, return CURLE_SEND_ERROR; } - /* we're ready to RX data */ + /* we are ready to RX data */ state->state = TFTP_STATE_RX; - time(&state->rx_time); + state->rx_time = time(NULL); break; case TFTP_EVENT_TIMEOUT: /* Increment the retry count and fail if over the limit */ state->retries++; infof(data, - "Timeout waiting for block %d ACK. Retries = %d", + "Timeout waiting for block %d ACK. Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); if(state->retries > state->retry_max) { state->error = TFTP_ERR_TIMEOUT; @@ -679,8 +678,8 @@ static CURLcode tftp_rx(struct tftp_state_data *state, 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); - /* don't bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we're done */ + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ state->state = TFTP_STATE_FIN; break; @@ -719,13 +718,13 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) int rblock = getrpacketblock(&state->rpacket); if(rblock != state->block && - /* There's a bug in tftpd-hpa that causes it to send us an ack for - * 65535 when the block number wraps to 0. So when we're expecting + /* There is a bug in tftpd-hpa that causes it to send us an ack for + * 65535 when the block number wraps to 0. So when we are expecting * 0, also accept 65535. See * https://www.syslinux.org/archives/2010-September/015612.html * */ !(state->block == 0 && rblock == 65535)) { - /* This isn't the expected block. Log it and up the retry counter */ + /* This is not the expected block. Log it and up the retry counter */ infof(data, "Received ACK for block %d, expecting %d", rblock, state->block); state->retries++; @@ -738,7 +737,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) else { /* Re-send the data packet */ sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); /* Check all sbytes were sent */ @@ -751,9 +750,9 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) return result; } - /* This is the expected packet. Reset the counters and send the next + /* This is the expected packet. Reset the counters and send the next block */ - time(&state->rx_time); + state->rx_time = time(NULL); state->block++; } else @@ -783,7 +782,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) } while(state->sbytes < state->blksize && cb); sbytes = sendto(state->sockfd, (void *) state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); /* Check all sbytes were sent */ @@ -801,7 +800,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) state->retries++; infof(data, "Timeout waiting for block %d ACK. " " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); - /* Decide if we've had enough */ + /* Decide if we have had enough */ if(state->retries > state->retry_max) { state->error = TFTP_ERR_TIMEOUT; state->state = TFTP_STATE_FIN; @@ -809,7 +808,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) else { /* Re-send the data packet */ sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, + 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); /* Check all sbytes were sent */ @@ -829,8 +828,8 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); - /* don't bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we're done */ + /* do not bother with the return code, but if the socket is still up we + * should be a good TFTP client and let the server know we are done */ state->state = TFTP_STATE_FIN; break; @@ -1001,7 +1000,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; } - /* we don't keep TFTP connections up basically because there's none or very + /* we do not keep TFTP connections up basically because there is none or very * little gain for UDP */ connclose(conn, "TFTP"); @@ -1032,7 +1031,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) * IPv4 and IPv6... */ int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, - conn->remote_addr->addrlen); + (curl_socklen_t)conn->remote_addr->addrlen); if(rc) { char buffer[STRERROR_LEN]; failf(data, "bind() failed; %s", @@ -1110,7 +1109,7 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) fromlen = sizeof(fromaddr); state->rbytes = (int)recvfrom(state->sockfd, (void *)state->rpacket.data, - state->blksize + 4, + (RECV_TYPE_ARG3)state->blksize + 4, 0, (struct sockaddr *)&fromaddr, &fromlen); @@ -1132,7 +1131,7 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data) switch(state->event) { case TFTP_EVENT_DATA: - /* Don't pass to the client empty or retransmitted packets */ + /* Do not pass to the client empty or retransmitted packets */ if(state->rbytes > 4 && (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { result = Curl_client_write(data, CLIENTWRITE_BODY, @@ -1208,7 +1207,7 @@ static timediff_t tftp_state_timeout(struct Curl_easy *data, if(current > state->rx_time + state->retry_time) { if(event) *event = TFTP_EVENT_TIMEOUT; - time(&state->rx_time); /* update even though we received nothing */ + state->rx_time = time(NULL); /* update even though we received nothing */ } return timeout_ms; @@ -1241,8 +1240,8 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) return result; *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) - /* Tell curl we're done */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + /* Tell curl we are done */ + Curl_xfer_setup_nop(data); } else { /* no timeouts to handle, check our socket */ @@ -1264,8 +1263,8 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) return result; *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; if(*done) - /* Tell curl we're done */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + /* Tell curl we are done */ + Curl_xfer_setup_nop(data); } /* if rc == 0, then select() timed out */ } @@ -1289,7 +1288,7 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done) DEBUGF(infof(data, "DO phase is complete")); } else if(!result) { - /* The multi code doesn't have this logic for the DOING state so we + /* The multi code does not have this logic for the DOING state so we provide it for TFTP since it may do the entire transfer in this state. */ if(Curl_pgrsUpdate(data)) @@ -1376,7 +1375,7 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, conn->transport = TRNSPRT_UDP; /* TFTP URLs support an extension like ";mode=" that - * we'll try to get now! */ + * we will try to get now! */ type = strstr(data->state.up.path, ";mode="); if(!type) diff --git a/Utilities/cmcurl/lib/timediff.h b/Utilities/cmcurl/lib/timediff.h index fb318d4f2..75f996c55 100644 --- a/Utilities/cmcurl/lib/timediff.h +++ b/Utilities/cmcurl/lib/timediff.h @@ -26,10 +26,10 @@ #include "curl_setup.h" -/* Use a larger type even for 32 bit time_t systems so that we can keep +/* Use a larger type even for 32-bit time_t systems so that we can keep microsecond accuracy in it */ typedef curl_off_t timediff_t; -#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T +#define FMT_TIMEDIFF_T FMT_OFF_T #define TIMEDIFF_T_MAX CURL_OFF_T_MAX #define TIMEDIFF_T_MIN CURL_OFF_T_MIN diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c index 5a6727cbc..bb29bfdfe 100644 --- a/Utilities/cmcurl/lib/timeval.c +++ b/Utilities/cmcurl/lib/timeval.c @@ -51,8 +51,8 @@ struct curltime Curl_now(void) #pragma warning(pop) #endif - now.tv_sec = milliseconds / 1000; - now.tv_usec = (milliseconds % 1000) * 1000; + now.tv_sec = (time_t)(milliseconds / 1000); + now.tv_usec = (int)((milliseconds % 1000) * 1000); } return now; } @@ -77,7 +77,7 @@ struct curltime Curl_now(void) /* ** clock_gettime() may be defined by Apple's SDK as weak symbol thus - ** code compiles but fails during run-time if clock_gettime() is + ** code compiles but fails during runtime if clock_gettime() is ** called on unsupported OS version. */ #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ @@ -95,7 +95,7 @@ struct curltime Curl_now(void) #endif (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) { cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); + cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); } else #endif @@ -107,18 +107,18 @@ struct curltime Curl_now(void) #endif (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) { cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); + cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); } /* ** Even when the configure process has truly detected monotonic clock ** availability, it might happen that it is not actually available at - ** run-time. When this occurs simply fallback to other time source. + ** runtime. When this occurs simply fallback to other time source. */ #ifdef HAVE_GETTIMEOFDAY else { (void)gettimeofday(&now, NULL); cnow.tv_sec = now.tv_sec; - cnow.tv_usec = (unsigned int)now.tv_usec; + cnow.tv_usec = (int)now.tv_usec; } #else else { @@ -137,7 +137,7 @@ struct curltime Curl_now(void) struct curltime Curl_now(void) { /* - ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which + ** Monotonic timer on macOS is provided by mach_absolute_time(), which ** returns time in Mach "absolute time units," which are platform-dependent. ** To convert to nanoseconds, one must use conversion factors specified by ** mach_timebase_info(). diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 744227eb3..b66005443 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -53,7 +53,7 @@ #endif #ifndef HAVE_SOCKET -#error "We can't compile without socket() support!" +#error "We cannot compile without socket() support!" #endif #include "urldata.h" @@ -160,6 +160,42 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc) return TRUE; } +static CURLcode xfer_recv_shutdown(struct Curl_easy *data, bool *done) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->sockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_conn_shutdown(data, sockindex, done); +} + +static bool xfer_recv_shutdown_started(struct Curl_easy *data) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->sockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_shutdown_started(data, sockindex); +} + +CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) +{ + int sockindex; + + if(!data || !data->conn) + return CURLE_FAILED_INIT; + if(data->conn->writesockfd == CURL_SOCKET_BAD) + return CURLE_FAILED_INIT; + sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]); + return Curl_conn_shutdown(data, sockindex, done); +} + /** * Receive raw response data for the transfer. * @param data the transfer @@ -186,17 +222,35 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data, else if(totalleft < (curl_off_t)blen) blen = (size_t)totalleft; } + else if(xfer_recv_shutdown_started(data)) { + /* we already reveived everything. Do not try more. */ + blen = 0; + } if(!blen) { - /* want nothing - continue as if read nothing. */ - DEBUGF(infof(data, "readwrite_data: we're done")); + /* want nothing more */ *err = CURLE_OK; - return 0; + nread = 0; + } + else { + *err = Curl_xfer_recv(data, buf, blen, &nread); } - *err = Curl_xfer_recv(data, buf, blen, &nread); if(*err) return -1; + if(nread == 0) { + if(data->req.shutdown) { + bool done; + *err = xfer_recv_shutdown(data, &done); + if(*err) + return -1; + if(!done) { + *err = CURLE_AGAIN; + return -1; + } + } + DEBUGF(infof(data, "sendrecv_dl: we are done")); + } DEBUGASSERT(nread >= 0); return nread; } @@ -206,9 +260,9 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data, * the stream was rewound (in which case we have data in a * buffer) */ -static CURLcode readwrite_data(struct Curl_easy *data, - struct SingleRequest *k, - int *didwhat) +static CURLcode sendrecv_dl(struct Curl_easy *data, + struct SingleRequest *k, + int *didwhat) { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; @@ -239,24 +293,31 @@ static CURLcode readwrite_data(struct Curl_easy *data, buf = xfer_buf; bytestoread = xfer_blen; - if(bytestoread && data->set.max_recv_speed) { + if(bytestoread && data->set.max_recv_speed > 0) { /* In case of speed limit on receiving: if this loop already got * data, break out. If not, limit the amount of bytes to receive. * The overall, timed, speed limiting is done in multi.c */ if(total_received) break; - if((size_t)data->set.max_recv_speed < bytestoread) + if(data->set.max_recv_speed < (curl_off_t)bytestoread) bytestoread = (size_t)data->set.max_recv_speed; } nread = Curl_xfer_recv_resp(data, buf, bytestoread, is_multiplex, &result); if(nread < 0) { - if(CURLE_AGAIN == result) { - result = CURLE_OK; - break; /* get out of loop */ + if(CURLE_AGAIN != result) + goto out; /* real error */ + result = CURLE_OK; + if(data->req.download_done && data->req.no_body && + !data->req.resp_trailer) { + DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, " + "not waiting for EOS")); + nread = 0; + /* continue as if we read the EOS */ } - goto out; /* real error */ + else + break; /* get out of loop */ } /* We only get a 0-length read on EndOfStream */ @@ -271,7 +332,9 @@ static CURLcode readwrite_data(struct Curl_easy *data, DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); - k->keepon &= ~(KEEP_RECV|KEEP_SEND); /* stop sending as well */ + result = Curl_req_stop_send_recv(data); + if(result) + goto out; if(k->eos_written) /* already did write this to client, leave */ break; } @@ -291,10 +354,11 @@ static CURLcode readwrite_data(struct Curl_easy *data, if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) break; - } while(maxloops-- && data_pending(data)); + } while(maxloops--); - if(maxloops <= 0) { - /* did not read until EAGAIN, mark read-again-please */ + if((maxloops <= 0) || data_pending(data)) { + /* did not read until EAGAIN or there is still pending data, mark as + read-again-please */ data->state.select_bits = CURL_CSELECT_IN; if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) data->state.select_bits |= CURL_CSELECT_OUT; @@ -302,55 +366,25 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && (conn->bits.close || is_multiplex)) { - /* When we've read the entire thing and the close bit is set, the server - may now close the connection. If there's now any kind of sending going + /* When we have read the entire thing and the close bit is set, the server + may now close the connection. If there is now any kind of sending going on from our side, we need to stop that immediately. */ infof(data, "we are done reading and this is set to close, stop send"); - k->keepon &= ~KEEP_SEND; /* no writing anymore either */ - k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */ + Curl_req_abort_sending(data); } out: Curl_multi_xfer_buf_release(data, xfer_buf); if(result) - DEBUGF(infof(data, "readwrite_data() -> %d", result)); + DEBUGF(infof(data, "sendrecv_dl() -> %d", result)); return result; } -#if defined(_WIN32) && defined(USE_WINSOCK) -#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY -#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B -#endif - -static void win_update_buffer_size(curl_socket_t sockfd) -{ - int result; - ULONG ideal; - DWORD ideallen; - result = WSAIoctl(sockfd, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0, - &ideal, sizeof(ideal), &ideallen, 0, 0); - if(result == 0) { - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, - (const char *)&ideal, sizeof(ideal)); - } -} -#else -#define win_update_buffer_size(x) -#endif - -#define curl_upload_refill_watermark(data) \ - ((size_t)((data)->set.upload_buffer_size >> 5)) - /* * Send data to upload to the server, when the socket is writable. */ -static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat) +static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat) { - CURLcode result = CURLE_OK; - - if((data->req.keepon & KEEP_SEND_PAUSE)) - return CURLE_OK; - /* We should not get here when the sending is already done. It * probably means that someone set `data-req.keepon |= KEEP_SEND` * when it should not. */ @@ -358,23 +392,9 @@ static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat) if(!Curl_req_done_sending(data)) { *didwhat |= KEEP_SEND; - result = Curl_req_send_more(data); - if(result) - return result; - -#if defined(_WIN32) && defined(USE_WINSOCK) - /* FIXME: this looks like it would fit better into cf-socket.c - * but then I do not know enough Windows to say... */ - { - struct curltime n = Curl_now(); - if(Curl_timediff(n, data->conn->last_sndbuf_update) > 1000) { - win_update_buffer_size(data->conn->writesockfd); - data->conn->last_sndbuf_update = n; - } - } -#endif + return Curl_req_send_more(data); } - return result; + return CURLE_OK; } static int select_bits_paused(struct Curl_easy *data, int select_bits) @@ -396,84 +416,46 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits) } /* - * Curl_readwrite() is the low-level function to be called when data is to + * Curl_sendrecv() is the low-level function to be called when data is to * be read and written to/from the connection. */ -CURLcode Curl_readwrite(struct Curl_easy *data) +CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) { - struct connectdata *conn = data->conn; struct SingleRequest *k = &data->req; - CURLcode result; - struct curltime now; + CURLcode result = CURLE_OK; int didwhat = 0; - int select_bits; - - /* Check if client writes had been paused and can resume now. */ - if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { - Curl_conn_ev_data_pause(data, FALSE); - result = Curl_cwriter_unpause(data); - if(result) - goto out; - } + DEBUGASSERT(nowp); if(data->state.select_bits) { if(select_bits_paused(data, data->state.select_bits)) { /* leave the bits unchanged, so they'll tell us what to do when * this transfer gets unpaused. */ - DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED")); result = CURLE_OK; goto out; } - select_bits = data->state.select_bits; data->state.select_bits = 0; } - else { - curl_socket_t fd_read; - curl_socket_t fd_write; - /* only use the proper socket if the *_HOLD bit is not set simultaneously - as then we are in rate limiting state in that transfer direction */ - if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) - fd_read = conn->sockfd; - else - fd_read = CURL_SOCKET_BAD; - - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - fd_write = conn->writesockfd; - else - fd_write = CURL_SOCKET_BAD; - - select_bits = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0); - } - - if(select_bits == CURL_CSELECT_ERR) { - failf(data, "select/poll returned error"); - result = CURLE_SEND_ERROR; - goto out; - } #ifdef USE_HYPER - if(conn->datastream) { - result = conn->datastream(data, conn, &didwhat, select_bits); + if(data->conn->datastream) { + result = data->conn->datastream(data, data->conn, &didwhat, + CURL_CSELECT_OUT|CURL_CSELECT_IN); if(result || data->req.done) goto out; } else { #endif - /* We go ahead and do a read if we have a readable socket or if - the stream was rewound (in which case we have data in a - buffer) */ - if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) { - result = readwrite_data(data, k, &didwhat); + /* We go ahead and do a read if we have a readable socket or if the stream + was rewound (in which case we have data in a buffer) */ + if(k->keepon & KEEP_RECV) { + result = sendrecv_dl(data, k, &didwhat); if(result || data->req.done) goto out; } /* If we still have writing to do, we check if we have a writable socket. */ - if(((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) || - (k->keepon & KEEP_SEND_TIMED)) { - /* write */ - - result = readwrite_upload(data, &didwhat); + if(Curl_req_want_send(data) || (data->req.keepon & KEEP_SEND_TIMED)) { + result = sendrecv_ul(data, &didwhat); if(result) goto out; } @@ -481,8 +463,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data) } #endif - now = Curl_now(); if(!didwhat) { + /* Transfer wanted to send/recv, but nothing was possible. */ result = Curl_conn_ev_data_idle(data); if(result) goto out; @@ -491,23 +473,23 @@ CURLcode Curl_readwrite(struct Curl_easy *data) if(Curl_pgrsUpdate(data)) result = CURLE_ABORTED_BY_CALLBACK; else - result = Curl_speedcheck(data, now); + result = Curl_speedcheck(data, *nowp); if(result) goto out; if(k->keepon) { - if(0 > Curl_timeleft(data, &now, FALSE)) { + if(0 > Curl_timeleft(data, nowp, FALSE)) { if(k->size != -1) { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" - CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(now, data->progress.t_startsingle), + failf(data, "Operation timed out after %" FMT_TIMEDIFF_T + " milliseconds with %" FMT_OFF_T " out of %" + FMT_OFF_T " bytes received", + Curl_timediff(*nowp, data->progress.t_startsingle), k->bytecount, k->size); } else { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(now, data->progress.t_startsingle), + failf(data, "Operation timed out after %" FMT_TIMEDIFF_T + " milliseconds with %" FMT_OFF_T " bytes received", + Curl_timediff(*nowp, data->progress.t_startsingle), k->bytecount); } result = CURLE_OPERATION_TIMEDOUT; @@ -520,16 +502,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data) * returning. */ if(!(data->req.no_body) && (k->size != -1) && - (k->bytecount != k->size) && -#ifdef CURL_DO_LINEEND_CONV - /* Most FTP servers don't adjust their file SIZE response for CRLFs, - so we'll check to see if the discrepancy can be explained - by the number of CRLFs we've changed to LFs. - */ - (k->bytecount != (k->size + data->state.crlf_conversions)) && -#endif /* CURL_DO_LINEEND_CONV */ - !k->newurl) { - failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T + (k->bytecount != k->size) && !k->newurl) { + failf(data, "transfer closed with %" FMT_OFF_T " bytes remaining to read", k->size - k->bytecount); result = CURLE_PARTIAL_FILE; goto out; @@ -546,7 +520,7 @@ CURLcode Curl_readwrite(struct Curl_easy *data) out: if(result) - DEBUGF(infof(data, "Curl_readwrite() -> %d", result)); + DEBUGF(infof(data, "Curl_sendrecv() -> %d", result)); return result; } @@ -569,7 +543,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) CURLcode result; if(!data->state.url && !data->set.uh) { - /* we can't do anything without URL */ + /* we cannot do anything without URL */ failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } @@ -593,7 +567,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } if(data->set.postfields && data->set.set_resume_from) { - /* we can't */ + /* we cannot */ failf(data, "cannot mix POSTFIELDS with RESUME_FROM"); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -695,7 +669,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) /* * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * basically anything through an HTTP proxy we can't limit this based on + * basically anything through an HTTP proxy we cannot limit this based on * protocol. */ if(data->set.str[STRING_USERAGENT]) { @@ -726,22 +700,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return result; } -/* - * Curl_posttransfer() is called immediately after a transfer ends - */ -CURLcode Curl_posttransfer(struct Curl_easy *data) -{ -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) - /* restore the signal handler for SIGPIPE before we get back */ - if(!data->set.no_signal) - signal(SIGPIPE, data->state.prev_signal); -#else - (void)data; /* unused parameter */ -#endif - - return CURLE_OK; -} - /* * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string * as given by the remote server and set up the new URL to request. @@ -823,16 +781,16 @@ CURLcode Curl_follow(struct Curl_easy *data, (data->req.httpcode != 401) && (data->req.httpcode != 407) && 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 */ + URL: do not allow a custom port number */ disallowport = TRUE; } DEBUGASSERT(data->state.uh); - 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 | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); + uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) + ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | + CURLU_ALLOW_SPACE | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); if(uc) { if(type != FOLLOW_FAKE) { failf(data, "The redirect target URL could not be parsed: %s", @@ -901,8 +859,8 @@ CURLcode Curl_follow(struct Curl_easy *data, } if(type == FOLLOW_FAKE) { - /* we're only figuring out the new url if we would've followed locations - but now we're done so we can get out! */ + /* we are only figuring out the new URL if we would have followed locations + but now we are done so we can get out! */ data->info.wouldredirect = newurl; if(reachedmax) { @@ -939,15 +897,15 @@ CURLcode Curl_follow(struct Curl_easy *data, /* 306 - Not used */ /* 307 - Temporary Redirect */ default: /* for all above (and the unknown ones) */ - /* Some codes are explicitly mentioned since I've checked RFC2616 and they - * seem to be OK to POST to. + /* Some codes are explicitly mentioned since I have checked RFC2616 and + * they seem to be OK to POST to. */ break; case 301: /* Moved Permanently */ /* (quote from RFC7231, section 6.4.2) * * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this + * method from POST to GET for the subsequent request. If this * behavior is undesired, the 307 (Temporary Redirect) status code * can be used instead. * @@ -973,7 +931,7 @@ CURLcode Curl_follow(struct Curl_easy *data, /* (quote from RFC7231, section 6.4.3) * * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this + * method from POST to GET for the subsequent request. If this * behavior is undesired, the 307 (Temporary Redirect) status code * can be used instead. * @@ -1014,14 +972,14 @@ CURLcode Curl_follow(struct Curl_easy *data, break; case 304: /* Not Modified */ /* 304 means we did a conditional request and it was "Not modified". - * We shouldn't get any Location: header in this response! + * We should not get any Location: header in this response! */ break; case 305: /* Use Proxy */ /* (quote from RFC2616, section 10.3.6): * "The requested resource MUST be accessed through the proxy given * by the Location field. The Location field gives the URI of the - * proxy. The recipient is expected to repeat this single request + * proxy. The recipient is expected to repeat this single request * via the proxy. 305 responses MUST only be generated by origin * servers." */ @@ -1043,8 +1001,9 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) bool retry = FALSE; *url = NULL; - /* if we're talking upload, we can't do the checks below, unless the protocol - is HTTP as when uploading over HTTP we will still get a response */ + /* if we are talking upload, we cannot do the checks below, unless the + protocol is HTTP as when uploading over HTTP we will still get a + response */ if(data->state.upload && !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) return CURLE_OK; @@ -1090,7 +1049,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) return CURLE_OUT_OF_MEMORY; connclose(conn, "retry"); /* close this connection */ - conn->bits.retry = TRUE; /* mark this as a connection we're about + conn->bits.retry = TRUE; /* mark this as a connection we are about to retry. Marking it this way should prevent i.e HTTP transfers to return error just because nothing has been @@ -1101,16 +1060,17 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) } /* - * Curl_xfer_setup() is called to setup some basic properties for the - * upcoming transfer. + * xfer_setup() is called to setup basic properties for the transfer. */ -void Curl_xfer_setup( +static void xfer_setup( struct Curl_easy *data, /* transfer */ int sockindex, /* socket index to read from or -1 */ curl_off_t size, /* -1 if unknown at this point */ bool getheader, /* TRUE if header parsing is wanted */ - int writesockindex /* socket index to write to, it may very well be + int writesockindex, /* socket index to write to, it may very well be the same we read from. -1 disables */ + bool shutdown /* shutdown connection at transfer end. Only + * supported when sending OR receiving. */ ) { struct SingleRequest *k = &data->req; @@ -1120,6 +1080,7 @@ void Curl_xfer_setup( DEBUGASSERT(conn != NULL); DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1)); + DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1)); if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) { /* when multiplexing, the read/write sockets need to be the same! */ @@ -1137,9 +1098,10 @@ void Curl_xfer_setup( conn->writesockfd = writesockindex == -1 ? CURL_SOCKET_BAD:conn->sock[writesockindex]; } - k->getheader = getheader; + k->getheader = getheader; k->size = size; + k->shutdown = shutdown; /* The code sequence below is placed in this function just because all necessary input is not always known in do_complete() as this function may @@ -1150,7 +1112,7 @@ void Curl_xfer_setup( if(size > 0) Curl_pgrsSetDownloadSize(data, size); } - /* we want header and/or body, if neither then don't do this! */ + /* we want header and/or body, if neither then do not do this! */ if(k->getheader || !data->req.no_body) { if(sockindex != -1) @@ -1162,6 +1124,33 @@ void Curl_xfer_setup( } +void Curl_xfer_setup_nop(struct Curl_easy *data) +{ + xfer_setup(data, -1, -1, FALSE, -1, FALSE); +} + +void Curl_xfer_setup1(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool getheader) +{ + int recv_index = (send_recv & CURL_XFER_RECV)? FIRSTSOCKET : -1; + int send_index = (send_recv & CURL_XFER_SEND)? FIRSTSOCKET : -1; + DEBUGASSERT((recv_index >= 0) || (recv_size == -1)); + xfer_setup(data, recv_index, recv_size, getheader, send_index, FALSE); +} + +void Curl_xfer_setup2(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool shutdown) +{ + int recv_index = (send_recv & CURL_XFER_RECV)? SECONDARYSOCKET : -1; + int send_index = (send_recv & CURL_XFER_SEND)? SECONDARYSOCKET : -1; + DEBUGASSERT((recv_index >= 0) || (recv_size == -1)); + xfer_setup(data, recv_index, recv_size, FALSE, send_index, shutdown); +} + CURLcode Curl_xfer_write_resp(struct Curl_easy *data, const char *buf, size_t blen, bool is_eos) @@ -1180,15 +1169,7 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, int cwtype = CLIENTWRITE_BODY; if(is_eos) cwtype |= CLIENTWRITE_EOS; - -#ifndef CURL_DISABLE_POP3 - if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) { - result = data->req.ignorebody? CURLE_OK : - Curl_pop3_write(data, buf, blen); - } - else -#endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(data, cwtype, buf, blen); + result = Curl_client_write(data, cwtype, buf, blen); } } @@ -1220,25 +1201,35 @@ CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature) return Curl_cw_out_done(data); } +bool Curl_xfer_needs_flush(struct Curl_easy *data) +{ + int sockindex; + sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) && + (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET])); + return Curl_conn_needs_flush(data, sockindex); +} + +CURLcode Curl_xfer_flush(struct Curl_easy *data) +{ + int sockindex; + sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) && + (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET])); + return Curl_conn_flush(data, sockindex); +} + CURLcode Curl_xfer_send(struct Curl_easy *data, - const void *buf, size_t blen, + const void *buf, size_t blen, bool eos, size_t *pnwritten) { CURLcode result; int sockindex; - if(!data || !data->conn) - return CURLE_FAILED_INIT; - /* FIXME: would like to enable this, but some protocols (MQTT) do not - * setup the transfer correctly, it seems - if(data->conn->writesockfd == CURL_SOCKET_BAD) { - failf(data, "transfer not setup for sending"); - DEBUGASSERT(0); - return CURLE_SEND_ERROR; - } */ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) && (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET])); - result = Curl_conn_send(data, sockindex, buf, blen, pnwritten); + result = Curl_conn_send(data, sockindex, buf, blen, eos, pnwritten); if(result == CURLE_AGAIN) { result = CURLE_OK; *pnwritten = 0; @@ -1246,6 +1237,8 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, else if(!result && *pnwritten) data->info.request_size += *pnwritten; + DEBUGF(infof(data, "Curl_xfer_send(len=%zu, eos=%d) -> %d, %zu", + blen, eos, result, *pnwritten)); return result; } @@ -1255,18 +1248,13 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data, { int sockindex; - if(!data || !data->conn) - return CURLE_FAILED_INIT; - /* FIXME: would like to enable this, but some protocols (MQTT) do not - * setup the transfer correctly, it seems - if(data->conn->sockfd == CURL_SOCKET_BAD) { - failf(data, "transfer not setup for receiving"); - DEBUGASSERT(0); - return CURLE_RECV_ERROR; - } */ + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + DEBUGASSERT(data->set.buffer_size > 0); + sockindex = ((data->conn->sockfd != CURL_SOCKET_BAD) && (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET])); - if(data->set.buffer_size > 0 && (size_t)data->set.buffer_size < blen) + if((size_t)data->set.buffer_size < blen) blen = (size_t)data->set.buffer_size; return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd); } @@ -1276,3 +1264,15 @@ CURLcode Curl_xfer_send_close(struct Curl_easy *data) Curl_conn_ev_data_done_send(data); return CURLE_OK; } + +bool Curl_xfer_is_blocked(struct Curl_easy *data) +{ + bool want_send = ((data)->req.keepon & KEEP_SEND); + bool want_recv = ((data)->req.keepon & KEEP_RECV); + if(!want_send) + return (want_recv && Curl_cwriter_is_paused(data)); + else if(!want_recv) + return (want_send && Curl_creader_is_paused(data)); + else + return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data); +} diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index ad0f3a20c..8d6f98d75 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h @@ -32,7 +32,6 @@ char *Curl_checkheaders(const struct Curl_easy *data, void Curl_init_CONNECT(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); -CURLcode Curl_posttransfer(struct Curl_easy *data); typedef enum { FOLLOW_NONE, /* not used within the function, just a placeholder to @@ -45,7 +44,7 @@ typedef enum { CURLcode Curl_follow(struct Curl_easy *data, char *newurl, followtype type); -CURLcode Curl_readwrite(struct Curl_easy *data); +CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp); int Curl_single_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks); CURLcode Curl_retry_request(struct Curl_easy *data, char **url); @@ -76,15 +75,37 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data, const char *hd0, size_t hdlen, bool is_eos); -/* This sets up a forthcoming transfer */ -void Curl_xfer_setup(struct Curl_easy *data, - int sockindex, /* socket index to read from or -1 */ - curl_off_t size, /* -1 if unknown at this point */ - bool getheader, /* TRUE if header parsing is wanted */ - int writesockindex /* socket index to write to. May be - the same we read from. -1 - disables */ - ); +#define CURL_XFER_NOP (0) +#define CURL_XFER_RECV (1<<(0)) +#define CURL_XFER_SEND (1<<(1)) +#define CURL_XFER_SENDRECV (CURL_XFER_RECV|CURL_XFER_SEND) + +/** + * The transfer is neither receiving nor sending now. + */ +void Curl_xfer_setup_nop(struct Curl_easy *data); + +/** + * The transfer will use socket 1 to send/recv. `recv_size` is + * the amount to receive or -1 if unknown. `getheader` indicates + * response header processing is expected. + */ +void Curl_xfer_setup1(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool getheader); + +/** + * The transfer will use socket 2 to send/recv. `recv_size` is + * the amount to receive or -1 if unknown. With `shutdown` being + * set, the transfer is only allowed to either send OR receive + * and the socket 2 connection will be shutdown at the end of + * the transfer. An unclean shutdown will fail the transfer. + */ +void Curl_xfer_setup2(struct Curl_easy *data, + int send_recv, + curl_off_t recv_size, + bool shutdown); /** * Multi has set transfer to DONE. Last chance to trigger @@ -92,13 +113,24 @@ void Curl_xfer_setup(struct Curl_easy *data, */ CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature); +/** + * Return TRUE iff transfer has pending data to send. Checks involved + * connection filters. + */ +bool Curl_xfer_needs_flush(struct Curl_easy *data); + +/** + * Flush any pending send data on the transfer connection. + */ +CURLcode Curl_xfer_flush(struct Curl_easy *data); + /** * Send data on the socket/connection filter designated * for transfer's outgoing data. * Will return CURLE_OK on blocking with (*pnwritten == 0). */ CURLcode Curl_xfer_send(struct Curl_easy *data, - const void *buf, size_t blen, + const void *buf, size_t blen, bool eos, size_t *pnwritten); /** @@ -111,5 +143,13 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data, ssize_t *pnrcvd); CURLcode Curl_xfer_send_close(struct Curl_easy *data); +CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done); + +/** + * Return TRUE iff the transfer is not done, but further progress + * is blocked. For example when it is only receiving and its writer + * is PAUSED. + */ +bool Curl_xfer_is_blocked(struct Curl_easy *data); #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index 2814d31ad..3bf0c0598 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -56,7 +56,7 @@ #endif #ifndef HAVE_SOCKET -#error "We can't compile without socket() support!" +#error "We cannot compile without socket() support!" #endif #include @@ -136,7 +136,7 @@ static void data_priority_cleanup(struct Curl_easy *data); #endif /* Some parts of the code (e.g. chunked encoding) assume this buffer has at - * more than just a few bytes to play with. Don't let it become too small or + * more than just a few bytes to play with. Do not let it become too small or * bad things will happen. */ #if READBUFFER_SIZE < READBUFFER_MIN @@ -234,8 +234,6 @@ CURLcode Curl_close(struct Curl_easy **datap) data = *datap; *datap = NULL; - Curl_expire_clear(data); /* shut off timers */ - /* Detach connection if any is left. This should not be normal, but can be the case for example with CONNECT_ONLY + recv/send (test 556) */ Curl_detach_connection(data); @@ -253,6 +251,8 @@ CURLcode Curl_close(struct Curl_easy **datap) } } + Curl_expire_clear(data); /* shut off any timers left */ + data->magic = 0; /* force a clear AFTER the possibly enforced removal from the multi handle, since that function uses the magic field! */ @@ -260,13 +260,12 @@ CURLcode Curl_close(struct Curl_easy **datap) if(data->state.rangestringalloc) free(data->state.range); - /* freed here just in case DONE wasn't called */ + /* freed here just in case DONE was not called */ Curl_req_free(&data->req, data); /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); Curl_safefree(data->state.first_host); - Curl_safefree(data->state.scratch); Curl_ssl_free_certinfo(data); if(data->state.referer_alloc) { @@ -365,7 +364,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->seek_client = ZERO_NULL; - set->filesize = -1; /* we don't know the size */ + set->filesize = -1; /* we do not know the size */ set->postfieldsize = -1; /* unknown size */ set->maxredirs = 30; /* sensible default */ @@ -415,8 +414,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->new_file_perms = 0644; /* Default permissions */ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; - set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | - CURLPROTO_FTPS; + set->redir_protocols = CURLPROTO_REDIR; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -467,6 +465,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->tcp_keepalive = FALSE; set->tcp_keepintvl = 60; set->tcp_keepidle = 60; + set->tcp_keepcnt = 9; set->tcp_fastopen = FALSE; set->tcp_nodelay = TRUE; set->ssl_enable_alpn = TRUE; @@ -536,9 +535,16 @@ CURLcode Curl_open(struct Curl_easy **curl) data->state.recent_conn_id = -1; /* and not assigned an id yet */ data->id = -1; + data->mid = -1; +#ifndef CURL_DISABLE_DOH + data->set.dohfor_mid = -1; +#endif data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ +#ifndef CURL_DISABLE_HTTP + Curl_llist_init(&data->state.httphdrs, NULL); +#endif } if(result) { @@ -551,23 +557,10 @@ CURLcode Curl_open(struct Curl_easy **curl) } else *curl = data; - return result; } -static void conn_shutdown(struct Curl_easy *data) -{ - DEBUGASSERT(data); - infof(data, "Closing connection"); - - /* possible left-overs from the async name resolvers */ - Curl_resolver_cancel(data); - - Curl_conn_close(data, SECONDARYSOCKET); - Curl_conn_close(data, FIRSTSOCKET); -} - -static void conn_free(struct Curl_easy *data, struct connectdata *conn) +void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) { size_t i; @@ -594,8 +587,8 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->sasl_authzid); Curl_safefree(conn->options); Curl_safefree(conn->oauth_bearer); - Curl_safefree(conn->host.rawalloc); /* host name buffer */ - Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ + Curl_safefree(conn->host.rawalloc); /* hostname buffer */ + Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ Curl_safefree(conn->hostname_resolve); Curl_safefree(conn->secondaryhostname); Curl_safefree(conn->localdev); @@ -604,13 +597,14 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) #ifdef USE_UNIX_SOCKETS Curl_safefree(conn->unix_domain_socket); #endif + Curl_safefree(conn->destination); free(conn); /* free all the connection oriented data */ } /* * Disconnects the given connection. Note the connection may not be the - * primary connection, like when freeing room in the connection cache or + * primary connection, like when freeing room in the connection pool or * killing of a dead old connection. * * A connection needs an easy handle when closing down. We support this passed @@ -618,18 +612,16 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn) * disassociated from an easy handle. * * This function MUST NOT reset state in the Curl_easy struct if that - * isn't strictly bound to the life-time of *this* particular connection. - * + * is not strictly bound to the life-time of *this* particular connection. */ - -void Curl_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) +bool Curl_on_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool aborted) { /* there must be a connection to close */ DEBUGASSERT(conn); - /* it must be removed from the connection cache */ - DEBUGASSERT(!conn->bundle); + /* it must be removed from the connection pool */ + DEBUGASSERT(!conn->bits.in_cpool); /* there must be an associated transfer */ DEBUGASSERT(data); @@ -637,22 +629,11 @@ void Curl_disconnect(struct Curl_easy *data, /* the transfer must be detached from the connection */ DEBUGASSERT(!data->conn); - DEBUGF(infof(data, "Curl_disconnect(conn #%" - CURL_FORMAT_CURL_OFF_T ", dead=%d)", - conn->connection_id, dead_connection)); - /* - * If this connection isn't marked to force-close, leave it open if there - * are other users of it - */ - if(CONN_INUSE(conn) && !dead_connection) { - DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); - return; - } + DEBUGF(infof(data, "Curl_disconnect(conn #%" FMT_OFF_T ", aborted=%d)", + conn->connection_id, aborted)); - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); - conn->dns_entry = NULL; - } + if(conn->dns_entry) + Curl_resolv_unlink(data, &conn->dns_entry); /* Cleanup NTLM connection-related data */ Curl_http_auth_cleanup_ntlm(conn); @@ -661,46 +642,31 @@ void Curl_disconnect(struct Curl_easy *data, Curl_http_auth_cleanup_negotiate(conn); if(conn->connect_only) - /* treat the connection as dead in CONNECT_ONLY situations */ - dead_connection = TRUE; + /* treat the connection as aborted in CONNECT_ONLY situations */ + aborted = TRUE; - /* temporarily attach the connection to this transfer handle for the - disconnect and shutdown */ - Curl_attach_connection(data, conn); - - if(conn->handler && conn->handler->disconnect) - /* This is set if protocol-specific cleanups should be made */ - conn->handler->disconnect(data, conn, dead_connection); - - conn_shutdown(data); - - /* detach it again */ - Curl_detach_connection(data); - - conn_free(data, conn); + return aborted; } /* - * IsMultiplexingPossible() + * Curl_xfer_may_multiplex() * - * Return a bitmask with the available multiplexing options for the given - * requested connection. + * Return a TRUE, iff the transfer can be done over an (appropriate) + * multiplexed connection. */ -static int IsMultiplexingPossible(const struct Curl_easy *handle, - const struct connectdata *conn) +static bool Curl_xfer_may_multiplex(const struct Curl_easy *data, + const struct connectdata *conn) { - int avail = 0; - /* If an HTTP protocol and multiplexing is enabled */ if((conn->handler->protocol & PROTO_FAMILY_HTTP) && (!conn->bits.protoconnstart || !conn->bits.close)) { - if(Curl_multiplex_wanted(handle->multi) && - (handle->state.httpwant >= CURL_HTTP_VERSION_2)) - /* allows HTTP/2 */ - avail |= CURLPIPE_MULTIPLEX; + if(Curl_multiplex_wanted(data->multi) && + (data->state.httpwant >= CURL_HTTP_VERSION_2)) + /* allows HTTP/2 or newer */ + return TRUE; } - return avail; + return FALSE; } #ifndef CURL_DISABLE_PROXY @@ -735,7 +701,7 @@ socks_proxy_info_matches(const struct proxy_info *data, return TRUE; } #else -/* disabled, won't get called */ +/* disabled, will not get called */ #define proxy_info_matches(x,y) FALSE #define socks_proxy_info_matches(x,y) FALSE #endif @@ -754,7 +720,7 @@ static bool conn_maxage(struct Curl_easy *data, idletime /= 1000; /* integer seconds is fine */ if(idletime > data->set.maxage_conn) { - infof(data, "Too old connection (%" CURL_FORMAT_TIMEDIFF_T + infof(data, "Too old connection (%" FMT_TIMEDIFF_T " seconds idle), disconnect it", idletime); return TRUE; } @@ -764,7 +730,7 @@ static bool conn_maxage(struct Curl_easy *data, if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) { infof(data, - "Too old connection (%" CURL_FORMAT_TIMEDIFF_T + "Too old connection (%" FMT_TIMEDIFF_T " seconds since creation), disconnect it", lifetime); return TRUE; } @@ -774,23 +740,24 @@ static bool conn_maxage(struct Curl_easy *data, } /* - * This function checks if the given connection is dead and prunes it from - * the connection cache if so. - * - * When this is called as a Curl_conncache_foreach() callback, the connection - * cache lock is held! - * - * Returns TRUE if the connection was dead and pruned. + * Return TRUE iff the given connection is considered dead. */ -static bool prune_if_dead(struct connectdata *conn, - struct Curl_easy *data) +bool Curl_conn_seems_dead(struct connectdata *conn, + struct Curl_easy *data, + struct curltime *pnow) { + DEBUGASSERT(!data->conn); if(!CONN_INUSE(conn)) { - /* The check for a dead socket makes sense only if the connection isn't in + /* The check for a dead socket makes sense only if the connection is not in use */ bool dead; - struct curltime now = Curl_now(); - if(conn_maxage(data, conn, now)) { + struct curltime now; + if(!pnow) { + now = Curl_now(); + pnow = &now; + } + + if(conn_maxage(data, conn, *pnow)) { /* avoid check if already too old */ dead = TRUE; } @@ -823,70 +790,47 @@ static bool prune_if_dead(struct connectdata *conn, * any time (HTTP/2 PING for example), the protocol handler needs * to install its own `connection_check` callback. */ + DEBUGF(infof(data, "connection has input pending, not reusable")); dead = TRUE; } Curl_detach_connection(data); } if(dead) { - /* remove connection from cache */ - infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead", + /* remove connection from cpool */ + infof(data, "Connection %" FMT_OFF_T " seems to be dead", conn->connection_id); - Curl_conncache_remove_conn(data, conn, FALSE); return TRUE; } } return FALSE; } -/* - * Wrapper to use prune_if_dead() function in Curl_conncache_foreach() - * - */ -static int call_prune_if_dead(struct Curl_easy *data, - struct connectdata *conn, void *param) +CURLcode Curl_conn_upkeep(struct Curl_easy *data, + struct connectdata *conn, + struct curltime *now) { - struct connectdata **pruned = (struct connectdata **)param; - if(prune_if_dead(conn, data)) { - /* stop the iteration here, pass back the connection that was pruned */ - *pruned = conn; - return 1; - } - return 0; /* continue iteration */ -} - -/* - * This function scans the connection cache for half-open/dead connections, - * closes and removes them. The cleanup is done at most once per second. - * - * When called, this transfer has no connection attached. - */ -static void prune_dead_connections(struct Curl_easy *data) -{ - struct curltime now = Curl_now(); - timediff_t elapsed; - - DEBUGASSERT(!data->conn); /* no connection */ - CONNCACHE_LOCK(data); - elapsed = - Curl_timediff(now, data->state.conn_cache->last_cleanup); - CONNCACHE_UNLOCK(data); - - if(elapsed >= 1000L) { - struct connectdata *pruned = NULL; - while(Curl_conncache_foreach(data, data->state.conn_cache, &pruned, - call_prune_if_dead)) { - /* unlocked */ - - /* connection previously removed from cache in prune_if_dead() */ + CURLcode result = CURLE_OK; + if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) + return result; - /* disconnect it */ - Curl_disconnect(data, pruned, TRUE); - } - CONNCACHE_LOCK(data); - data->state.conn_cache->last_cleanup = now; - CONNCACHE_UNLOCK(data); + /* briefly attach for action */ + Curl_attach_connection(data, conn); + if(conn->handler->connection_check) { + /* Do a protocol-specific keepalive check on the connection. */ + unsigned int rc; + rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); + if(rc & CONNRESULT_DEAD) + result = CURLE_RECV_ERROR; } + else { + /* Do the generic action on the FIRSTSOCKET filter chain */ + result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); + } + Curl_detach_connection(data); + + conn->keepalive = *now; + return result; } #ifdef USE_SSH @@ -900,425 +844,420 @@ static bool ssh_config_matches(struct connectdata *one, #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 - * exactly the same and thus should be used instead. - * - * If there is a match, this function returns TRUE - and has marked the - * connection as 'in-use'. It must later be called with ConnectionDone() to - * return back to 'idle' (unused) state. - * - * The force_reuse flag is set if the connection must be used. - */ -static bool -ConnectionExists(struct Curl_easy *data, - struct connectdata *needle, - struct connectdata **usethis, - bool *force_reuse, - bool *waitpipe) +struct url_conn_match { + struct connectdata *found; + struct Curl_easy *data; + struct connectdata *needle; + BIT(may_multiplex); + BIT(want_ntlm_http); + BIT(want_proxy_ntlm_http); + + BIT(wait_pipe); + BIT(force_reuse); + BIT(seen_pending_conn); + BIT(seen_single_use_conn); + BIT(seen_multiplex_conn); +}; + +static bool url_match_conn(struct connectdata *conn, void *userdata) { - struct connectdata *chosen = NULL; - bool foundPendingCandidate = FALSE; - bool canmultiplex = FALSE; - struct connectbundle *bundle; - struct Curl_llist_element *curr; + struct url_conn_match *match = userdata; + struct Curl_easy *data = match->data; + struct connectdata *needle = match->needle; -#ifdef USE_NTLM - bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); -#ifndef CURL_DISABLE_PROXY - bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && - ((data->state.authproxy.want & - CURLAUTH_NTLM) && - (needle->handler->protocol & PROTO_FAMILY_HTTP))); -#else - bool wantProxyNTLMhttp = FALSE; -#endif -#endif - /* plain HTTP with upgrade */ - bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && - (needle->handler->protocol & CURLPROTO_HTTP); + /* Check if `conn` can be used for transfer `data` */ - *usethis = NULL; - *force_reuse = FALSE; - *waitpipe = FALSE; + if(conn->connect_only || conn->bits.close) + /* connect-only or to-be-closed connections will not be reused */ + return FALSE; - /* Look up the bundle with all the connections to this particular host. - Locks the connection cache, beware of early returns! */ - bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); - if(!bundle) { - CONNCACHE_UNLOCK(data); + if(data->set.ipver != CURL_IPRESOLVE_WHATEVER + && data->set.ipver != conn->ip_version) { + /* skip because the connection is not via the requested IP version */ return FALSE; } - infof(data, "Found bundle for host: %p [%s]", - (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); - - /* We can only multiplex iff the transfer allows it AND we know - * that the server we want to talk to supports it as well. */ - canmultiplex = FALSE; - if(IsMultiplexingPossible(data, needle)) { - if(bundle->multiuse == BUNDLE_UNKNOWN) { - if(data->set.pipewait) { - infof(data, "Server doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - infof(data, "Server doesn't support multiplex (yet)"); - } - else if(bundle->multiuse == BUNDLE_MULTIPLEX) { - if(Curl_multiplex_wanted(data->multi)) - canmultiplex = TRUE; - else - infof(data, "Could multiplex, but not asked to"); - } - else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { - infof(data, "Can not multiplex, even if we wanted to"); - } - } - - curr = bundle->conn_list.head; - while(curr) { - struct connectdata *check = curr->ptr; - /* Get next node now. We might remove a dead `check` connection which - * would invalidate `curr` as well. */ - curr = curr->next; - /* Note that if we use an HTTP proxy in normal mode (no tunneling), we - * check connections to that proxy and not to the actual remote server. - */ - if(check->connect_only || check->bits.close) - /* connect-only or to-be-closed connections will not be reused */ - continue; + if(needle->localdev || needle->localport) { + /* If we are bound to a specific local end (IP+port), we must not + reuse a random other one, although if we did not ask for a + particular one we can reuse one that was bound. + + This comparison is a bit rough and too strict. Since the input + parameters can be specified in numerous ways and still end up the + same it would take a lot of processing to make it really accurate. + Instead, this matching will assume that reuses of bound connections + will most likely also reuse the exact same binding parameters and + missing out a few edge cases should not hurt anyone very much. + */ + if((conn->localport != needle->localport) || + (conn->localportrange != needle->localportrange) || + (needle->localdev && + (!conn->localdev || strcmp(conn->localdev, needle->localdev)))) + return FALSE; + } + + if(needle->bits.conn_to_host != conn->bits.conn_to_host) + /* do not mix connections that use the "connect to host" feature and + * connections that do not use this feature */ + return FALSE; - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { - /* skip because the connection is not via the requested IP version */ - continue; - } + if(needle->bits.conn_to_port != conn->bits.conn_to_port) + /* do not mix connections that use the "connect to port" feature and + * connections that do not use this feature */ + return FALSE; - if(!canmultiplex) { - if(Curl_resolver_asynch() && - /* remote_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't reuse this connection */ - !check->primary.remote_ip[0]) - continue; - } + if(!Curl_conn_is_connected(conn, FIRSTSOCKET) || + conn->bits.asks_multiplex) { + /* Not yet connected, or not yet decided if it multiplexes. The later + * happens for HTTP/2 Upgrade: requests that need a response. */ + if(match->may_multiplex) { + match->seen_pending_conn = TRUE; + /* Do not pick a connection that has not connected yet */ + infof(data, "Connection #%" FMT_OFF_T + " is not open enough, cannot reuse", conn->connection_id); + } + /* Do not pick a connection that has not connected yet */ + return FALSE; + } + /* `conn` is connected. If it has transfers, can we add ours to it? */ - if(CONN_INUSE(check)) { - if(!canmultiplex) { - /* transfer can't be multiplexed and check is in use */ - continue; - } - else { - /* Could multiplex, but not when check belongs to another multi */ - struct Curl_llist_element *e = check->easyq.head; - struct Curl_easy *entry = e->ptr; - if(entry->multi != data->multi) - continue; - } + if(CONN_INUSE(conn)) { + if(!conn->bits.multiplex) { + /* conn busy and conn cannot take more transfers */ + match->seen_single_use_conn = TRUE; + return FALSE; } - - if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T - " isn't open enough, can't reuse", check->connection_id); - continue; + match->seen_multiplex_conn = TRUE; + if(!match->may_multiplex) + /* conn busy and transfer cannot be multiplexed */ + return FALSE; + else { + /* transfer and conn multiplex. Are they on the same multi? */ + struct Curl_llist_node *e = Curl_llist_head(&conn->easyq); + struct Curl_easy *entry = Curl_node_elem(e); + if(entry->multi != data->multi) + return FALSE; } + } + /* `conn` is connected and we could add the transfer to it, if + * all the other criteria do match. */ - /* `check` is connected. if it is in use and does not support multiplex, - * we cannot use it. */ - if(!check->bits.multiplex && CONN_INUSE(check)) - continue; - + /* Does `conn` use the correct protocol? */ #ifdef USE_UNIX_SOCKETS - if(needle->unix_domain_socket) { - if(!check->unix_domain_socket) - continue; - if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) - continue; - if(needle->bits.abstract_unix_socket != - check->bits.abstract_unix_socket) - continue; - } - else if(check->unix_domain_socket) - continue; -#endif - - if((needle->handler->flags&PROTOPT_SSL) != - (check->handler->flags&PROTOPT_SSL)) - /* don't do mixed SSL and non-SSL connections */ - if(get_protocol_family(check->handler) != - needle->handler->protocol || !check->bits.tls_upgraded) - /* except protocols that have been upgraded via TLS */ - continue; - - if(needle->bits.conn_to_host != check->bits.conn_to_host) - /* don't mix connections that use the "connect to host" feature and - * connections that don't use this feature */ - continue; - - if(needle->bits.conn_to_port != check->bits.conn_to_port) - /* don't mix connections that use the "connect to port" feature and - * connections that don't use this feature */ - continue; + if(needle->unix_domain_socket) { + if(!conn->unix_domain_socket) + return FALSE; + if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket)) + return FALSE; + if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket) + return FALSE; + } + else if(conn->unix_domain_socket) + return FALSE; +#endif + + if((needle->handler->flags&PROTOPT_SSL) != + (conn->handler->flags&PROTOPT_SSL)) + /* do not do mixed SSL and non-SSL connections */ + if(get_protocol_family(conn->handler) != + needle->handler->protocol || !conn->bits.tls_upgraded) + /* except protocols that have been upgraded via TLS */ + return FALSE; #ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy != check->bits.httpproxy || - needle->bits.socksproxy != check->bits.socksproxy) - continue; - - if(needle->bits.socksproxy && - !socks_proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) - continue; - - if(needle->bits.httpproxy) { - if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) - continue; - - if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) - continue; - - if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { - /* https proxies come in different types, http/1.1, h2, ... */ - if(needle->http_proxy.proxytype != check->http_proxy.proxytype) - continue; - /* match SSL config to proxy */ - if(!Curl_ssl_conn_config_match(data, check, TRUE)) { - DEBUGF(infof(data, - "Connection #%" CURL_FORMAT_CURL_OFF_T - " has different SSL proxy parameters, can't reuse", - check->connection_id)); - continue; - } - /* the SSL config to the server, which may apply here is checked - * further below */ + if(needle->bits.httpproxy != conn->bits.httpproxy || + needle->bits.socksproxy != conn->bits.socksproxy) + return FALSE; + + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &conn->socks_proxy)) + return FALSE; + + if(needle->bits.httpproxy) { + if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy) + return FALSE; + + if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy)) + return FALSE; + + if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + if(needle->http_proxy.proxytype != conn->http_proxy.proxytype) + return FALSE; + /* match SSL config to proxy */ + if(!Curl_ssl_conn_config_match(data, conn, TRUE)) { + DEBUGF(infof(data, + "Connection #%" FMT_OFF_T + " has different SSL proxy parameters, cannot reuse", + conn->connection_id)); + return FALSE; } + /* the SSL config to the server, which may apply here is checked + * further below */ } + } #endif - if(h2upgrade && !check->httpversion && canmultiplex) { - if(data->set.pipewait) { - infof(data, "Server upgrade doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no reuse */ - } - infof(data, "Server upgrade cannot be used"); - continue; /* can't be used atm */ - } - - if(needle->localdev || needle->localport) { - /* If we are bound to a specific local end (IP+port), we must not - reuse a random other one, although if we didn't ask for a - particular one we can reuse one that was bound. - - This comparison is a bit rough and too strict. Since the input - parameters can be specified in numerous ways and still end up the - same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that reuses of bound connections - will most likely also reuse the exact same binding parameters and - missing out a few edge cases shouldn't hurt anyone very much. - */ - if((check->localport != needle->localport) || - (check->localportrange != needle->localportrange) || - (needle->localdev && - (!check->localdev || strcmp(check->localdev, needle->localdev)))) - continue; - } - - 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(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; - } + if(match->may_multiplex && + (data->state.httpwant == CURL_HTTP_VERSION_2_0) && + (needle->handler->protocol & CURLPROTO_HTTP) && + !conn->httpversion) { + if(data->set.pipewait) { + infof(data, "Server upgrade does not support multiplex yet, wait"); + match->found = NULL; + match->wait_pipe = TRUE; + return TRUE; /* stop searching, we want to wait */ + } + infof(data, "Server upgrade cannot be used"); + return FALSE; + } + + if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + /* This protocol requires credentials per connection, + so verify that we are using the same name and password as well */ + if(Curl_timestrcmp(needle->user, conn->user) || + Curl_timestrcmp(needle->passwd, conn->passwd) || + Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) || + Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) { + /* one of them was different */ + return FALSE; } + } - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ - if(needle->gssapi_delegation != check->gssapi_delegation) - continue; + /* GSS delegation differences do not actually affect every connection + and auth method, but this check takes precaution before efficiency */ + if(needle->gssapi_delegation != conn->gssapi_delegation) + return FALSE; - /* If looking for HTTP and the HTTP version we want is less - * than the HTTP version of the check connection, continue looking */ - if((needle->handler->protocol & PROTO_FAMILY_HTTP) && - (((check->httpversion >= 20) && - (data->state.httpwant < CURL_HTTP_VERSION_2_0)) - || ((check->httpversion >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) - continue; + /* If looking for HTTP and the HTTP version we want is less + * than the HTTP version of conn, continue looking */ + if((needle->handler->protocol & PROTO_FAMILY_HTTP) && + (((conn->httpversion >= 20) && + (data->state.httpwant < CURL_HTTP_VERSION_2_0)) + || ((conn->httpversion >= 30) && + (data->state.httpwant < CURL_HTTP_VERSION_3)))) + return FALSE; #ifdef USE_SSH - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { - if(!ssh_config_matches(needle, check)) - continue; - } + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { + if(!ssh_config_matches(needle, conn)) + return FALSE; + } #endif #ifndef CURL_DISABLE_FTP - else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ - if(Curl_timestrcmp(needle->proto.ftpc.account, - check->proto.ftpc.account) || - Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, - check->proto.ftpc.alternative_to_user) || - (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) || - (needle->proto.ftpc.ccc != check->proto.ftpc.ccc)) - continue; - } + else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) { + /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + if(Curl_timestrcmp(needle->proto.ftpc.account, + conn->proto.ftpc.account) || + Curl_timestrcmp(needle->proto.ftpc.alternative_to_user, + conn->proto.ftpc.alternative_to_user) || + (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) || + (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc)) + return FALSE; + } #endif - /* Additional match requirements if talking TLS OR - * not talking to a HTTP proxy OR using a tunnel through a proxy */ - if((needle->handler->flags&PROTOPT_SSL) + /* Additional match requirements if talking TLS OR + * not talking to an HTTP proxy OR using a tunnel through a proxy */ + if((needle->handler->flags&PROTOPT_SSL) #ifndef CURL_DISABLE_PROXY - || !needle->bits.httpproxy || needle->bits.tunnel_proxy -#endif - ) { - /* Talking the same protocol scheme or a TLS upgraded protocol in the - * same protocol family? */ - if(!strcasecompare(needle->handler->scheme, check->handler->scheme) && - (get_protocol_family(check->handler) != - needle->handler->protocol || !check->bits.tls_upgraded)) - continue; - - /* If needle has "conn_to_*" set, check must match this */ - if((needle->bits.conn_to_host && !strcasecompare( - needle->conn_to_host.name, check->conn_to_host.name)) || - (needle->bits.conn_to_port && - needle->conn_to_port != check->conn_to_port)) - continue; - - /* hostname and port must match */ - if(!strcasecompare(needle->host.name, check->host.name) || - needle->remote_port != check->remote_port) - continue; - - /* If talking TLS, check needs to use the same SSL options. */ - if((needle->handler->flags & PROTOPT_SSL) && - !Curl_ssl_conn_config_match(data, check, FALSE)) { - DEBUGF(infof(data, - "Connection #%" CURL_FORMAT_CURL_OFF_T - " has different SSL parameters, can't reuse", - check->connection_id)); - continue; - } + || !needle->bits.httpproxy || needle->bits.tunnel_proxy +#endif + ) { + /* Talking the same protocol scheme or a TLS upgraded protocol in the + * same protocol family? */ + if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) && + (get_protocol_family(conn->handler) != + needle->handler->protocol || !conn->bits.tls_upgraded)) + return FALSE; + + /* If needle has "conn_to_*" set, conn must match this */ + if((needle->bits.conn_to_host && !strcasecompare( + needle->conn_to_host.name, conn->conn_to_host.name)) || + (needle->bits.conn_to_port && + needle->conn_to_port != conn->conn_to_port)) + return FALSE; + + /* hostname and port must match */ + if(!strcasecompare(needle->host.name, conn->host.name) || + needle->remote_port != conn->remote_port) + return FALSE; + + /* If talking TLS, conn needs to use the same SSL options. */ + if((needle->handler->flags & PROTOPT_SSL) && + !Curl_ssl_conn_config_match(data, conn, FALSE)) { + DEBUGF(infof(data, + "Connection #%" FMT_OFF_T + " has different SSL parameters, cannot reuse", + conn->connection_id)); + return FALSE; } + } #if defined(USE_NTLM) - /* If we are looking for an HTTP+NTLM connection, check if this is - already authenticating with the right credentials. If not, keep - looking so that we can reuse NTLM connections if - possible. (Especially we must not reuse the same connection if - partway through a handshake!) */ - if(wantNTLMhttp) { - 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 */ - if(check->http_ntlm_state == NTLMSTATE_NONE) - chosen = check; - continue; - } - } - else if(check->http_ntlm_state != NTLMSTATE_NONE) { - /* Connection is using NTLM auth but we don't want NTLM */ - continue; - } + /* If we are looking for an HTTP+NTLM connection, check if this is + already authenticating with the right credentials. If not, keep + looking so that we can reuse NTLM connections if + possible. (Especially we must not reuse the same connection if + partway through a handshake!) */ + if(match->want_ntlm_http) { + if(Curl_timestrcmp(needle->user, conn->user) || + Curl_timestrcmp(needle->passwd, conn->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(conn->http_ntlm_state == NTLMSTATE_NONE) + match->found = conn; + return FALSE; + } + } + else if(conn->http_ntlm_state != NTLMSTATE_NONE) { + /* Connection is using NTLM auth but we do not want NTLM */ + return FALSE; + } #ifndef CURL_DISABLE_PROXY - /* Same for Proxy NTLM authentication */ - if(wantProxyNTLMhttp) { - /* Both check->http_proxy.user and check->http_proxy.passwd can be - * NULL */ - if(!check->http_proxy.user || !check->http_proxy.passwd) - continue; - - 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) { - /* Proxy connection is using NTLM auth but we don't want NTLM */ - continue; - } -#endif - if(wantNTLMhttp || wantProxyNTLMhttp) { - /* Credentials are already checked, we may use this connection. - * With NTLM being weird as it is, we MUST use a - * connection where it has already been fully negotiated. - * If it has not, we keep on looking for a better one. */ - chosen = check; - - if((wantNTLMhttp && - (check->http_ntlm_state != NTLMSTATE_NONE)) || - (wantProxyNTLMhttp && - (check->proxy_ntlm_state != NTLMSTATE_NONE))) { - /* We must use this connection, no other */ - *force_reuse = TRUE; - break; - } - /* Continue look up for a better connection */ - continue; + /* Same for Proxy NTLM authentication */ + if(match->want_proxy_ntlm_http) { + /* Both conn->http_proxy.user and conn->http_proxy.passwd can be + * NULL */ + if(!conn->http_proxy.user || !conn->http_proxy.passwd) + return FALSE; + + if(Curl_timestrcmp(needle->http_proxy.user, + conn->http_proxy.user) || + Curl_timestrcmp(needle->http_proxy.passwd, + conn->http_proxy.passwd)) + return FALSE; + } + else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { + /* Proxy connection is using NTLM auth but we do not want NTLM */ + return FALSE; + } +#endif + if(match->want_ntlm_http || match->want_proxy_ntlm_http) { + /* Credentials are already checked, we may use this connection. + * With NTLM being weird as it is, we MUST use a + * connection where it has already been fully negotiated. + * If it has not, we keep on looking for a better one. */ + match->found = conn; + + if((match->want_ntlm_http && + (conn->http_ntlm_state != NTLMSTATE_NONE)) || + (match->want_proxy_ntlm_http && + (conn->proxy_ntlm_state != NTLMSTATE_NONE))) { + /* We must use this connection, no other */ + match->force_reuse = TRUE; + return TRUE; } + /* Continue look up for a better connection */ + return FALSE; + } #endif - if(CONN_INUSE(check)) { - DEBUGASSERT(canmultiplex); - DEBUGASSERT(check->bits.multiplex); - /* If multiplexed, make sure we don't go over concurrency limit */ - if(CONN_INUSE(check) >= - Curl_multi_max_concurrent_streams(data->multi)) { - infof(data, "client side MAX_CONCURRENT_STREAMS reached" - ", skip (%zu)", CONN_INUSE(check)); - continue; - } - if(CONN_INUSE(check) >= - Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) { - infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", - CONN_INUSE(check)); - continue; - } - /* When not multiplexed, we have a match here! */ - infof(data, "Multiplexed connection found"); + if(CONN_INUSE(conn)) { + DEBUGASSERT(match->may_multiplex); + DEBUGASSERT(conn->bits.multiplex); + /* If multiplexed, make sure we do not go over concurrency limit */ + if(CONN_INUSE(conn) >= + Curl_multi_max_concurrent_streams(data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)", CONN_INUSE(conn)); + return FALSE; } - else if(prune_if_dead(check, data)) { - /* disconnect it */ - Curl_disconnect(data, check, TRUE); - continue; + if(CONN_INUSE(conn) >= + Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", + CONN_INUSE(conn)); + return FALSE; } + /* When not multiplexed, we have a match here! */ + infof(data, "Multiplexed connection found"); + } + else if(Curl_conn_seems_dead(conn, data, NULL)) { + /* removed and disconnect. Do not treat as aborted. */ + Curl_cpool_disconnect(data, conn, FALSE); + return FALSE; + } - /* We have found a connection. Let's stop searching. */ - chosen = check; - break; - } /* loop over connection bundle */ + /* We have found a connection. Let's stop searching. */ + match->found = conn; + return TRUE; +} - if(chosen) { - /* mark it as used before releasing the lock */ - Curl_attach_connection(data, chosen); - CONNCACHE_UNLOCK(data); - *usethis = chosen; - return TRUE; /* yes, we found one to use! */ +static bool url_match_result(bool result, void *userdata) +{ + struct url_conn_match *match = userdata; + (void)result; + if(match->found) { + /* Attach it now while still under lock, so the connection does + * no longer appear idle and can be reaped. */ + Curl_attach_connection(match->data, match->found); + return TRUE; } - CONNCACHE_UNLOCK(data); - - if(foundPendingCandidate && data->set.pipewait) { - infof(data, + else if(match->seen_single_use_conn && !match->seen_multiplex_conn) { + /* We've seen a single-use, existing connection to the destination and + * no multiplexed one. It seems safe to assume that the server does + * not support multiplexing. */ + match->wait_pipe = FALSE; + } + else if(match->seen_pending_conn && match->data->set.pipewait) { + infof(match->data, "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set"); - *waitpipe = TRUE; + match->wait_pipe = TRUE; } + match->force_reuse = FALSE; + return FALSE; +} - return FALSE; /* no matching connecting exists */ +/* + * Given one filled in connection struct (named needle), this function should + * detect if there already is one that has all the significant details + * exactly the same and thus should be used instead. + * + * If there is a match, this function returns TRUE - and has marked the + * connection as 'in-use'. It must later be called with ConnectionDone() to + * return back to 'idle' (unused) state. + * + * The force_reuse flag is set if the connection must be used. + */ +static bool +ConnectionExists(struct Curl_easy *data, + struct connectdata *needle, + struct connectdata **usethis, + bool *force_reuse, + bool *waitpipe) +{ + struct url_conn_match match; + bool result; + + memset(&match, 0, sizeof(match)); + match.data = data; + match.needle = needle; + match.may_multiplex = Curl_xfer_may_multiplex(data, needle); + +#ifdef USE_NTLM + match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) && + (needle->handler->protocol & PROTO_FAMILY_HTTP)); +#ifndef CURL_DISABLE_PROXY + match.want_proxy_ntlm_http = + (needle->bits.proxy_user_passwd && + (data->state.authproxy.want & CURLAUTH_NTLM) && + (needle->handler->protocol & PROTO_FAMILY_HTTP)); +#endif +#endif + + /* Find a connection in the pool that matches what "data + needle" + * requires. If a suitable candidate is found, it is attached to "data". */ + result = Curl_cpool_find(data, needle->destination, needle->destination_len, + url_match_conn, url_match_result, &match); + + /* wait_pipe is TRUE if we encounter a bundle that is undecided. There + * is no matching connection then, yet. */ + *usethis = match.found; + *force_reuse = match.force_reuse; + *waitpipe = match.wait_pipe; + return result; } /* @@ -1335,6 +1274,21 @@ void Curl_verboseconnect(struct Curl_easy *data, infof(data, "Connected to %s (%s) port %u", CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip, conn->primary.remote_port); +#if !defined(CURL_DISABLE_HTTP) + if(conn->handler->protocol & PROTO_FAMILY_HTTP) { + switch(conn->alpn) { + case CURL_HTTP_VERSION_3: + infof(data, "using HTTP/3"); + break; + case CURL_HTTP_VERSION_2: + infof(data, "using HTTP/2"); + break; + default: + infof(data, "using HTTP/1.x"); + break; + } + } +#endif } #endif @@ -1357,7 +1311,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->primary.remote_port = -1; /* unknown at this point */ conn->remote_port = -1; /* unknown at this point */ - /* Default protocol-independent behavior doesn't support persistent + /* Default protocol-independent behavior does not support persistent connections, so we set this to force-close. Protocols that support this need to set this to FALSE in their "curl_do" functions. */ connclose(conn, "Default to force-close"); @@ -1643,7 +1597,7 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, unsigned int c = 978; while(l) { c <<= 5; - c += Curl_raw_tolower(*s); + c += (unsigned int)Curl_raw_tolower(*s); s++; l--; } @@ -1678,7 +1632,7 @@ static CURLcode findprotocol(struct Curl_easy *data, } } - /* The protocol was not found in the table, but we don't have to assign it + /* The protocol was not found in the table, but we do not have to assign it to anything since it is already assigned to a dummy-struct in the create_conn() function when the connectdata struct is allocated. */ failf(data, "Protocol \"%s\" %s%s", protostr, @@ -1796,12 +1750,12 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, if(!use_set_uh) { char *newurl; - uc = curl_url_set(uh, CURLUPART_URL, data->state.url, - CURLU_GUESS_SCHEME | - CURLU_NON_SUPPORT_SCHEME | - (data->set.disallow_username_in_url ? - CURLU_DISALLOW_USER : 0) | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); + uc = curl_url_set(uh, CURLUPART_URL, data->state.url, (unsigned int) + (CURLU_GUESS_SCHEME | + CURLU_NON_SUPPORT_SCHEME | + (data->set.disallow_username_in_url ? + CURLU_DISALLOW_USER : 0) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); if(uc) { failf(data, "URL rejected: %s", curl_url_strerror(uc)); return Curl_uc_to_curlcode(uc); @@ -1827,7 +1781,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, 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); + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); return CURLE_URL_MALFORMAT; } hostname = data->state.up.hostname; @@ -1845,7 +1799,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, zonefrom_url(uh, data, conn); } - /* make sure the connect struct gets its own copy of the host name */ + /* make sure the connect struct gets its own copy of the hostname */ conn->host.rawalloc = strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; @@ -1892,7 +1846,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; /* - * User name and password set with their own options override the + * username and password set with their own options override the * credentials possibly set in the URL. */ if(!data->set.str[STRING_PASSWORD]) { @@ -1914,7 +1868,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } if(!data->set.str[STRING_USERNAME]) { - /* we don't use the URL API's URL decoder option here since it rejects + /* we do not 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 */ uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); @@ -1979,7 +1933,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, /* - * If we're doing a resumed transfer, we need to setup our stuff + * If we are doing a resumed transfer, we need to setup our stuff * properly. */ static CURLcode setup_range(struct Curl_easy *data) @@ -1991,7 +1945,7 @@ static CURLcode setup_range(struct Curl_easy *data) free(s->range); if(s->resume_from) - s->range = aprintf("%" CURL_FORMAT_CURL_OFF_T "-", s->resume_from); + s->range = aprintf("%" FMT_OFF_T "-", s->resume_from); else s->range = strdup(data->set.str[STRING_SET_RANGE]); @@ -2023,6 +1977,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, struct connectdata *conn) { const struct Curl_handler *p; + const char *hostname; + int port; CURLcode result; /* Perform setup complement if some. */ @@ -2042,6 +1998,34 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, was very likely already set to the proxy port */ conn->primary.remote_port = p->defport; + /* Now create the destination name */ +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + hostname = conn->http_proxy.host.name; + port = conn->primary.remote_port; + } + else +#endif + { + port = conn->remote_port; + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + } + +#ifdef USE_IPV6 + conn->destination = aprintf("%u/%d/%s", conn->scope_id, port, hostname); +#else + conn->destination = aprintf("%d/%s", port, hostname); +#endif + if(!conn->destination) + return CURLE_OUT_OF_MEMORY; + + conn->destination_len = strlen(conn->destination) + 1; + Curl_strntolower(conn->destination, conn->destination, + conn->destination_len - 1); + return CURLE_OK; } @@ -2074,7 +2058,7 @@ static char *detect_proxy(struct Curl_easy *data, * the first to check for.) * * For compatibility, the all-uppercase versions of these variables are - * checked if the lowercase versions don't exist. + * checked if the lowercase versions do not exist. */ char proxy_env[20]; char *envp = proxy_env; @@ -2088,7 +2072,7 @@ static char *detect_proxy(struct Curl_easy *data, proxy = curl_getenv(proxy_env); /* - * We don't try the uppercase version of HTTP_PROXY because of + * We do not try the uppercase version of HTTP_PROXY because of * security reasons: * * When curl is used in a webserver application @@ -2137,7 +2121,7 @@ static char *detect_proxy(struct Curl_easy *data, /* * If this is supposed to use a proxy, we need to figure out the proxy - * host name, so that we can reuse an existing connection + * hostname, so that we can reuse an existing connection * that may exist registered to the same proxy host. */ static CURLcode parse_proxy(struct Curl_easy *data, @@ -2284,7 +2268,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, conn->primary.remote_port = port; } - /* now, clone the proxy host name */ + /* now, clone the proxy hostname */ uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); if(uc) { result = CURLE_OUT_OF_MEMORY; @@ -2365,7 +2349,7 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, return result; } -/* create_conn helper to parse and init proxy values. to be called after unix +/* create_conn helper to parse and init proxy values. to be called after Unix socket init but before any proxy vars are evaluated. */ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, struct connectdata *conn) @@ -2374,7 +2358,6 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, char *socksproxy = NULL; char *no_proxy = NULL; CURLcode result = CURLE_OK; - bool spacesep = FALSE; /************************************************************* * Extract the user and password from the authentication string @@ -2421,8 +2404,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy, - &spacesep)) { + data->set.str[STRING_NOPROXY] : no_proxy)) { Curl_safefree(proxy); Curl_safefree(socksproxy); } @@ -2431,13 +2413,10 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ - if(spacesep) - infof(data, "space-separated NOPROXY patterns are deprecated"); - Curl_safefree(no_proxy); #ifdef USE_UNIX_SOCKETS - /* For the time being do not mix proxy and unix domain sockets. See #1274 */ + /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ if(proxy && conn->unix_domain_socket) { free(proxy); proxy = NULL; @@ -2445,14 +2424,14 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, #endif if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { - free(proxy); /* Don't bother with an empty proxy string or if the - protocol doesn't work with network */ + free(proxy); /* Do not bother with an empty proxy string or if the + protocol does not work with network */ proxy = NULL; } if(socksproxy && (!*socksproxy || (conn->handler->flags & PROTOPT_NONETWORK))) { - free(socksproxy); /* Don't bother with an empty socks proxy string or if - the protocol doesn't work with network */ + free(socksproxy); /* Do not bother with an empty socks proxy string or if + the protocol does not work with network */ socksproxy = NULL; } @@ -2524,7 +2503,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; if(!conn->bits.proxy) { - /* we aren't using the proxy after all... */ + /* we are not using the proxy after all... */ conn->bits.proxy = FALSE; conn->bits.httpproxy = FALSE; conn->bits.socksproxy = FALSE; @@ -2546,7 +2525,7 @@ out: /* * Curl_parse_login_details() * - * This is used to parse a login string for user name, password and options in + * This is used to parse a login string for username, password and options in * the following formats: * * user @@ -2693,7 +2672,7 @@ static CURLcode override_login(struct Curl_easy *data, bool url_provided = FALSE; if(data->state.aptr.user) { - /* there was a user name in the URL. Use the URL decoded version */ + /* there was a username in the URL. Use the URL decoded version */ userp = &data->state.aptr.user; url_provided = TRUE; } @@ -2774,7 +2753,7 @@ static CURLcode override_login(struct Curl_easy *data, } /* - * Set the login details so they're available in the connection + * Set the login details so they are available in the connection */ static CURLcode set_login(struct Curl_easy *data, struct connectdata *conn) @@ -2865,8 +2844,8 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, else infof(data, "Invalid IPv6 address format"); portptr = ptr; - /* Note that if this didn't end with a bracket, we still advanced the - * hostptr first, but I can't see anything wrong with that as no host + /* Note that if this did not end with a bracket, we still advanced the + * hostptr first, but I cannot see anything wrong with that as no host * name nor a numeric can legally start with a bracket. */ #else @@ -2880,7 +2859,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, host_portno = strchr(portptr, ':'); if(host_portno) { char *endp = NULL; - *host_portno = '\0'; /* cut off number from host name */ + *host_portno = '\0'; /* cut off number from hostname */ host_portno++; if(*host_portno) { long portparse = strtol(host_portno, &endp, 10); @@ -2895,7 +2874,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, } } - /* now, clone the cleaned host name */ + /* now, clone the cleaned hostname */ DEBUGASSERT(hostptr); *hostname_result = strdup(hostptr); if(!*hostname_result) { @@ -3028,7 +3007,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC if(data->asi && !host && (port == -1) && ((conn->handler->protocol == CURLPROTO_HTTPS) || -#ifdef CURLDEBUG +#ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") #else @@ -3091,7 +3070,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, conn->transport = TRNSPRT_QUIC; conn->httpversion = 30; break; - default: /* shouldn't be possible */ + default: /* should not be possible */ break; } } @@ -3130,143 +3109,85 @@ static CURLcode resolve_unix(struct Curl_easy *data, return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; } - hostaddr->inuse++; + hostaddr->refcount = 1; /* connection is the only one holding this */ 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) +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode resolve_server(struct Curl_easy *data, + struct connectdata *conn, + bool *async) { - struct Curl_dns_entry *hostaddr = NULL; - struct hostname *host; + struct hostname *ehost; timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + const char *peertype = "host"; int rc; +#ifdef USE_UNIX_SOCKETS + char *unix_path = conn->unix_domain_socket; - DEBUGASSERT(conn->dns_entry == NULL); - - host = conn->bits.socksproxy ? &conn->socks_proxy.host : - &conn->http_proxy.host; - - conn->hostname_resolve = strdup(host->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!unix_path && CONN_IS_PROXIED(conn) && 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 - rc = Curl_resolv_timeout(data, conn->hostname_resolve, - conn->primary.remote_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; + if(unix_path) { + /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */ + conn->transport = TRNSPRT_UNIX; + return resolve_unix(data, conn, unix_path); } - - return CURLE_OK; -} #endif -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; - DEBUGASSERT(conn->dns_entry == NULL); - connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->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->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; +#ifndef CURL_DISABLE_PROXY + if(CONN_IS_PROXIED(conn)) { + ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : + &conn->http_proxy.host; + peertype = "proxy"; + } + else +#endif + { + ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->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->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + } /* Resolve target host right on */ - conn->hostname_resolve = strdup(connhost->name); + conn->hostname_resolve = strdup(ehost->name); if(!conn->hostname_resolve) return CURLE_OUT_OF_MEMORY; rc = Curl_resolv_timeout(data, conn->hostname_resolve, - conn->primary.remote_port, &hostaddr, timeout_ms); - conn->dns_entry = hostaddr; + conn->primary.remote_port, + &conn->dns_entry, timeout_ms); if(rc == CURLRESOLV_PENDING) *async = TRUE; else if(rc == CURLRESOLV_TIMEDOUT) { - failf(data, "Failed to resolve host '%s' with timeout after %" - CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname, + failf(data, "Failed to resolve %s '%s' with timeout after %" + FMT_TIMEDIFF_T " ms", peertype, ehost->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); + else if(!conn->dns_entry) { + failf(data, "Could not resolve %s: %s", peertype, ehost->dispname); return CURLE_COULDNT_RESOLVE_HOST; } return CURLE_OK; } -/* 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 - - if(unix_path) { - conn->transport = TRNSPRT_UNIX; - return resolve_unix(data, conn, unix_path); - } -#endif - -#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 reuse - case. */ - *async = FALSE; - return CURLE_OK; - } - - return resolve_fresh(data, conn, async); -} - /* * Cleanup the connection `temp`, just allocated for `data`, before using the - * previously `existing` one for `data`. All relevant info is copied over + * previously `existing` one for `data`. All relevant info is copied over * and `temp` is freed. */ static void reuse_conn(struct Curl_easy *data, @@ -3276,7 +3197,7 @@ static void reuse_conn(struct Curl_easy *data, /* get the user+password information from the temp struct since it may * be new for this request even when we reuse an existing connection */ if(temp->user) { - /* use the new user name and password though */ + /* use the new username and password though */ Curl_safefree(existing->user); Curl_safefree(existing->passwd); existing->user = temp->user; @@ -3288,7 +3209,7 @@ static void reuse_conn(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd; if(existing->bits.proxy_user_passwd) { - /* use the new proxy user name and proxy password though */ + /* use the new proxy username and proxy password though */ Curl_safefree(existing->http_proxy.user); Curl_safefree(existing->socks_proxy.user); Curl_safefree(existing->http_proxy.passwd); @@ -3304,7 +3225,7 @@ static void reuse_conn(struct Curl_easy *data, } #endif - /* Finding a connection for reuse in the cache matches, among other + /* Finding a connection for reuse in the cpool matches, among other * things on the "remote-relevant" hostname. This is not necessarily * the authority of the URL, e.g. conn->host. For example: * - we use a proxy (not tunneling). we want to send all requests @@ -3335,14 +3256,14 @@ static void reuse_conn(struct Curl_easy *data, temp->hostname_resolve = NULL; /* reuse init */ - existing->bits.reuse = TRUE; /* yes, we're reusing here */ + existing->bits.reuse = TRUE; /* yes, we are reusing here */ - conn_free(data, temp); + Curl_conn_free(data, temp); } /** * create_conn() sets up a new connectdata struct, or reuses an already - * existing one, and resolves host name. + * existing one, and resolves hostname. * * if this function returns CURLE_OK and *async is set to TRUE, the resolve * response will be coming asynchronously. If *async is FALSE, the name is @@ -3366,8 +3287,6 @@ static CURLcode create_conn(struct Curl_easy *data, bool connections_available = TRUE; bool force_reuse = FALSE; bool waitpipe = FALSE; - size_t max_host_connections = Curl_multi_max_host_connections(data->multi); - size_t max_total_connections = Curl_multi_max_total_connections(data->multi); *async = FALSE; *in_connect = NULL; @@ -3427,7 +3346,7 @@ static CURLcode create_conn(struct Curl_easy *data, } #endif - /* After the unix socket init but before the proxy vars are used, parse and + /* After the Unix socket init but before the proxy vars are used, parse and initialize the proxy vars */ #ifndef CURL_DISABLE_PROXY result = create_conn_helper_init_proxy(data, conn); @@ -3524,7 +3443,7 @@ static CURLcode create_conn(struct Curl_easy *data, goto out; /*********************************************************************** - * file: is a special case in that it doesn't need a network connection + * file: is a special case in that it does not need a network connection ***********************************************************************/ #ifndef CURL_DISABLE_FILE if(conn->handler->flags & PROTOPT_NONETWORK) { @@ -3532,13 +3451,15 @@ static CURLcode create_conn(struct Curl_easy *data, /* this is supposed to be the connect function so we better at least check that the file is present here! */ DEBUGASSERT(conn->handler->connect_it); - Curl_persistconninfo(data, conn, NULL); + data->info.conn_scheme = conn->handler->scheme; + /* conn_protocol can only provide "old" protocols */ + data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; result = conn->handler->connect_it(data, &done); - /* Setup a "faked" transfer that'll do nothing */ + /* Setup a "faked" transfer that will do nothing */ if(!result) { Curl_attach_connection(data, conn); - result = Curl_conncache_add_conn(data); + result = Curl_cpool_add_conn(data, conn); if(result) goto out; @@ -3552,7 +3473,7 @@ static CURLcode create_conn(struct Curl_easy *data, (void)conn->handler->done(data, result, FALSE); goto out; } - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); } /* since we skip do_init() */ @@ -3574,7 +3495,8 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; - prune_dead_connections(data); + /* FIXME: do we really want to run this every time we add a transfer? */ + Curl_cpool_prune_dead(data); /************************************************************* * Check the current list of connections to see if we can @@ -3633,50 +3555,31 @@ static CURLcode create_conn(struct Curl_easy *data, "soon", and we wait for that */ connections_available = FALSE; else { - /* this gets a lock on the conncache */ - struct connectbundle *bundle = - Curl_conncache_find_bundle(data, conn, data->state.conn_cache); - - if(max_host_connections > 0 && bundle && - (bundle->num_connections >= max_host_connections)) { - struct connectdata *conn_candidate; - - /* The bundle is full. Extract the oldest connection. */ - conn_candidate = Curl_conncache_extract_bundle(data, bundle); - CONNCACHE_UNLOCK(data); - - if(conn_candidate) - Curl_disconnect(data, conn_candidate, FALSE); - else { - infof(data, "No more connections allowed to host: %zu", - max_host_connections); + switch(Curl_cpool_check_limits(data, conn)) { + case CPOOL_LIMIT_DEST: + infof(data, "No more connections allowed to host"); + connections_available = FALSE; + break; + case CPOOL_LIMIT_TOTAL: +#ifndef CURL_DISABLE_DOH + if(data->set.dohfor_mid >= 0) + infof(data, "Allowing DoH to override max connection limit"); + else +#endif + { + infof(data, "No connections available in cache"); connections_available = FALSE; } - } - else - CONNCACHE_UNLOCK(data); - - } - - if(connections_available && - (max_total_connections > 0) && - (Curl_conncache_size(data) >= max_total_connections)) { - struct connectdata *conn_candidate; - - /* The cache is full. Let's see if we can kill a connection. */ - conn_candidate = Curl_conncache_extract_oldest(data); - if(conn_candidate) - Curl_disconnect(data, conn_candidate, FALSE); - else { - infof(data, "No connections available in cache"); - connections_available = FALSE; + break; + default: + break; } } if(!connections_available) { infof(data, "No connections available."); - conn_free(data, conn); + Curl_conn_free(data, conn); *in_connect = NULL; result = CURLE_NO_CONNECTION_AVAILABLE; @@ -3694,13 +3597,13 @@ static CURLcode create_conn(struct Curl_easy *data, } Curl_attach_connection(data, conn); - result = Curl_conncache_add_conn(data); + result = Curl_cpool_add_conn(data, conn); if(result) goto out; } #if defined(USE_NTLM) - /* If NTLM is requested in a part of this connection, make sure we don't + /* If NTLM is requested in a part of this connection, make sure we do not assume the state is fine as this is a fresh connection and NTLM is connection based. */ if((data->state.authhost.picked & CURLAUTH_NTLM) && @@ -3731,16 +3634,35 @@ static CURLcode create_conn(struct Curl_easy *data, /* Continue connectdata initialization here. */ - /************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ - result = resolve_server(data, conn, async); - if(result) - goto out; + if(conn->bits.reuse) { + /* We are reusing the connection - no need to resolve anything, and + idnconvert_hostname() was called already in create_conn() for the reuse + case. */ + *async = FALSE; + } + else { + /************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ + result = resolve_server(data, conn, async); + if(result) + goto out; + } + + /* persist the scheme and handler the transfer is using */ + data->info.conn_scheme = conn->handler->scheme; + /* conn_protocol can only provide "old" protocols */ + data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; + data->info.used_proxy = +#ifdef CURL_DISABLE_PROXY + 0 +#else + conn->bits.proxy +#endif + ; /* Everything general done, inform filters that they need - * to prepare for a data transfer. - */ + * to prepare for a data transfer. */ result = Curl_conn_ev_data_setup(data); out: @@ -3766,18 +3688,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, return result; } -#ifndef CURL_DISABLE_PROXY - /* set proxy_connect_closed to false unconditionally already here since it - is used strictly to provide extra information to a parent function in the - case of proxy CONNECT failures and we must make sure we don't have it - lingering set from a previous invoke */ - conn->bits.proxy_connect_closed = FALSE; -#endif - -#ifdef CURL_DO_LINEEND_CONV - data->state.crlf_conversions = 0; /* reset CRLF conversion counter */ -#endif /* CURL_DO_LINEEND_CONV */ - /* set start time here for timeout purposes in the connect procedure, it is later set again for the progress meter purpose */ conn->now = Curl_now(); @@ -3812,7 +3722,7 @@ CURLcode Curl_connect(struct Curl_easy *data, /* multiplexed */ *protocol_done = TRUE; else if(!*asyncp) { - /* DNS resolution is done: that's either because this is a reused + /* DNS resolution is done: that is either because this is a reused connection, in which case DNS was unnecessary, or because DNS really did finish already (synch resolver/fast async resolve) */ result = Curl_setup_conn(data, protocol_done); @@ -3823,11 +3733,10 @@ CURLcode Curl_connect(struct Curl_easy *data, return result; } else if(result && conn) { - /* We're not allowed to return failure with memory left allocated in the + /* We are not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_detach_connection(data); - Curl_conncache_remove_conn(data, conn, TRUE); - Curl_disconnect(data, conn, TRUE); + Curl_cpool_disconnect(data, conn, TRUE); } return result; @@ -3849,9 +3758,9 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) CURLcode result; if(conn) { - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to + conn->bits.do_more = FALSE; /* by default there is no curl_do_more() to use */ - /* if the protocol used doesn't support wildcards, switch it off */ + /* if the protocol used does not support wildcards, switch it off */ if(data->state.wildcardmatch && !(conn->handler->flags & PROTOPT_WILDCARD)) data->state.wildcardmatch = FALSE; diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index 198a00ad1..47c1db44f 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h @@ -37,10 +37,11 @@ void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); -void Curl_disconnect(struct Curl_easy *data, - struct connectdata *, bool dead_connection); +bool Curl_on_disconnect(struct Curl_easy *data, + struct connectdata *, bool aborted); CURLcode Curl_setup_conn(struct Curl_easy *data, bool *protocol_done); +void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); @@ -64,6 +65,21 @@ void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn, int sockindex); #endif +/** + * Return TRUE iff the given connection is considered dead. + * @param nowp NULL or pointer to time being checked against. + */ +bool Curl_conn_seems_dead(struct connectdata *conn, + struct Curl_easy *data, + struct curltime *nowp); + +/** + * Perform upkeep operations on the connection. + */ +CURLcode Curl_conn_upkeep(struct Curl_easy *data, + struct connectdata *conn, + struct curltime *now); + #if defined(USE_HTTP2) || defined(USE_HTTP3) void Curl_data_priority_clear_state(struct Curl_easy *data); #else diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index c40281a89..fcffab2e9 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h @@ -30,9 +30,9 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, CURLUcode Curl_url_set_authority(CURLU *u, const char *authority); -#ifdef DEBUGBUILD -CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme); +#ifdef UNITTESTS +UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); #endif #endif /* HEADER_CURL_URLAPI_INT_H */ diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index eb0396687..3d4a3f94b 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -41,13 +41,13 @@ #include "curl_memory.h" #include "memdebug.h" - /* MSDOS/Windows style drive prefix, eg c: in c:foo */ + /* MS-DOS/Windows style drive prefix, eg c: in c:foo */ #define STARTS_WITH_DRIVE_PREFIX(str) \ ((('a' <= str[0] && str[0] <= 'z') || \ ('A' <= str[0] && str[0] <= 'Z')) && \ (str[1] == ':')) - /* MSDOS/Windows style drive prefix, optionally with + /* MS-DOS/Windows style drive prefix, optionally with * a '|' instead of ':', followed by a slash or NUL */ #define STARTS_WITH_URL_DRIVE_PREFIX(str) \ ((('a' <= (str)[0] && (str)[0] <= 'z') || \ @@ -82,6 +82,7 @@ struct Curl_URL { unsigned short portnum; /* the numerical version (if 'port' is set) */ BIT(query_present); /* to support blank */ BIT(fragment_present); /* to support blank */ + BIT(guessed_scheme); /* when a URL without scheme is parsed */ }; #define DEFAULT_SCHEME "https" @@ -101,7 +102,7 @@ static void free_urlhandle(struct Curl_URL *u) } /* - * Find the separator at the end of the host name, or the '?' in cases like + * Find the separator at the end of the hostname, or the '?' in cases like * http://www.example.com?id=2380 */ static const char *find_host_sep(const char *url) @@ -140,7 +141,7 @@ static const char hexdigits[] = "0123456789abcdef"; /* 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 + * URL encoding should be skipped for hostnames, otherwise IDN resolution * will fail. */ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, @@ -205,7 +206,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme) { - int i = 0; + size_t i = 0; DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); (void)buflen; /* only used in debug-builds */ if(buf) @@ -229,7 +230,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, 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. */ + be the hostname "data" with a specified port number. */ /* the length of the scheme is the name part only */ size_t len = i; @@ -267,7 +268,7 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) bool skip_slash = FALSE; *newurl = NULL; - /* protsep points to the start of the host name */ + /* protsep points to the start of the hostname */ protsep = strstr(base, "//"); if(!protsep) protsep = base; @@ -277,13 +278,13 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) if('/' != relurl[0]) { int level = 0; - /* First we need to find out if there's a ?-letter in the URL, + /* First we need to find out if there is a ?-letter in the URL, and cut it and the right-side of that off */ pathsep = strchr(protsep, '?'); if(pathsep) *pathsep = 0; - /* we have a relative path to append to the last slash if there's one + /* we have a relative path to append to the last slash if there is one available, or the new URL is just a query string (starts with a '?') or a fragment (starts with '#') we append the new one at the end of the current URL */ @@ -292,7 +293,7 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) if(pathsep) *pathsep = 0; - /* Check if there's any slash after the host name, and if so, remember + /* Check if there is any slash after the hostname, and if so, remember that position instead */ pathsep = strchr(protsep, '/'); if(pathsep) @@ -347,7 +348,7 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) if(pathsep) { /* When people use badly formatted URLs, such as "http://www.example.com?dir=/home/daniel" we must not use the first - slash, if there's a ?-letter before it! */ + slash, if there is a ?-letter before it! */ char *sep = strchr(protsep, '?'); if(sep && (sep < pathsep)) pathsep = sep; @@ -355,8 +356,8 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) } else { /* There was no slash. Now, since we might be operating on a badly - formatted URL, such as "http://www.example.com?id=2380" which - doesn't use a slash separator as it is supposed to, we need to check + formatted URL, such as "http://www.example.com?id=2380" which does + not use a slash separator as it is supposed to, we need to check for a ?-letter as well! */ pathsep = strchr(protsep, '?'); if(pathsep) @@ -367,7 +368,7 @@ static CURLcode concat_url(char *base, const char *relurl, char **newurl) Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); - /* copy over the root url part */ + /* copy over the root URL part */ result = Curl_dyn_add(&newest, base); if(result) return result; @@ -420,15 +421,15 @@ static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags) /* * parse_hostname_login() * - * Parse the login details (user name, password and options) from the URL and - * strip them out of the host name + * Parse the login details (username, password and options) from the URL and + * strip them out of the hostname * */ static CURLUcode parse_hostname_login(struct Curl_URL *u, const char *login, size_t len, unsigned int flags, - size_t *offset) /* to the host name */ + size_t *offset) /* to the hostname */ { CURLUcode result = CURLUE_OK; CURLcode ccode; @@ -475,7 +476,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, if(userp) { if(flags & CURLU_DISALLOW_USER) { - /* Option DISALLOW_USER is set and url contains username. */ + /* Option DISALLOW_USER is set and URL contains username. */ result = CURLUE_USER_NOT_ALLOWED; goto out; } @@ -493,7 +494,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, u->options = optionsp; } - /* the host name starts at this offset */ + /* the hostname starts at this offset */ *offset = ptr - login; return CURLUE_OK; @@ -538,11 +539,11 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, unsigned long port; size_t keep = portptr - hostname; - /* Browser behavior adaptation. If there's a colon with no digits after, + /* Browser behavior adaptation. If there is a colon with no digits after, just cut off the name there which makes us ignore the colon and just use the default port. Firefox, Chrome and Safari all do that. - Don't do it if the URL has no scheme, to make something that looks like + Do not do it if the URL has no scheme, to make something that looks like a scheme not work! */ Curl_dyn_setlen(host, keep); @@ -591,7 +592,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, char zoneid[16]; int i = 0; char *h = &hostname[len + 1]; - /* pass '25' if present and is a url encoded percent sign */ + /* pass '25' if present and is a URL encoded percent sign */ if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) h += 2; while(*h && (*h != ']') && (i < 15)) @@ -664,7 +665,6 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, */ #define HOST_ERROR -1 /* out of memory */ -#define HOST_BAD -2 /* bad IPv4 address */ #define HOST_NAME 1 #define HOST_IPV4 2 @@ -686,7 +686,7 @@ static int ipv4_normalize(struct dynbuf *host) char *endp = NULL; unsigned long l; if(!ISDIGIT(*c)) - /* most importantly this doesn't allow a leading plus or minus */ + /* most importantly this does not allow a leading plus or minus */ return HOST_NAME; l = strtoul(c, &endp, 0); if(errno) @@ -802,7 +802,7 @@ static CURLUcode parse_authority(struct Curl_URL *u, CURLcode result; /* - * Parse the login details and strip them out of the host name. + * Parse the login details and strip them out of the hostname. */ uc = parse_hostname_login(u, auth, authlen, flags, &offset); if(uc) @@ -835,7 +835,6 @@ static CURLUcode parse_authority(struct Curl_URL *u, case HOST_ERROR: uc = CURLUE_OUT_OF_MEMORY; break; - case HOST_BAD: default: uc = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */ break; @@ -907,7 +906,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) do { bool dotdot = TRUE; if(*input == '.') { - /* A. If the input buffer begins with a prefix of "../" or "./", then + /* A. If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, */ if(!strncmp("./", input, 2)) { @@ -918,7 +917,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) input += 3; clen -= 3; } - /* D. if the input buffer consists only of "." or "..", then remove + /* D. if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, */ else if(!strcmp(".", input) || !strcmp("..", input) || @@ -930,7 +929,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) dotdot = FALSE; } else if(*input == '/') { - /* B. if the input buffer begins with a prefix of "/./" or "/.", where + /* 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)) { @@ -943,7 +942,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) break; } - /* C. if the input buffer begins with a prefix of "/../" or "/..", + /* 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, */ @@ -977,7 +976,7 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) dotdot = FALSE; if(!dotdot) { - /* E. move the first path segment in the input buffer to the end of + /* 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. */ @@ -1070,7 +1069,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) * Appendix E, but believe me, it was meant to be there. --MK) */ if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { - /* the URL includes a host name, it must match "localhost" or + /* the URL includes a hostname, it must match "localhost" or "127.0.0.1" to be valid */ if(checkprefix("localhost/", ptr) || checkprefix("127.0.0.1/", ptr)) { @@ -1080,9 +1079,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) #if defined(_WIN32) size_t len; - /* the host name, NetBIOS computer name, can not contain disallowed + /* the hostname, NetBIOS computer name, can not contain disallowed chars, and the delimiting slash character must be appended to the - host name */ + hostname */ path = strpbrk(ptr, "/\\:*?\"<>|"); if(!path || *path != '/') { result = CURLUE_BAD_FILE_URL; @@ -1118,11 +1117,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) Curl_dyn_reset(&host); #if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) - /* Don't allow Windows drive letters when not in Windows. + /* Do not allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ 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 */ + /* File drive letters are only accepted in MS-DOS/Windows */ result = CURLUE_BAD_FILE_URL; goto fail; } @@ -1162,7 +1161,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) result = CURLUE_BAD_SLASHES; goto fail; } - hostp = p; /* host name starts here */ + hostp = p; /* hostname starts here */ } else { /* no scheme! */ @@ -1188,7 +1187,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) } } - /* find the end of the host name + port number */ + /* find the end of the hostname + port number */ hostlen = strcspn(hostp, "/?#"); path = &hostp[hostlen]; @@ -1202,7 +1201,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if((flags & CURLU_GUESS_SCHEME) && !schemep) { const char *hostname = Curl_dyn_ptr(&host); - /* legacy curl-style guess based on host name */ + /* legacy curl-style guess based on hostname */ if(checkprefix("ftp.", hostname)) schemep = "ftp"; else if(checkprefix("dict.", hostname)) @@ -1223,6 +1222,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) result = CURLUE_OUT_OF_MEMORY; goto fail; } + u->guessed_scheme = TRUE; } } else if(flags & CURLU_NO_AUTHORITY) { @@ -1437,6 +1437,8 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ptr = u->scheme; ifmissing = CURLUE_NO_SCHEME; urldecode = FALSE; /* never for schemes */ + if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) + return CURLUE_NO_SCHEME; break; case CURLUPART_USER: ptr = u->user; @@ -1465,7 +1467,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ifmissing = CURLUE_NO_PORT; urldecode = FALSE; /* never for port */ if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { - /* there's no stored port number, but asked to deliver + /* there is no stored port number, but asked to deliver a default one for the scheme */ const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme); if(h) { @@ -1525,6 +1527,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, return CURLUE_NO_HOST; else { const struct Curl_handler *h = NULL; + char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) scheme = u->scheme; else if(flags & CURLU_DEFAULT_SCHEME) @@ -1534,7 +1537,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, h = Curl_get_scheme_handler(scheme); if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there's no stored port number, but asked to deliver + /* there is no stored port number, but asked to deliver a default one for the scheme */ if(h) { msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); @@ -1595,8 +1598,13 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, } } - url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - scheme, + if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme) + msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); + else + schemebuf[0] = 0; + + url = aprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + schemebuf, u->user ? u->user : "", u->password ? ":": "", u->password ? u->password : "", @@ -1718,6 +1726,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, break; case CURLUPART_SCHEME: storep = &u->scheme; + u->guessed_scheme = FALSE; break; case CURLUPART_USER: storep = &u->user; @@ -1790,6 +1799,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } else return CURLUE_BAD_SCHEME; + u->guessed_scheme = FALSE; break; } case CURLUPART_USER: @@ -1862,7 +1872,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return CURLUE_MALFORMED_INPUT; /* if the new thing is absolute or the old one is not - * (we could not get an absolute url in 'oldurl'), + * (we could not get an absolute URL in 'oldurl'), * then replace the existing with the new. */ if(Curl_is_absolute_url(part, NULL, 0, flags & (CURLU_GUESS_SCHEME| @@ -1978,10 +1988,26 @@ nomem: else if(what == CURLUPART_HOST) { size_t n = Curl_dyn_len(&enc); if(!n && (flags & CURLU_NO_AUTHORITY)) { - /* Skip hostname check, it's allowed to be empty. */ + /* Skip hostname check, it is allowed to be empty. */ } else { - if(!n || hostname_check(u, (char *)newp, n)) { + bool bad = FALSE; + if(!n) + bad = TRUE; /* empty hostname is not okay */ + else if(!urlencode) { + /* if the host name part was not URL encoded here, it was set ready + URL encoded so we need to decode it to check */ + size_t dlen; + char *decoded = NULL; + CURLcode result = + Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); + if(result || hostname_check(u, decoded, dlen)) + bad = TRUE; + free(decoded); + } + else if(hostname_check(u, (char *)newp, n)) + bad = TRUE; + if(bad) { Curl_dyn_free(&enc); return CURLUE_BAD_HOSTNAME; } diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 8b1bd65d6..22dceeed8 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -67,7 +67,7 @@ struct curl_trc_featt; #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 + * 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) @@ -77,6 +77,10 @@ struct curl_trc_featt; #define CURLPROTO_WSS 0 #endif +/* the default protocols accepting a redirect to */ +#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ + CURLPROTO_FTPS) + /* This should be undefined once we need bit 32 or higher */ #define PROTO_TYPE_SMALL @@ -101,6 +105,12 @@ typedef unsigned int curl_prot_t; #define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" +#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) +/* do FTP line-end CRLF => LF conversions on platforms that prefer LF-only. It + also means: keep CRLF line endings on the CRLF platforms */ +#define CURL_PREFER_LF_LINEENDS +#endif + /* Convenience defines for checking protocols or their SSL based version. Each protocol handler should only ever have a single CURLPROTO_ in its protocol field. */ @@ -159,6 +169,7 @@ typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ int sockindex, /* socketindex */ const void *buf, /* data to write */ size_t len, /* max amount to write */ + bool eos, /* last chunk */ CURLcode *err); /* error to return */ /* return the count of bytes read, or -1 on error */ @@ -257,22 +268,6 @@ enum protection_level { }; #endif -/* enum for the nonblocking SSL connection state machine */ -typedef enum { - ssl_connect_1, - ssl_connect_2, - ssl_connect_2_reading, - ssl_connect_2_writing, - ssl_connect_3, - ssl_connect_done -} ssl_connect_state; - -typedef enum { - ssl_connection_none, - ssl_connection_negotiating, - ssl_connection_complete -} ssl_connection_state; - /* SSL backend-specific data; declared differently by each SSL backend */ struct ssl_backend_data; @@ -288,11 +283,11 @@ struct ssl_peer { char *sni; /* SNI version of hostname or NULL if not usable */ ssl_peer_type type; /* type of the peer information */ int port; /* port we are talking to */ - int transport; /* TCP or QUIC */ + int transport; /* one of TRNSPRT_* defines */ }; struct ssl_primary_config { - char *CApath; /* certificate dir (doesn't work on windows) */ + char *CApath; /* certificate dir (does not work on Windows) */ char *CAfile; /* certificate to verify peer against */ char *issuercert; /* optional issuer certificate filename */ char *clientcert; @@ -314,7 +309,7 @@ struct ssl_primary_config { BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ BIT(verifystatus); /* set TRUE if certificate status must be checked */ - BIT(sessionid); /* cache session IDs or not */ + BIT(cache_session); /* cache session or not */ }; struct ssl_config_data { @@ -323,7 +318,7 @@ struct ssl_config_data { curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ void *fsslctxp; /* parameter for call back */ char *cert_type; /* format for certificate (default: PEM)*/ - char *key; /* private key file name */ + char *key; /* private key filename */ struct curl_blob *key_blob; char *key_type; /* format for private key (default: PEM) */ char *key_passwd; /* plain text private key password */ @@ -331,7 +326,7 @@ struct ssl_config_data { BIT(falsestart); BIT(enable_beast); /* allow this flaw for interoperability's sake */ BIT(no_revoke); /* disable SSL certificate revocation checks */ - BIT(no_partialchain); /* don't accept partial certificate chains */ + BIT(no_partialchain); /* do not accept partial certificate chains */ BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation list errors */ BIT(native_ca_store); /* use the native ca store of operating system */ @@ -348,8 +343,8 @@ typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize); /* information stored about one single SSL session */ struct Curl_ssl_session { - char *name; /* host name for which this ID was used */ - char *conn_to_host; /* host name for the connection (may be NULL) */ + char *name; /* hostname for which this ID was used */ + char *conn_to_host; /* hostname for the connection (may be NULL) */ const char *scheme; /* protocol scheme used */ void *sessionid; /* as returned from the SSL layer */ size_t idsize; /* if known, otherwise 0 */ @@ -457,7 +452,7 @@ struct ntlmdata { unsigned int flags; unsigned char nonce[8]; unsigned int target_info_len; - void *target_info; /* TargetInfo received in the ntlm type-2 message */ + void *target_info; /* TargetInfo received in the NTLM type-2 message */ #endif }; #endif @@ -470,6 +465,7 @@ struct negotiatedata { gss_ctx_id_t context; gss_name_t spn; gss_buffer_desc output_token; + struct dynbuf channel_binding_data; #else #ifdef USE_WINDOWS_SSPI #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS @@ -511,9 +507,6 @@ struct ConnectBits { This is implicit when SSL-protocols are used through proxies, but can also be enabled explicitly by apps */ - BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection - in a CONNECT request with auth, so that - libcurl should reconnect and continue. */ BIT(proxy); /* if set, this transfer is done through a proxy - any type */ #endif /* always modify bits.close with the connclose() and connkeep() macros! */ @@ -535,10 +528,10 @@ struct ConnectBits { re-attempted at another connection. */ #ifndef CURL_DISABLE_FTP BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out - EPSV doesn't work we disable it for the forthcoming + EPSV does not work we disable it for the forthcoming requests */ BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out - EPRT doesn't work we disable it for the forthcoming + EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ @@ -548,6 +541,7 @@ struct ConnectBits { #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ + BIT(asks_multiplex); /* connection asks for multiplexing, but is not yet */ BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ @@ -562,6 +556,10 @@ struct ConnectBits { accept() */ BIT(parallel_connect); /* set TRUE when a parallel connect attempt has started (happy eyeballs) */ + BIT(aborted); /* connection was aborted, e.g. in unclean state */ + BIT(shutdown_handler); /* connection shutdown: handler shut down */ + BIT(shutdown_filters); /* connection shutdown: filters shut down */ + BIT(in_cpool); /* connection is kept in a connection pool */ }; struct hostname { @@ -630,27 +628,6 @@ struct easy_pollset { unsigned char actions[MAX_SOCKSPEREASYHANDLE]; }; -enum doh_slots { - /* Explicit values for first two symbols so as to match hard-coded - * constants in existing code - */ - DOH_PROBE_SLOT_IPADDR_V4 = 0, /* make 'V4' stand out for readability */ - DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */ - - /* Space here for (possibly build-specific) additional slot definitions */ -#ifdef USE_HTTPSRR - DOH_PROBE_SLOT_HTTPS = 2, /* for HTTPS RR */ -#endif - - /* for example */ - /* #ifdef WANT_DOH_FOOBAR_TXT */ - /* DOH_PROBE_SLOT_FOOBAR_TXT, */ - /* #endif */ - - /* AFTER all slot definitions, establish how many we have */ - DOH_PROBE_SLOTS -}; - /* * Specific protocol handler. */ @@ -676,7 +653,7 @@ struct Curl_handler { /* This function *MAY* be set to a protocol-dependent function that is run * after the connect() and everything is done, as a step in the connection. * The 'done' pointer points to a bool that should be set to TRUE if the - * function completes before return. If it doesn't complete, the caller + * function completes before return. If it does not complete, the caller * should call the ->connecting() function until it is. */ CURLcode (*connect_it)(struct Curl_easy *data, bool *done); @@ -707,7 +684,7 @@ struct Curl_handler { struct connectdata *conn, curl_socket_t *socks); /* This function *MAY* be set to a protocol-dependent function that is run - * by the curl_disconnect(), as a step in the disconnection. If the handler + * by the curl_disconnect(), as a step in the disconnection. If the handler * is called because the connection has been considered dead, * dead_connection is set to TRUE. The connection is (again) associated with * the transfer here. @@ -755,11 +732,11 @@ struct Curl_handler { the send function might need to be called while uploading, or vice versa. */ #define PROTOPT_DIRLOCK (1<<3) -#define PROTOPT_NONETWORK (1<<4) /* protocol doesn't use the network! */ +#define PROTOPT_NONETWORK (1<<4) /* protocol does not use the network! */ #define PROTOPT_NEEDSPWD (1<<5) /* needs a password, and if none is set it gets a default */ -#define PROTOPT_NOURLQUERY (1<<6) /* protocol can't handle - url query strings (?foo=bar) ! */ +#define PROTOPT_NOURLQUERY (1<<6) /* protocol cannot handle + URL query strings (?foo=bar) ! */ #define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per request instead of per connection */ #define PROTOPT_ALPN (1<<8) /* set ALPN for this */ @@ -770,9 +747,9 @@ struct Curl_handler { HTTP proxy as HTTP proxies may know this protocol and act as a gateway */ #define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ -#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in - user name and password */ -#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol can't proxy over TCP */ +#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ASCII) in + username and password */ +#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol cannot proxy over TCP */ #define CONNCHECK_NONE 0 /* No checks */ #define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ @@ -793,7 +770,7 @@ struct proxy_info { int port; unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in use */ - char *user; /* proxy user name string, allocated */ + char *user; /* proxy username string, allocated */ char *passwd; /* proxy password string, allocated */ }; @@ -809,20 +786,22 @@ struct ldapconninfo; * unique for an entire connection. */ struct connectdata { - struct Curl_llist_element bundle_node; /* conncache */ + struct Curl_llist_node cpool_node; /* conncache lists */ curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ void *closesocket_client; - /* This is used by the connection cache logic. If this returns TRUE, this + /* This is used by the connection pool logic. If this returns TRUE, this handle is still used by one or more easy handles and can only used by any other easy handle without careful consideration (== only for multiplexing) and it cannot be used by another multi handle! */ -#define CONN_INUSE(c) ((c)->easyq.size) +#define CONN_INUSE(c) Curl_llist_count(&(c)->easyq) /**** Fields set when inited and not modified again */ curl_off_t connection_id; /* Contains a unique number to make it easier to track the connections in the log output */ + char *destination; /* string carrying normalized hostname+port+scope */ + size_t destination_len; /* strlen(destination) + 1 */ /* 'dns_entry' is the particular host we use. This points to an entry in the DNS cache and it will not get pruned while locked. It gets unlocked in @@ -835,8 +814,8 @@ struct connectdata { const struct Curl_sockaddr_ex *remote_addr; struct hostname host; - char *hostname_resolve; /* host name to resolve to address, allocated */ - char *secondaryhostname; /* secondary socket host name (ftp) */ + char *hostname_resolve; /* hostname to resolve to address, allocated */ + char *secondaryhostname; /* secondary socket hostname (ftp) */ struct hostname conn_to_host; /* the host to connect to. valid only if bits.conn_to_host is set */ #ifndef CURL_DISABLE_PROXY @@ -844,25 +823,33 @@ struct connectdata { struct proxy_info http_proxy; #endif /* 'primary' and 'secondary' get filled with IP quadruple - (local/remote numerical ip address and port) whenever a is *attempted*. + (local/remote numerical ip address and port) whenever a connect is + *attempted*. When more than one address is tried for a connection these will hold data for the last attempt. When the connection is actually established these are updated with data which comes directly from the socket. */ struct ip_quadruple primary; struct ip_quadruple secondary; - char *user; /* user name string, allocated */ + char *user; /* username 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 */ struct curltime now; /* "current" time */ struct curltime created; /* creation time */ - struct curltime lastused; /* when returned to the connection cache */ + struct curltime lastused; /* when returned to the connection poolas idle */ curl_socket_t sock[2]; /* two sockets, the second is used for the data transfer when doing FTP */ Curl_recv *recv[2]; Curl_send *send[2]; struct Curl_cfilter *cfilter[2]; /* connection filters */ + struct { + struct curltime start[2]; /* when filter shutdown started */ + unsigned int timeout_ms; /* 0 means no timeout */ + } shutdown; + /* Last pollset used in connection shutdown. Used to detect changes + * for multi_socket API. */ + struct easy_pollset shutdown_poll; struct ssl_primary_config ssl_config; #ifndef CURL_DISABLE_PROXY @@ -908,11 +895,6 @@ struct connectdata { CtxtHandle *sslContext; #endif -#if defined(_WIN32) && defined(USE_WINSOCK) - struct curltime last_sndbuf_update; /* last time readwrite_upload called - win_update_buffer_size */ -#endif - #ifdef USE_GSASL struct gsasldata gsasl; #endif @@ -975,7 +957,6 @@ struct connectdata { unsigned int unused:1; /* avoids empty union */ } proto; - struct connectbundle *bundle; /* The bundle we are member of */ #ifdef USE_UNIX_SOCKETS char *unix_domain_socket; #endif @@ -986,7 +967,7 @@ struct connectdata { /* When this connection is created, store the conditions for the local end bind. This is stored before the actual bind and before any connection is made and will serve the purpose of being used for comparison reasons so - that subsequent bound-requested connections aren't accidentally reusing + that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; unsigned short localportrange; @@ -1045,7 +1026,7 @@ struct PureInfo { unsigned long httpauthavail; /* what host auth types were announced */ long numconnects; /* how many new connection did libcurl created */ char *contenttype; /* the content type of the object */ - char *wouldredirect; /* URL this would've been redirected to if asked to */ + char *wouldredirect; /* URL this would have been redirected to if asked to */ curl_off_t retry_after; /* info from Retry-After: header */ unsigned int header_size; /* size of read header(s) in bytes */ @@ -1054,7 +1035,7 @@ struct PureInfo { even when the session handle is no longer associated with a connection, and also allow curl_easy_reset() to clear this information from the session handle without disturbing information which is still alive, and - that might be reused, in the connection cache. */ + that might be reused, in the connection pool. */ struct ip_quadruple primary; int conn_remote_port; /* this is the "remote port", which is the port number of the used URL, independent of proxy or @@ -1064,19 +1045,28 @@ struct PureInfo { struct curl_certinfo certs; /* info about the certs. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ CURLproxycode pxcode; - BIT(timecond); /* set to TRUE if the time condition didn't match, which + BIT(timecond); /* set to TRUE if the time condition did not match, which thus made the document NOT get fetched */ BIT(used_proxy); /* the transfer used a proxy */ }; +struct pgrs_measure { + struct curltime start; /* when measure started */ + curl_off_t start_size; /* the 'cur_size' the measure started at */ +}; + +struct pgrs_dir { + curl_off_t total_size; /* total expected bytes */ + curl_off_t cur_size; /* transferred bytes so far */ + curl_off_t speed; /* bytes per second transferred */ + struct pgrs_measure limit; +}; struct Progress { time_t lastshow; /* time() of the last displayed progress meter or NULL to force redraw at next call */ - curl_off_t size_dl; /* total expected size */ - curl_off_t size_ul; /* total expected size */ - curl_off_t downloaded; /* transferred so far */ - curl_off_t uploaded; /* transferred so far */ + struct pgrs_dir ul; + struct pgrs_dir dl; curl_off_t current_speed; /* uses the currently fastest transfer */ @@ -1085,14 +1075,12 @@ struct Progress { timediff_t timespent; - curl_off_t dlspeed; - curl_off_t ulspeed; - timediff_t t_postqueue; timediff_t t_nslookup; timediff_t t_connect; timediff_t t_appconnect; timediff_t t_pretransfer; + timediff_t t_posttransfer; timediff_t t_starttransfer; timediff_t t_redirect; @@ -1101,14 +1089,6 @@ struct Progress { struct curltime t_startop; struct curltime t_acceptdata; - - /* upload speed limit */ - struct curltime ul_limit_start; - curl_off_t ul_limit_size; - /* download speed limit */ - struct curltime dl_limit_start; - curl_off_t dl_limit_size; - #define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */ curl_off_t speeder[ CURR_TIME ]; @@ -1205,7 +1185,7 @@ typedef enum { * One instance for each timeout an easy handle can set. */ struct time_node { - struct Curl_llist_element list; + struct Curl_llist_node list; struct curltime time; expire_id eid; }; @@ -1223,8 +1203,6 @@ struct urlpieces { }; struct UrlState { - /* Points to the connection cache */ - struct conncache *conn_cache; /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ @@ -1237,8 +1215,8 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* host name, port number and protocol of the first (not followed) request. - if set, this should be the host name that we will sent authorization to, + /* hostname, port number and protocol of the first (not followed) request. + if set, this should be the hostname that we will sent authorization to, no else. Used to make Location: following not keep sending user+password. This is strdup()ed data. */ char *first_host; @@ -1249,7 +1227,6 @@ struct UrlState { struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ long sessionage; /* number of the most recent session */ int os_errno; /* filled in with errno whenever an error occurs */ - char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */ long followlocation; /* redirect counter */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL @@ -1277,12 +1254,6 @@ struct UrlState { /* a place to store the most recently set (S)FTP entrypath */ char *most_recent_ftp_entrypath; -#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__) -/* do FTP line-end conversions on most platforms */ -#define CURL_DO_LINEEND_CONV - /* for FTP downloads: how many CRLFs did we converted to LFs? */ - curl_off_t crlf_conversions; -#endif char *range; /* range, if used. See README for detailed specification on this syntax. */ curl_off_t resume_from; /* continue [ftp] transfer from here */ @@ -1375,9 +1346,6 @@ struct UrlState { unsigned char select_bits; /* != 0 -> bitmask of socket events for this transfer overriding anything the socket may report */ -#ifdef CURLDEBUG - BIT(conncache_lock); -#endif /* when curl_easy_perform() is called, the multi handle is "owned" by the easy handle so curl_easy_cleanup() on such an easy handle will also close the multi handle! */ @@ -1390,7 +1358,7 @@ struct UrlState { called. */ BIT(allow_port); /* Is set.use_port allowed to take effect or not. This is always set TRUE when curl_easy_perform() is called. */ - BIT(authproblem); /* TRUE if there's some problem authenticating */ + BIT(authproblem); /* TRUE if there is some problem authenticating */ /* set after initial USER failure, to prevent an authentication loop */ BIT(wildcardmatch); /* enable wildcard matching */ BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous @@ -1428,12 +1396,12 @@ struct UrlState { struct Curl_multi; /* declared in multihandle.c */ enum dupstring { - STRING_CERT, /* client certificate file name */ + STRING_CERT, /* client certificate filename */ STRING_CERT_TYPE, /* format for certificate (default: PEM)*/ - STRING_KEY, /* private key file name */ + STRING_KEY, /* private key filename */ STRING_KEY_PASSWD, /* plain text private key password */ STRING_KEY_TYPE, /* format for private key (default: PEM) */ - STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */ + STRING_SSL_CAPATH, /* CA directory name (does not work on Windows) */ STRING_SSL_CAFILE, /* certificate file to verify peer against */ STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ @@ -1442,12 +1410,12 @@ enum dupstring { STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ STRING_SERVICE_NAME, /* Service name */ #ifndef CURL_DISABLE_PROXY - STRING_CERT_PROXY, /* client certificate file name */ + STRING_CERT_PROXY, /* client certificate filename */ STRING_CERT_TYPE_PROXY, /* format for certificate (default: PEM)*/ - STRING_KEY_PROXY, /* private key file name */ + STRING_KEY_PROXY, /* private key filename */ STRING_KEY_PASSWD_PROXY, /* plain text private key password */ STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */ - STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */ + STRING_SSL_CAPATH_PROXY, /* CA directory name (does not work on Windows) */ STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */ STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ @@ -1461,8 +1429,10 @@ enum dupstring { STRING_COOKIEJAR, /* dump all cookies to this file */ #endif STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */ - STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL doesn't specify */ + STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL does not specify */ STRING_DEVICE, /* local network interface/address to use */ + STRING_INTERFACE, /* local network interface to use */ + STRING_BINDHOST, /* local address to use */ STRING_ENCODING, /* Accept-Encoding string */ #ifndef CURL_DISABLE_FTP STRING_FTP_ACCOUNT, /* ftp account data */ @@ -1502,9 +1472,9 @@ enum dupstring { #ifdef USE_SSH STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ - STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ + STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ASCII hex */ STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */ - STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ + STRING_SSH_KNOWNHOSTS, /* filename of knownhosts file */ #endif #ifndef CURL_DISABLE_SMTP STRING_MAIL_FROM, @@ -1575,7 +1545,7 @@ enum dupblob { }; /* callback that gets called when this easy handle is completed within a multi - handle. Only used for internally created transfers, like for example + handle. Only used for internally created transfers, like for example DoH. */ typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result); @@ -1600,7 +1570,7 @@ struct UserDefined { #ifndef CURL_DISABLE_BINDLOCAL unsigned short localport; /* local port number to bind to */ unsigned short localportrange; /* number of additional port numbers to test - in case the 'localport' one can't be + in case the 'localport' one cannot be bind()ed */ #endif curl_write_callback fwrite_func; /* function that stores the output */ @@ -1633,9 +1603,10 @@ struct UserDefined { void *progress_client; /* pointer to pass to the progress callback */ void *ioctl_client; /* pointer to pass to the ioctl callback */ unsigned int timeout; /* ms, 0 means no timeout */ - unsigned int connecttimeout; /* ms, 0 means no timeout */ + unsigned int connecttimeout; /* ms, 0 means default timeout */ unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ unsigned int server_response_timeout; /* ms, 0 means no timeout */ + unsigned int shutdowntimeout; /* ms, 0 means default 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 @@ -1700,7 +1671,7 @@ struct UserDefined { struct curl_slist *postquote; /* after the transfer */ struct curl_slist *prequote; /* before the transfer, after type */ /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP - 1 - create directories that don't exist + 1 - create directories that do not exist 2 - the same but also allow MKD to fail once */ unsigned char ftp_create_missing_dirs; @@ -1747,6 +1718,7 @@ struct UserDefined { int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ + int tcp_keepcnt; /* maximum number of keepalive probes */ long expect_100_timeout; /* in milliseconds */ #if defined(USE_HTTP2) || defined(USE_HTTP3) @@ -1758,7 +1730,7 @@ struct UserDefined { 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 */ + curl_off_t dohfor_mid; /* this is a DoH request for that transfer */ #endif CURLU *uh; /* URL handle for the current parsed URL */ #ifndef CURL_DISABLE_HTTP @@ -1795,10 +1767,10 @@ struct UserDefined { /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially - and they don't change during operations. */ + and they do not change during operations. */ BIT(quick_exit); /* set 1L when it is okay to leak things (like - threads), as we're about to exit() anyway and - don't want lengthy cleanups to delay termination, + threads), as we are about to exit() anyway and + do not want lengthy cleanups to delay termination, e.g. after a DNS timeout */ BIT(get_filetime); /* get the time and get of the remote file */ #ifndef CURL_DISABLE_PROXY @@ -1818,7 +1790,7 @@ struct UserDefined { us */ BIT(wildcard_enabled); /* enable wildcard matching */ #endif - BIT(hide_progress); /* don't use the progress meter */ + BIT(hide_progress); /* do not use the progress meter */ BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */ BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */ BIT(http_follow_location); /* follow HTTP redirects */ @@ -1864,7 +1836,7 @@ struct UserDefined { #ifdef USE_UNIX_SOCKETS BIT(abstract_unix_socket); #endif - BIT(disallow_username_in_url); /* disallow username in url */ + BIT(disallow_username_in_url); /* disallow username in URL */ #ifndef CURL_DISABLE_DOH BIT(doh); /* DNS-over-HTTPS enabled */ BIT(doh_verifypeer); /* DoH certificate peer verification */ @@ -1909,22 +1881,23 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ unsigned int magic; - /* once an easy handle is tied to a connection cache + /* once an easy handle is tied to a connection pool a non-negative number to distinguish this transfer from - other using the same cache. For easier tracking + other using the same pool. For easier tracking in log output. This may wrap around after LONG_MAX to 0 again, so it - has no uniqueness guarantee for very large processings. */ + has no uniqueness guarantee for very large processings. + Note: it has no uniqueness either IFF more than one connection pool + is used by the libcurl application. */ curl_off_t id; - - /* first, two fields for the linked list of these */ - struct Curl_easy *next; - struct Curl_easy *prev; + /* once an easy handle is added to a multi, either explicitly by the + * libcurl application or implicitly during `curl_easy_perform()`, + * a unique identifier inside this one multi instance. */ + curl_off_t mid; struct connectdata *conn; - struct Curl_llist_element connect_queue; /* for the pending and msgsent - lists */ - struct Curl_llist_element conn_queue; /* list per connectdata */ + struct Curl_llist_node multi_queue; /* for multihandle list management */ + struct Curl_llist_node conn_queue; /* list per connectdata */ CURLMstate mstate; /* the handle's state */ CURLcode result; /* previous result */ diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c index 29389c2c7..cf8108ac5 100644 --- a/Utilities/cmcurl/lib/vauth/cleartext.c +++ b/Utilities/cmcurl/lib/vauth/cleartext.c @@ -100,11 +100,11 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, * Curl_auth_create_login_message() * * This is used to generate an already encoded LOGIN message containing the - * user name or password ready for sending to the recipient. + * username or password ready for sending to the recipient. * * Parameters: * - * valuep [in] - The user name or user's password. + * valuep [in] - The username or user's password. * out [out] - The result storage. * * Returns void. @@ -118,11 +118,11 @@ void Curl_auth_create_login_message(const char *valuep, struct bufref *out) * Curl_auth_create_external_message() * * This is used to generate an already encoded EXTERNAL message containing - * the user name ready for sending to the recipient. + * the username ready for sending to the recipient. * * Parameters: * - * user [in] - The user name. + * user [in] - The username. * out [out] - The result storage. * * Returns void. diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index 91fb261c5..f8bdd5458 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c @@ -51,7 +51,7 @@ * Parameters: * * chlg [in] - The challenge. - * userp [in] - The user name. + * userp [in] - The username. * passwdp [in] - The user's password. * out [out] - The result storage. * diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index a742cce26..4fc5b1c28 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -103,7 +103,7 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, case ',': if(!starts_with_quote) { - /* This signals the end of the content if we didn't get a starting + /* This signals the end of the content if we did not get a starting quote and then we do "sloppy" parsing */ c = 0; /* the end */ continue; @@ -142,7 +142,7 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, } #if !defined(USE_WINDOWS_SSPI) -/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */ +/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { @@ -151,7 +151,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); } -/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ascii string */ +/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ASCII string */ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ unsigned char *dest) /* 65 bytes */ { @@ -326,7 +326,7 @@ bool Curl_auth_is_digest_supported(void) * * data [in] - The session handle. * chlg [in] - The challenge message. - * userp [in] - The user name. + * userp [in] - The username. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * out [out] - The result storage. @@ -629,7 +629,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, } } else - break; /* We're done here */ + break; /* We are done here */ /* Pass all additional spaces here */ while(*chlg && ISBLANK(*chlg)) @@ -646,7 +646,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(before && !digest->stale) return CURLE_BAD_CONTENT_ENCODING; - /* We got this header without a nonce, that's a bad Digest line! */ + /* We got this header without a nonce, that is a bad Digest line! */ if(!digest->nonce) return CURLE_BAD_CONTENT_ENCODING; @@ -666,7 +666,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The user name. + * userp [in] - The username. * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. @@ -788,7 +788,7 @@ static CURLcode auth_create_digest_http_message( return CURLE_OUT_OF_MEMORY; if(digest->qop && strcasecompare(digest->qop, "auth-int")) { - /* We don't support auth-int for PUT or POST */ + /* We do not support auth-int for PUT or POST */ char hashed[65]; char *hashthis2; @@ -835,12 +835,12 @@ static CURLcode auth_create_digest_http_message( Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" - Digest parameters are all quoted strings. Username which is provided by + Digest parameters are all quoted strings. Username which is provided by 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 + 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); @@ -957,7 +957,7 @@ static CURLcode auth_create_digest_http_message( * Parameters: * * data [in] - The session handle. - * userp [in] - The user name. + * userp [in] - The username. * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index 4696f29ad..39a0c306d 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c @@ -60,12 +60,13 @@ bool Curl_auth_is_digest_supported(void) SECURITY_STATUS status; /* Query the security package for Digest */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); + status = + Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); /* Release the package buffer as it is not required anymore */ if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); } return (status == SEC_E_OK ? TRUE : FALSE); @@ -81,7 +82,7 @@ bool Curl_auth_is_digest_supported(void) * * data [in] - The session handle. * chlg [in] - The challenge message. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. * out [out] - The result storage. @@ -119,17 +120,18 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, } /* Query the security package for DigestSSP */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); + status = + Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); + failf(data, "SSPI: could not get auth info"); return CURLE_AUTH_ERROR; } token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ output_token = malloc(token_max); @@ -160,7 +162,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, p_identity = NULL; /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, + status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_DIGEST), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, @@ -190,20 +192,20 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(token_max); /* Generate our response message */ - status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, + status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, 0, 0, 0, &chlg_desc, 0, &context, &resp_desc, &attrs, &expiry); if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { #if !defined(CURL_DISABLE_VERBOSE_STRINGS) char buffer[STRERROR_LEN]; #endif - s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); free(spn); free(output_token); @@ -223,8 +225,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free); /* Free our handles */ - s_pSecFn->DeleteSecurityContext(&context); - s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_pSecFn->DeleteSecurityContext(&context); + Curl_pSecFn->FreeCredentialsHandle(&credentials); /* Free the identity structure */ Curl_sspi_free_identity(p_identity); @@ -291,7 +293,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, } } else - break; /* We're done here */ + break; /* We are done here */ /* Pass all additional spaces here */ while(*chlg && ISBLANK(*chlg)) @@ -324,8 +326,8 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, { size_t chlglen = strlen(chlg); - /* We had an input token before so if there's another one now that means we - provided bad credentials in the previous request or it's stale. */ + /* We had an input token before so if there is another one now that means we + provided bad credentials in the previous request or it is stale. */ if(digest->input_token) { bool stale = false; const char *p = chlg; @@ -379,7 +381,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. @@ -410,17 +412,18 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, (void) data; /* Query the security package for DigestSSP */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); + status = + Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), + &SecurityPackage); if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); + failf(data, "SSPI: could not get auth info"); return CURLE_AUTH_ERROR; } token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate the output buffer according to the max token size as indicated by the security package */ @@ -436,7 +439,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, (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_pSecFn->DeleteSecurityContext(digest->http_context); Curl_safefree(digest->http_context); } Curl_safefree(digest->user); @@ -463,13 +466,14 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, chlg_buf[4].pvBuffer = output_token; chlg_buf[4].cbBuffer = curlx_uztoul(token_max); - status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0); + status = Curl_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, + 0); if(status == SEC_E_OK) output_token_len = chlg_buf[4].cbBuffer; else { /* delete the context so a new one can be made */ infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", (long)status); - s_pSecFn->DeleteSecurityContext(digest->http_context); + Curl_pSecFn->DeleteSecurityContext(digest->http_context); Curl_safefree(digest->http_context); } } @@ -529,7 +533,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, } /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, + status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_DIGEST), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, @@ -565,7 +569,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, spn = curlx_convert_UTF8_to_tchar((char *) uripath); if(!spn) { - s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); free(output_token); @@ -579,7 +583,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Generate our response message */ - status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, + status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, ISC_REQ_USE_HTTP_STYLE, 0, 0, &chlg_desc, 0, @@ -589,13 +593,13 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); + Curl_pSecFn->CompleteAuthToken(&credentials, &resp_desc); else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { #if !defined(CURL_DISABLE_VERBOSE_STRINGS) char buffer[STRERROR_LEN]; #endif - s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); free(output_token); @@ -615,7 +619,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, output_token_len = resp_buf.cbBuffer; - s_pSecFn->FreeCredentialsHandle(&credentials); + Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); } @@ -660,7 +664,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Delete security context */ if(digest->http_context) { - s_pSecFn->DeleteSecurityContext(digest->http_context); + Curl_pSecFn->DeleteSecurityContext(digest->http_context); Curl_safefree(digest->http_context); } diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index 16b6e4037..748cdf93a 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c @@ -65,10 +65,10 @@ bool Curl_auth_is_gssapi_supported(void) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name. + * userp [in] - The username. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in[ - The host name. + * host [in[ - The hostname. * mutual_auth [in] - Flag specifying whether or not mutual authentication * is enabled. * chlg [in] - Optional challenge message. @@ -243,7 +243,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Process the maximum message size the server can receive */ if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server + we do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index 17a517a97..b168a27ad 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c @@ -55,13 +55,13 @@ bool Curl_auth_is_gssapi_supported(void) SECURITY_STATUS status; /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_KERBEROS), &SecurityPackage); /* Release the package buffer as it is not required anymore */ if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); } return (status == SEC_E_OK ? TRUE : FALSE); @@ -76,10 +76,10 @@ bool Curl_auth_is_gssapi_supported(void) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * mutual_auth [in] - Flag specifying whether or not mutual authentication * is enabled. * chlg [in] - Optional challenge message. @@ -118,18 +118,18 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->output_token) { /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_KERBEROS), &SecurityPackage); if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); + failf(data, "SSPI: could not get auth info"); return CURLE_AUTH_ERROR; } krb5->token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ krb5->output_token = malloc(krb5->token_max); @@ -158,7 +158,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, + status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_KERBEROS), SECPKG_CRED_OUTBOUND, NULL, @@ -197,7 +197,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); /* Generate our challenge-response message */ - status = s_pSecFn->InitializeSecurityContext(krb5->credentials, + status = Curl_pSecFn->InitializeSecurityContext(krb5->credentials, chlg ? krb5->context : NULL, krb5->spn, (mutual_auth ? @@ -215,7 +215,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, return CURLE_AUTH_ERROR; if(memcmp(&context, krb5->context, sizeof(context))) { - s_pSecFn->DeleteSecurityContext(krb5->context); + Curl_pSecFn->DeleteSecurityContext(krb5->context); memcpy(krb5->context, &context, sizeof(context)); } @@ -282,7 +282,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, } /* Get our response size information */ - status = s_pSecFn->QueryContextAttributes(krb5->context, + status = Curl_pSecFn->QueryContextAttributes(krb5->context, SECPKG_ATTR_SIZES, &sizes); @@ -304,7 +304,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, input_buf[1].cbBuffer = 0; /* Decrypt the inbound challenge and obtain the qop */ - status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); + status = Curl_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); if(status != SEC_E_OK) { infof(data, "GSSAPI handshake failure (empty security message)"); return CURLE_BAD_CONTENT_ENCODING; @@ -323,7 +323,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, ((unsigned long)indata[2] << 8) | indata[3]; /* Free the challenge as it is not required anymore */ - s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); + Curl_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); /* Process the security layer */ if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { @@ -335,7 +335,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Process the maximum message size the server can receive */ if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server + we do not require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } @@ -392,7 +392,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, wrap_buf[2].cbBuffer = sizes.cbBlockSize; /* Encrypt the data */ - status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, + status = Curl_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, &wrap_desc, 0); if(status != SEC_E_OK) { free(padding); @@ -448,14 +448,14 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) { /* Free our security context */ if(krb5->context) { - s_pSecFn->DeleteSecurityContext(krb5->context); + Curl_pSecFn->DeleteSecurityContext(krb5->context); free(krb5->context); krb5->context = NULL; } /* Free our credentials handle */ if(krb5->credentials) { - s_pSecFn->FreeCredentialsHandle(krb5->credentials); + Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); free(krb5->credentials); krb5->credentials = NULL; } diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 018e6a67e..0050b4132 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -59,10 +59,6 @@ /* "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) @@ -325,10 +321,10 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * ntlm [in/out] - The NTLM data struct being used and modified. * out [out] - The result storage. * @@ -384,9 +380,9 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, "%c%c" /* 2 zeroes */ "%c%c" /* host length */ "%c%c" /* host allocated space */ - "%c%c" /* host name offset */ + "%c%c" /* hostname offset */ "%c%c" /* 2 zeroes */ - "%s" /* host name */ + "%s" /* hostname */ "%s", /* domain string */ 0, /* trailing zero */ 0, 0, 0, /* part of type-1 long */ @@ -448,7 +444,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * ntlm [in/out] - The NTLM data struct being used and modified. * out [out] - The result storage. @@ -470,7 +466,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, 12 LM/LMv2 Response security buffer 20 NTLM/NTLMv2 Response security buffer 28 Target Name security buffer - 36 User Name security buffer + 36 username security buffer 44 Workstation Name security buffer (52) Session Key security buffer (*) (60) Flags long (*) @@ -482,15 +478,17 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, CURLcode result = CURLE_OK; size_t size; unsigned char ntlmbuf[NTLM_BUFSIZE]; - int lmrespoff; + unsigned int lmrespoff; unsigned char lmresp[24]; /* fixed-size */ - int ntrespoff; + unsigned int ntrespoff; unsigned int ntresplen = 24; unsigned char ntresp[24]; /* fixed-size */ unsigned char *ptr_ntresp = &ntresp[0]; unsigned char *ntlmv2resp = NULL; bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; - char host[HOSTNAME_MAX + 1] = ""; + /* The fixed hostname we provide, in order to not leak our real local host + name. Copy the name used by Firefox. */ + static const char host[] = "WORKSTATION"; const char *user; const char *domain = ""; size_t hostoff = 0; @@ -515,21 +513,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, user = userp; 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))) { - infof(data, "gethostname() failed, continuing without"); - hostlen = 0; - } - else { - hostlen = strlen(host); - } -#else - (void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME); - hostlen = sizeof(NTLM_HOSTNAME)-1; -#endif + hostlen = sizeof(host) - 1; if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { unsigned char ntbuffer[0x18]; @@ -585,7 +569,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, return result; Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp); - ntlm->flags &= ~NTLMFLAG_NEGOTIATE_NTLM2_KEY; + ntlm->flags &= ~(unsigned int)NTLMFLAG_NEGOTIATE_NTLM2_KEY; /* A safer but less compatible alternative is: * Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp); @@ -722,7 +706,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* Make sure that the domain, user and host strings fit in the buffer before we copy them there. */ if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) { - failf(data, "user + domain + host name too big"); + failf(data, "user + domain + hostname too big"); return CURLE_OUT_OF_MEMORY; } diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 92054316d..55ec8201d 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c @@ -55,12 +55,12 @@ bool Curl_auth_is_ntlm_supported(void) SECURITY_STATUS status; /* Query the security package for NTLM */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), + status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), &SecurityPackage); /* Release the package buffer as it is not required anymore */ if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); } return (status == SEC_E_OK ? TRUE : FALSE); @@ -75,10 +75,10 @@ bool Curl_auth_is_ntlm_supported(void) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * ntlm [in/out] - The NTLM data struct being used and modified. * out [out] - The result storage. * @@ -103,17 +103,17 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, Curl_auth_cleanup_ntlm(ntlm); /* Query the security package for NTLM */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), + status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), &SecurityPackage); if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); + failf(data, "SSPI: could not get auth info"); return CURLE_AUTH_ERROR; } ntlm->token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our output buffer */ ntlm->output_token = malloc(ntlm->token_max); @@ -141,7 +141,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, + status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *) TEXT(SP_NAME_NTLM), SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, @@ -167,7 +167,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max); /* Generate our type-1 message */ - status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, + status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, ntlm->spn, 0, 0, SECURITY_NETWORK_DREP, NULL, 0, @@ -175,7 +175,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, &attrs, &expiry); if(status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); + Curl_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); else if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) @@ -233,7 +233,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * ntlm [in/out] - The NTLM data struct being used and modified. * out [out] - The result storage. @@ -282,7 +282,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SEC_CHANNEL_BINDINGS channelBindings; SecPkgContext_Bindings pkgBindings; pkgBindings.Bindings = &channelBindings; - status = s_pSecFn->QueryContextAttributes( + status = Curl_pSecFn->QueryContextAttributes( ntlm->sslContext, SECPKG_ATTR_ENDPOINT_BINDINGS, &pkgBindings @@ -305,7 +305,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max); /* Generate our type-3 message */ - status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, + status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials, ntlm->context, ntlm->spn, 0, 0, SECURITY_NETWORK_DREP, @@ -343,14 +343,14 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { /* Free our security context */ if(ntlm->context) { - s_pSecFn->DeleteSecurityContext(ntlm->context); + Curl_pSecFn->DeleteSecurityContext(ntlm->context); free(ntlm->context); ntlm->context = NULL; } /* Free our credentials handle */ if(ntlm->credentials) { - s_pSecFn->FreeCredentialsHandle(ntlm->credentials); + Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); free(ntlm->credentials); ntlm->credentials = NULL; } diff --git a/Utilities/cmcurl/lib/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c index a4adbdcf1..dc94afa36 100644 --- a/Utilities/cmcurl/lib/vauth/oauth2.c +++ b/Utilities/cmcurl/lib/vauth/oauth2.c @@ -49,8 +49,8 @@ * * Parameters: * - * user[in] - The user name. - * host[in] - The host name. + * user[in] - The username. + * host[in] - The hostname. * port[in] - The port(when not Port 80). * bearer[in] - The bearer token. * out[out] - The result storage. @@ -87,7 +87,7 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * * Parameters: * - * user[in] - The user name. + * user[in] - The username. * bearer[in] - The bearer token. * out[out] - The result storage. * diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index e1d52b755..f48d9b700 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c @@ -65,10 +65,10 @@ bool Curl_auth_is_spnego_supported(void) * Parameters: * * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. + * userp [in] - The username in the format User or Domain\User. * passwdp [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * chlg64 [in] - The optional base64 encoded challenge message. * nego [in/out] - The Negotiate data struct being used and modified. * @@ -91,14 +91,16 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; + struct gss_channel_bindings_struct chan; (void) user; (void) password; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server - * rejected it (since we're again here). Exit with an error since we - * can't invent anything better */ + * rejected it (since we are again here). Exit with an error since we + * cannot invent anything better */ Curl_auth_cleanup_spnego(nego); return CURLE_LOGIN_DENIED; } @@ -148,13 +150,21 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, input_token.length = chlglen; } + /* Set channel binding data if available */ + if(nego->channel_binding_data.leng > 0) { + memset(&chan, 0, sizeof(struct gss_channel_bindings_struct)); + chan.application_data.length = nego->channel_binding_data.leng; + chan.application_data.value = nego->channel_binding_data.bufr; + chan_bindings = &chan; + } + /* Generate our challenge-response message */ major_status = Curl_gss_init_sec_context(data, &minor_status, &nego->context, nego->spn, &Curl_spnego_mech_oid, - GSS_C_NO_CHANNEL_BINDINGS, + chan_bindings, &input_token, &output_token, TRUE, diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index d3245d0b1..38b26ab90 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c @@ -57,13 +57,13 @@ bool Curl_auth_is_spnego_supported(void) SECURITY_STATUS status; /* Query the security package for Negotiate */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NEGOTIATE), &SecurityPackage); /* Release the package buffer as it is not required anymore */ if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); } @@ -79,10 +79,10 @@ bool Curl_auth_is_spnego_supported(void) * Parameters: * * data [in] - The session handle. - * user [in] - The user name in the format User or Domain\User. + * user [in] - The username in the format User or Domain\User. * password [in] - The user's password. * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * chlg64 [in] - The optional base64 encoded challenge message. * nego [in/out] - The Negotiate data struct being used and modified. * @@ -113,8 +113,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(nego->context && nego->status == SEC_E_OK) { /* We finished successfully our part of authentication, but server - * rejected it (since we're again here). Exit with an error since we - * can't invent anything better */ + * rejected it (since we are again here). Exit with an error since we + * cannot invent anything better */ Curl_auth_cleanup_spnego(nego); return CURLE_LOGIN_DENIED; } @@ -128,18 +128,18 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->output_token) { /* Query the security package for Negotiate */ - nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_NEGOTIATE), - &SecurityPackage); + nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_NEGOTIATE), + &SecurityPackage); if(nego->status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); + failf(data, "SSPI: could not get auth info"); return CURLE_AUTH_ERROR; } nego->token_max = SecurityPackage->cbMaxToken; /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); + Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our output buffer */ nego->output_token = malloc(nego->token_max); @@ -168,8 +168,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Acquire our credentials handle */ - nego->status = - s_pSecFn->AcquireCredentialsHandle(NULL, + nego->status = (DWORD) + Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)TEXT(SP_NAME_NEGOTIATE), SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, @@ -218,7 +218,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SEC_CHANNEL_BINDINGS channelBindings; SecPkgContext_Bindings pkgBindings; pkgBindings.Bindings = &channelBindings; - nego->status = s_pSecFn->QueryContextAttributes( + nego->status = (DWORD)Curl_pSecFn->QueryContextAttributes( nego->sslContext, SECPKG_ATTR_ENDPOINT_BINDINGS, &pkgBindings @@ -242,16 +242,16 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(nego->token_max); /* Generate our challenge-response message */ - nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials, - chlg ? nego->context : - NULL, - nego->spn, - ISC_REQ_CONFIDENTIALITY, - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, - 0, nego->context, - &resp_desc, &attrs, - &expiry); + nego->status = + (DWORD)Curl_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : NULL, + nego->spn, + ISC_REQ_CONFIDENTIALITY, + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, + 0, nego->context, + &resp_desc, &attrs, + &expiry); /* Free the decoded challenge as it is not required anymore */ free(chlg); @@ -259,7 +259,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(GSS_ERROR(nego->status)) { char buffer[STRERROR_LEN]; failf(data, "InitializeSecurityContext failed: %s", - Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); + Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -269,11 +269,12 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(nego->status == SEC_I_COMPLETE_NEEDED || nego->status == SEC_I_COMPLETE_AND_CONTINUE) { - nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc); + nego->status = (DWORD)Curl_pSecFn->CompleteAuthToken(nego->context, + &resp_desc); if(GSS_ERROR(nego->status)) { char buffer[STRERROR_LEN]; failf(data, "CompleteAuthToken failed: %s", - Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); + Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -332,14 +333,14 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) { /* Free our security context */ if(nego->context) { - s_pSecFn->DeleteSecurityContext(nego->context); + Curl_pSecFn->DeleteSecurityContext(nego->context); free(nego->context); nego->context = NULL; } /* Free our credentials handle */ if(nego->credentials) { - s_pSecFn->FreeCredentialsHandle(nego->credentials); + Curl_pSecFn->FreeCredentialsHandle(nego->credentials); free(nego->credentials); nego->credentials = NULL; } diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c index 62fc7c40f..ace43c47d 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.c +++ b/Utilities/cmcurl/lib/vauth/vauth.c @@ -48,7 +48,7 @@ * Parameters: * * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. + * host [in] - The hostname. * realm [in] - The realm. * * Returns a pointer to the newly allocated SPN. @@ -93,7 +93,7 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, return NULL; /* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar - must be freed by curlx_unicodefree we'll dupe the result so that the + must be freed by curlx_unicodefree we will dupe the result so that the pointer this function returns can be normally free'd. */ tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn); free(utf8_spn); @@ -115,14 +115,14 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, * Domain/User (curl Down-level format - for compatibility with existing code) * User@Domain (User Principal Name) * - * Note: The user name may be empty when using a GSS-API library or Windows + * Note: The username may be empty when using a GSS-API library or Windows * SSPI as the user and domain are either obtained from the credentials cache * when using GSS-API or via the currently logged in user's credentials when * using Windows SSPI. * * Parameters: * - * user [in] - The user name. + * user [in] - The username. * * Returns TRUE on success; otherwise FALSE. */ diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index a1e333b16..a3e192dcf 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c @@ -258,10 +258,11 @@ char *curl_version(void) api.ldapai_info_version = LDAP_API_INFO_VERSION; if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { - unsigned int patch = api.ldapai_vendor_version % 100; - unsigned int major = api.ldapai_vendor_version / 10000; + unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); + unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); unsigned int minor = - ((api.ldapai_vendor_version - major * 10000) - patch) / 100; + (((unsigned int)api.ldapai_vendor_version - major * 10000) + - patch) / 100; msnprintf(ldap_buf, sizeof(ldap_buf), "%s/%u.%u.%u", api.ldapai_vendor_name, major, minor, patch); src[i++] = ldap_buf; @@ -394,7 +395,7 @@ static const char * const supported_protocols[] = { }; /* - * Feature presence run-time check functions. + * Feature presence runtime check functions. * * Warning: the value returned by these should not change between * curl_global_init() and curl_global_cleanup() calls. @@ -540,7 +541,7 @@ static curl_version_info_data version_info = { LIBCURL_VERSION, LIBCURL_VERSION_NUM, OS, /* as found by configure or set by hand at build-time */ - 0, /* features bitmask is built at run-time */ + 0, /* features bitmask is built at runtime */ NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ NULL, /* zlib_version */ @@ -580,7 +581,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp) int features = 0; #if defined(USE_SSH) - static char ssh_buffer[80]; + static char ssh_buf[80]; /* 'ssh_buffer' clashes with libssh/libssh.h */ #endif #ifdef USE_SSL #ifdef CURL_WITH_MULTI_SSL @@ -596,7 +597,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp) static char zstd_buffer[80]; #endif - (void)stamp; /* avoid compiler warnings, we don't use this */ + (void)stamp; /* avoid compiler warnings, we do not use this */ #ifdef USE_SSL Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); @@ -621,8 +622,8 @@ curl_version_info_data *curl_version_info(CURLversion stamp) #endif #if defined(USE_SSH) - Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer)); - version_info.libssh_version = ssh_buffer; + Curl_ssh_version(ssh_buf, sizeof(ssh_buf)); + version_info.libssh_version = ssh_buf; #endif #ifdef HAVE_BROTLI @@ -640,7 +641,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp) #ifdef USE_NGHTTP2 { nghttp2_info *h2 = nghttp2_version(0); - version_info.nghttp2_ver_num = h2->version_num; + version_info.nghttp2_ver_num = (unsigned int)h2->version_num; version_info.nghttp2_version = h2->version_str; } #endif diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c index e0f239e15..25ec82746 100644 --- a/Utilities/cmcurl/lib/version_win32.c +++ b/Utilities/cmcurl/lib/version_win32.c @@ -30,8 +30,10 @@ #include "version_win32.h" #include "warnless.h" -/* The last #include files should be: */ +/* The last 2 #include files should be in this order */ +#ifdef BUILDING_LIBCURL #include "curl_memory.h" +#endif #include "memdebug.h" /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) @@ -53,7 +55,7 @@ struct OUR_OSVERSIONINFOEXW { /* * curlx_verify_windows_version() * - * This is used to verify if we are running on a specific windows version. + * This is used to verify if we are running on a specific Windows version. * * Parameters: * @@ -63,7 +65,7 @@ struct OUR_OSVERSIONINFOEXW { * ignored. * platform [in] - The optional platform identifier. * condition [in] - The test condition used to specifier whether we are - * checking a version less then, equal to or greater than + * checking a version less than, equal to or greater than * what is specified in the major and minor version * numbers. * @@ -78,13 +80,13 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, bool matched = FALSE; #if defined(CURL_WINDOWS_APP) - (void)buildVersion; - /* We have no way to determine the Windows version from Windows apps, - so let's assume we're running on the target Windows version. */ + so let's assume we are running on the target Windows version. */ const WORD fullVersion = MAKEWORD(minorVersion, majorVersion); const WORD targetVersion = (WORD)_WIN32_WINNT; + (void)buildVersion; + switch(condition) { case VERSION_LESS_THAN: matched = targetVersion < fullVersion; @@ -108,7 +110,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, } if(matched && (platform == PLATFORM_WINDOWS)) { - /* we're always running on PLATFORM_WINNT */ + /* we are always running on PLATFORM_WINNT */ matched = FALSE; } #elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \ diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h index 95c066112..95a9e7f21 100644 --- a/Utilities/cmcurl/lib/version_win32.h +++ b/Utilities/cmcurl/lib/version_win32.h @@ -44,7 +44,7 @@ typedef enum { PLATFORM_WINNT } PlatformIdentifier; -/* This is used to verify if we are running on a specific windows version */ +/* This is used to verify if we are running on a specific Windows version */ bool curlx_verify_windows_version(const unsigned int majorVersion, const unsigned int minorVersion, const unsigned int buildVersion, diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index d49af6ea4..ac7865c1a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -119,16 +119,38 @@ struct cf_msh3_ctx { struct cf_call_data call_data; struct curltime connect_started; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ - struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */ /* Flags written by msh3/msquic thread */ bool handshake_complete; bool handshake_succeeded; bool connected; + BIT(initialized); /* Flags written by curl thread */ BIT(verbose); BIT(active); }; +static void h3_stream_hash_free(void *stream); + +static void cf_msh3_ctx_init(struct cf_msh3_ctx *ctx, + const struct Curl_addrinfo *ai) +{ + DEBUGASSERT(!ctx->initialized); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); + ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; + ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + ctx->initialized = TRUE; +} + +static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_hash_destroy(&ctx->streams); + } + free(ctx); +} + static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data); /* How to access `call_data` from a cf_msh3 filter */ @@ -158,7 +180,7 @@ struct stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \ - Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct stream_ctx *stream) { @@ -191,7 +213,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); CURL_TRC_CF(data, cf, "data setup"); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -207,7 +229,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { CURL_TRC_CF(data, cf, "easy handle is done"); - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -293,7 +315,7 @@ static const MSH3_REQUEST_IF msh3_request_if = { msh3_data_sent }; -/* Decode HTTP status code. Returns -1 if no valid status code was +/* Decode HTTP status code. Returns -1 if no valid status code was decoded. (duplicate from http2.c) */ static int decode_status_code(const char *value, size_t len) { @@ -593,7 +615,8 @@ out: } static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_msh3_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -603,7 +626,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, size_t nheader, i; ssize_t nwritten = -1; struct cf_call_data save; - bool eos; CF_DATA_SAVE(save, cf, data); @@ -646,21 +668,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, nva[i].ValueLength = e->valuelen; } - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - /* known request body size or -1 */ - eos = FALSE; - break; - default: - /* there is not request body */ - eos = TRUE; - stream->upload_done = TRUE; - break; - } - CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, nva, nheader, @@ -689,7 +696,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, } /* TODO - msh3/msquic will hold onto this memory until the send complete - event. How do we make sure curl doesn't free it until then? */ + event. How do we make sure curl does not free it until then? */ *err = CURLE_OK; nwritten = len; } @@ -813,7 +820,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, CURLcode result; bool verify; - Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + DEBUGASSERT(ctx->initialized); conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config) return CURLE_FAILED_INIT; @@ -840,7 +847,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->api = MsH3ApiOpen(); if(!ctx->api) { - failf(data, "can't create msh3 api"); + failf(data, "cannot create msh3 api"); return CURLE_FAILED_INIT; } @@ -851,7 +858,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, &addr, !verify); if(!ctx->qconn) { - failf(data, "can't create msh3 connection"); + failf(data, "cannot create msh3 connection"); if(ctx->api) { MsH3ApiClose(ctx->api); ctx->api = NULL; @@ -883,7 +890,7 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) { + if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) { ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; return CURLE_COULDNT_CONNECT; @@ -904,7 +911,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "handshake succeeded"); cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -940,7 +946,6 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) MsH3ApiClose(ctx->api); ctx->api = NULL; } - Curl_hash_destroy(&ctx->streams); if(ctx->active) { /* We share our socket at cf->conn->sock[cf->sockindex] when active. @@ -979,10 +984,11 @@ static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); cf_msh3_close(cf, data); - free(cf->ctx); - cf->ctx = NULL; + if(cf->ctx) { + cf_msh3_ctx_free(cf->ctx); + cf->ctx = NULL; + } /* no CF_DATA_RESTORE(cf, save); its gone */ - } static CURLcode cf_msh3_query(struct Curl_cfilter *cf, @@ -1038,6 +1044,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_msh3_destroy, cf_msh3_connect, cf_msh3_close, + Curl_cf_def_shutdown, Curl_cf_def_get_host, cf_msh3_adjust_pollset, cf_msh3_data_pending, @@ -1080,9 +1087,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); - ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; - ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; + cf_msh3_ctx_init(ctx, ai); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); @@ -1090,7 +1095,7 @@ out: *pcf = (!result)? cf : NULL; if(result) { Curl_safefree(cf); - Curl_safefree(ctx); + cf_msh3_ctx_free(ctx); } return result; diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index 0d9d87f34..3b958e227 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -88,7 +88,7 @@ /* The pool keeps spares around and half of a full stream windows * seems good. More does not seem to improve performance. * The benefit of the pool is that stream buffer to not keep - * spares. So memory consumption goes down when streams run empty, + * spares. Memory consumption goes down when streams run empty, * have a large upload done, etc. */ #define H3_STREAM_POOL_SPARES \ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 @@ -129,16 +129,16 @@ struct cf_ngtcp2_ctx { nghttp3_settings h3settings; struct curltime started_at; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ - struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ struct dynbuf scratch; /* temp buffer for header construction */ - struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ size_t max_stream_window; /* max flow window for one stream */ uint64_t max_idle_ms; /* max idle time for QUIC connection */ uint64_t used_bidi_streams; /* bidi streams we have opened */ uint64_t max_bidi_streams; /* max bidi streams we can open */ int qlogfd; - BIT(conn_closed); /* connection is closed */ + BIT(initialized); + BIT(shutdown_started); /* queued shutdown packets */ }; /* How to access `call_data` from a cf_ngtcp2 filter */ @@ -146,6 +146,34 @@ struct cf_ngtcp2_ctx { #define CF_CTX_CALL_DATA(cf) \ ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data +static void h3_stream_hash_free(void *stream); + +static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) +{ + DEBUGASSERT(!ctx->initialized); + ctx->qlogfd = -1; + ctx->version = NGTCP2_PROTO_VER_MAX; + ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; + ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->initialized = TRUE; +} + +static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_dyn_free(&ctx->scratch); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->peer); + } + free(ctx); +} + struct pkt_io_ctx; static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -162,7 +190,6 @@ struct h3_stream_ctx { struct bufq sendbuf; /* h3 request body */ struct h1_req_parser h1; /* h1 request parsing */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - size_t upload_blocked_len; /* the amount written last and EGAINed */ curl_uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ int status_code; /* HTTP status code */ @@ -175,7 +202,7 @@ struct h3_stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) #define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\ Curl_hash_offt_get(&(ctx)->streams, (id)))) @@ -198,10 +225,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!data || !data->req.p.http) { - failf(data, "initialization failure, transfer not http initialized"); + if(!data) return CURLE_FAILED_INIT; - } if(stream) return CURLE_OK; @@ -217,7 +242,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->sendbuf_len_in_flight = 0; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -242,7 +267,7 @@ static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, NGHTTP3_H3_REQUEST_CANCELLED); result = cf_progress_egress(cf, data, NULL); if(result) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cancel stream -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cancel stream -> %d", stream->id, result); } } @@ -253,10 +278,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done", stream->id); cf_ngtcp2_stream_close(cf, data, stream); - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -266,7 +291,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct h3_stream_ctx **pstream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct h3_stream_ctx *stream; (void)cf; @@ -276,8 +300,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, return data; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -326,8 +352,8 @@ static void pktx_update_time(struct pkt_io_ctx *pktx, struct cf_ngtcp2_ctx *ctx = cf->ctx; vquic_ctx_update_time(&ctx->q); - pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS + - ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; + pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS + + (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; } static void pktx_init(struct pkt_io_ctx *pktx, @@ -417,7 +443,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, } } -static int init_ngh3_conn(struct Curl_cfilter *cf); +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf); static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) { @@ -491,12 +517,12 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, if(!data) data = CF_DATA_CURRENT(cf); if(data) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read_stream(len=%zu) -> %zd", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd", stream_id, buflen, nconsumed); if(nconsumed < 0) { struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id); if(data && stream) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] error on known stream, " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, " "reset=%d, closed=%d", stream_id, stream->reset, stream->closed); } @@ -506,8 +532,8 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, /* number of bytes inside buflen which consists of framing overhead * including QPACK HEADERS. In other words, it does not consume payload of * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); + ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed); + ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed); return 0; } @@ -556,8 +582,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, } rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] quic close(app_error=%" - CURL_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] quic close(app_error=%" + FMT_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { cf_ngtcp2_h3_err_set(cf, data, rv); @@ -582,7 +608,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid, (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -620,8 +646,8 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, (void)tconn; ctx->max_bidi_streams = max_streams; if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" CURL_PRIu64 - ", used %" CURL_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, + CURL_TRC_CF(data, cf, "max bidi streams now %" FMT_PRIu64 + ", used %" FMT_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, (curl_uint64_t)ctx->used_bidi_streams); return 0; } @@ -647,8 +673,7 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid, } s_data = get_stream_easy(cf, data, stream_id, &stream); if(s_data && stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" CURL_PRId64 "] unblock quic flow", - stream_id); + CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id); stream->quic_flow_blocked = FALSE; h3_drain_stream(cf, s_data); } @@ -663,7 +688,7 @@ static void cb_rand(uint8_t *dest, size_t destlen, result = Curl_rand(NULL, dest, destlen); if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand + /* cb_rand is only used for non-cryptographic context. If Curl_rand failed, just fill 0 and call it *random*. */ memset(dest, 0, destlen); } @@ -706,6 +731,11 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, return 0; } +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(push) +# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif + static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ @@ -749,6 +779,10 @@ static ngtcp2_callbacks ng_callbacks = { NULL, /* early_data_rejected */ }; +#if defined(_MSC_VER) && defined(_DLL) +# pragma warning(pop) +#endif + /** * Connection maintenance like timeouts on packet ACKs etc. are done by us, not * the OS like for TCP. POLL events on the socket therefore are not @@ -798,7 +832,8 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, if(timeout % NGTCP2_MILLISECONDS) { timeout += NGTCP2_MILLISECONDS; } - Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); } } return CURLE_OK; @@ -815,6 +850,9 @@ static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, return; Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); + if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) + want_send = TRUE; + if(want_recv || want_send) { struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; @@ -855,11 +893,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, stream->id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id); } h3_drain_stream(cf, data); return 0; @@ -875,7 +913,7 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos); if(stream->xfer_result) - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu " "bytes of headers", stream->id, stream->xfer_result, blen); } } @@ -891,7 +929,7 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu bytes " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu bytes " "of data", stream->id, stream->xfer_result, blen); } } @@ -914,12 +952,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE); if(blen) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] ACK %zu bytes of DATA", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA", stream->id, blen); ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen); ngtcp2_conn_extend_max_offset(ctx->qconn, blen); } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu", stream->id, blen); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu", stream->id, blen); return 0; } @@ -954,10 +992,10 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(!stream) return 0; - /* add a CRLF only if we've received some headers */ + /* add a CRLF only if we have received some headers */ h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -1005,7 +1043,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, if(!result) h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch), Curl_dyn_len(&ctx->scratch), FALSE); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, Curl_dyn_ptr(&ctx->scratch)); if(result) { return -1; @@ -1013,7 +1051,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); Curl_dyn_reset(&ctx->scratch); @@ -1046,7 +1084,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, app_error_code); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; @@ -1065,9 +1103,9 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } return 0; @@ -1091,7 +1129,7 @@ static nghttp3_callbacks ngh3_callbacks = { NULL /* recv_settings */ }; -static int init_ngh3_conn(struct Curl_cfilter *cf) +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf) { struct cf_ngtcp2_ctx *ctx = cf->ctx; CURLcode result; @@ -1160,14 +1198,13 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, (void)cf; if(stream->reset) { - failf(data, - "HTTP/3 stream %" CURL_PRId64 " reset by server", stream->id); + failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" CURL_PRId64 " was closed cleanly, but before " + "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); *err = CURLE_HTTP3; @@ -1202,7 +1239,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data); - if(!stream || ctx->conn_closed) { + if(!stream || ctx->shutdown_started) { *err = CURLE_RECV_ERROR; goto out; } @@ -1214,7 +1251,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); *err = stream->xfer_result; nread = -1; @@ -1239,7 +1276,7 @@ out: nread = -1; } } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", stream? stream->id : -1, blen, nread, *err); CF_DATA_RESTORE(cf, save); return nread; @@ -1268,11 +1305,11 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, Curl_bufq_skip(&stream->sendbuf, skiplen); stream->sendbuf_len_in_flight -= skiplen; - /* Everything ACKed, we resume upload processing */ - if(!stream->sendbuf_len_in_flight) { + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; + return NGHTTP3_ERR_CALLBACK_FAILURE; } } return 0; @@ -1330,14 +1367,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -1451,12 +1487,12 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send, " "connection is closing", stream->id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send -> " - "%d (%s)", stream->id, rc, ngtcp2_strerror(rc)); + CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); break; } *err = CURLE_SEND_ERROR; @@ -1465,10 +1501,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); } @@ -1481,7 +1517,8 @@ out: } static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1497,6 +1534,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data); *err = CURLE_OK; + (void)eos; /* TODO: use for stream EOF and block handling */ result = cf_progress_ingress(cf, data, &pktx); if(result) { *err = result; @@ -1504,7 +1542,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } if(!stream || stream->id < 0) { - if(ctx->conn_closed) { + if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); *err = CURLE_SEND_ERROR; sent = -1; @@ -1518,27 +1556,12 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, stream = H3_STREAM_CTX(ctx, data); } else if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); *err = stream->xfer_result; sent = -1; goto out; } - else if(stream->upload_blocked_len) { - /* the data in `buf` has already been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= stream->upload_blocked_len); - if(len < stream->upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happen. We are not prepared to handle that. */ - failf(data, "HTTP/3 send again with decreased length"); - *err = CURLE_HTTP3; - sent = -1; - goto out; - } - sent = (ssize_t)stream->upload_blocked_len; - stream->upload_blocked_len = 0; - } else if(stream->closed) { if(stream->resp_hds_complete) { /* Server decided to close the stream after having sent us a final @@ -1546,19 +1569,19 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * body. This happens on 30x or 40x responses. * We silently discard the data sent, since this is not a transport * error situation. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; sent = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; sent = -1; goto out; } - else if(ctx->conn_closed) { + else if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot send on closed connection"); *err = CURLE_SEND_ERROR; sent = -1; @@ -1566,7 +1589,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->id, len, sent, *err); if(sent < 0) { @@ -1582,25 +1605,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent = -1; } - if(stream && sent > 0 && stream->sendbuf_len_in_flight) { - /* We have unacknowledged DATA and cannot report success to our - * caller. Instead we EAGAIN and remember how much we have already - * "written" into our various internal connection buffers. */ - stream->upload_blocked_len = sent; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), " - "%zu bytes in flight -> EGAIN", stream->id, len, - stream->sendbuf_len_in_flight); - *err = CURLE_AGAIN; - sent = -1; - } - out: result = check_and_set_expiry(cf, data, &pktx); if(result) { *err = result; sent = -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->id : -1, len, sent, *err); CF_DATA_RESTORE(cf, save); return sent; @@ -1613,7 +1624,6 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1631,7 +1641,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, ++pktx->pkt_count; ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); + (socklen_t)ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, remote_addrlen); pi.ecn = (uint8_t)ecn; @@ -1747,7 +1757,7 @@ static ssize_t read_pkt_to_send(void *userp, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] block quic flow", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] block quic flow", (curl_int64_t)stream_id); DEBUGASSERT(stream); if(stream) @@ -1867,7 +1877,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, DEBUGASSERT(nread > 0); if(pktcnt == 0) { /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We'll see. */ + * payload size or it is a PMTUD. We will see. */ gsolen = (size_t)nread; } else if((size_t)nread > gsolen || @@ -1961,7 +1971,8 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream && !stream->send_closed) { stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf); + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } break; @@ -1983,53 +1994,116 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, return result; } -static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) +static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) { struct cf_call_data save = ctx->call_data; + if(!ctx->initialized) + return; if(ctx->qlogfd != -1) { close(ctx->qlogfd); } + ctx->qlogfd = -1; Curl_vquic_tls_cleanup(&ctx->tls); vquic_ctx_free(&ctx->q); if(ctx->h3conn) nghttp3_conn_del(ctx->h3conn); if(ctx->qconn) ngtcp2_conn_del(ctx->qconn); - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_dyn_free(&ctx->scratch); - Curl_hash_clean(&ctx->streams); - Curl_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - - memset(ctx, 0, sizeof(*ctx)); - ctx->qlogfd = -1; ctx->call_data = save; } -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(ctx && ctx->qconn && !ctx->conn_closed) { + struct cf_call_data save; + struct pkt_io_ctx pktx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + pktx_init(&pktx, cf, data); + + if(!ctx->shutdown_started) { char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - struct pkt_io_ctx pktx; - ngtcp2_ssize rc; - - ctx->conn_closed = TRUE; - pktx_init(&pktx, cf, data); - rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - CURL_TRC_CF(data, cf, "closing connection(err_type=%d, err_code=%" - CURL_PRIu64 ") -> %d", ctx->last_error.type, - (curl_uint64_t)ctx->last_error.error_code, (int)rc); - if(rc > 0) { - while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && - SOCKERRNO == EINTR); + ngtcp2_ssize nwritten; + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = cf_progress_egress(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } } + + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + FMT_PRIu64 ") -> %d", ctx->last_error.type, + (curl_uint64_t)ctx->last_error.error_code, (int)nwritten); + if(nwritten > 0) { + Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &result); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", result); + goto out; + } + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; + } + } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); + *done = TRUE; + goto out; + } + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. So, we are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + bool done; + cf_ngtcp2_shutdown(cf, data, &done); } static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -2040,7 +2114,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx && ctx->qconn) { cf_ngtcp2_conn_close(cf, data); - cf_ngtcp2_ctx_clear(ctx); + cf_ngtcp2_ctx_close(ctx); CURL_TRC_CF(data, cf, "close"); } cf->connected = FALSE; @@ -2049,18 +2123,11 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); CURL_TRC_CF(data, cf, "destroy"); - if(ctx) { - cf_ngtcp2_ctx_clear(ctx); - free(ctx); + if(cf->ctx) { + cf_ngtcp2_ctx_free(cf->ctx); + cf->ctx = NULL; } - cf->ctx = NULL; - /* No CF_DATA_RESTORE(cf, save) possible */ - (void)save; } #ifdef USE_OPENSSL @@ -2105,7 +2172,7 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ - /* Enable the session cache because it's a prerequisite for the + /* Enable the session cache because it is a prerequisite for the * "new session" callback. Use the "external storage" mode to prevent * OpenSSL from creating an internal session cache. */ @@ -2120,7 +2187,7 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } #elif defined(USE_WOLFSSL) - if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) { + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) { failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); return CURLE_FAILED_INIT; } @@ -2142,14 +2209,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, const struct Curl_sockaddr_ex *sockaddr = NULL; int qfd; - ctx->version = NGTCP2_PROTO_VER_MAX; - ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; - ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); - + DEBUGASSERT(ctx->initialized); result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) return result; @@ -2196,7 +2256,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, (struct sockaddr *)&ctx->q.local_addr, ctx->q.local_addrlen); ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->sa_addr, sockaddr->addrlen); + &sockaddr->sa_addr, (socklen_t)sockaddr->addrlen); rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, &ctx->connected_path, @@ -2211,7 +2271,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #elif defined(USE_GNUTLS) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); #else - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle); #endif ngtcp2_ccerr_default(&ctx->last_error); @@ -2250,12 +2310,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { - /* Not time yet to attempt the next connect */ - CURL_TRC_CF(data, cf, "waiting for reconnect time"); - goto out; - } - if(!ctx->qconn) { ctx->started_at = now; result = cf_connect_start(cf, data, &pktx); @@ -2331,7 +2385,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, * by callback. QUIC counts the number over the lifetime of the * connection, ever increasing. * We count the *open* transfers plus the budget for new ones. */ - if(!ctx->qconn || ctx->conn_closed) { + if(!ctx->qconn || ctx->shutdown_started) { *pres1 = 0; } else if(ctx->max_bidi_streams) { @@ -2343,8 +2397,8 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; } else /* transport params not arrived yet? take our default. */ - *pres1 = Curl_multi_max_concurrent_streams(data->multi); - CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%zu in use)", cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); CF_DATA_RESTORE(cf, save); @@ -2389,7 +2443,7 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); *input_pending = FALSE; - if(!ctx->qconn || ctx->conn_closed) + if(!ctx->qconn || ctx->shutdown_started) goto out; /* Both sides of the QUIC connection announce they max idle times in @@ -2416,8 +2470,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, alive = TRUE; if(*input_pending) { CURLcode result; - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, only "protocol frames" */ *input_pending = FALSE; result = cf_progress_ingress(cf, data, NULL); @@ -2437,6 +2491,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_destroy, cf_ngtcp2_connect, cf_ngtcp2_close, + cf_ngtcp2_shutdown, Curl_cf_def_get_host, cf_ngtcp2_adjust_pollset, cf_ngtcp2_data_pending, @@ -2463,8 +2518,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->qlogfd = -1; - cf_ngtcp2_ctx_clear(ctx); + cf_ngtcp2_ctx_init(ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) @@ -2485,7 +2539,7 @@ out: if(udp_cf) Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_safefree(cf); - Curl_safefree(ctx); + cf_ngtcp2_ctx_free(ctx); } return result; } diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c index 8b9e889d7..6121b2f89 100644 --- a/Utilities/cmcurl/lib/vquic/curl_osslq.c +++ b/Utilities/cmcurl/lib/vquic/curl_osslq.c @@ -71,7 +71,7 @@ /* The pool keeps spares around and half of a full stream window * seems good. More does not seem to improve performance. * The benefit of the pool is that stream buffer to not keep - * spares. So memory consumption goes down when streams run empty, + * spares. Memory consumption goes down when streams run empty, * have a large upload done, etc. */ #define H3_STREAM_POOL_SPARES \ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 @@ -232,7 +232,7 @@ static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, if(!s->ssl) { return CURLE_FAILED_INIT; } - s->id = SSL_get_stream_id(s->ssl); + s->id = (curl_int64_t)SSL_get_stream_id(s->ssl); SSL_set_app_data(s->ssl, user_data); return CURLE_OK; } @@ -288,34 +288,121 @@ struct cf_osslq_ctx { struct curltime started_at; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ struct curltime first_byte_at; /* when first byte was recvd */ - struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ size_t max_stream_window; /* max flow window for one stream */ uint64_t max_idle_ms; /* max idle time for QUIC connection */ + BIT(initialized); BIT(got_first_byte); /* if first byte was received */ -#ifdef USE_OPENSSL BIT(x509_store_setup); /* if x509 store has been set up */ BIT(protocol_shutdown); /* QUIC connection is shut down */ -#endif + BIT(need_recv); /* QUIC connection needs to receive */ + BIT(need_send); /* QUIC connection needs to send */ }; -static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx) +static void h3_stream_hash_free(void *stream); + +static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx) +{ + DEBUGASSERT(!ctx->initialized); + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->initialized = TRUE; +} + +static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_bufcp_free(&ctx->stream_bufcp); + Curl_hash_clean(&ctx->streams); + Curl_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->peer); + } + free(ctx); +} + +static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx) { struct cf_call_data save = ctx->call_data; cf_osslq_h3conn_cleanup(&ctx->h3); Curl_vquic_tls_cleanup(&ctx->tls); vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_hash_clean(&ctx->streams); - Curl_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - - memset(ctx, 0, sizeof(*ctx)); ctx->call_data = save; } +static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_osslq_ctx *ctx = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + int rc; + + CF_DATA_SAVE(save, cf, data); + + if(cf->shutdown || ctx->protocol_shutdown) { + *done = TRUE; + return CURLE_OK; + } + + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + ctx->need_send = FALSE; + ctx->need_recv = FALSE; + + rc = SSL_shutdown_ex(ctx->tls.ossl.ssl, + SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0); + if(rc == 0) { /* ongoing */ + CURL_TRC_CF(data, cf, "shutdown ongoing"); + ctx->need_recv = TRUE; + goto out; + } + else if(rc == 1) { /* done */ + CURL_TRC_CF(data, cf, "shutdown finished"); + *done = TRUE; + goto out; + } + else { + long sslerr; + char err_buffer[256]; + int err = SSL_get_error(ctx->tls.ossl.ssl, rc); + + switch(err) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + CURL_TRC_CF(data, cf, "shutdown not received, but closed"); + *done = TRUE; + goto out; + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "shutdown sent, want receive"); + ctx->need_recv = TRUE; + goto out; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "shutdown send blocked"); + ctx->need_send = TRUE; + goto out; + default: + /* We give up on this. */ + sslerr = ERR_get_error(); + CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) : + osslq_SSL_ERROR_to_str(err)), + SOCKERRNO); + *done = TRUE; + result = CURLE_OK; + goto out; + } + } +out: + CF_DATA_RESTORE(cf, save); + return result; +} + static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_osslq_ctx *ctx = cf->ctx; @@ -323,9 +410,14 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx && ctx->tls.ossl.ssl) { - /* TODO: send connection close */ CURL_TRC_CF(data, cf, "cf_osslq_close()"); - cf_osslq_ctx_clear(ctx); + if(!cf->shutdown && !ctx->protocol_shutdown) { + /* last best effort, which OpenSSL calls a "rapid" shutdown. */ + SSL_shutdown_ex(ctx->tls.ossl.ssl, + (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID), + NULL, 0); + } + cf_osslq_ctx_close(ctx); } cf->connected = FALSE; @@ -341,8 +433,9 @@ static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) CURL_TRC_CF(data, cf, "destroy"); if(ctx) { CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); - cf_osslq_ctx_clear(ctx); - free(ctx); + if(ctx->tls.ossl.ssl) + cf_osslq_ctx_close(ctx); + cf_osslq_ctx_free(ctx); } cf->ctx = NULL; /* No CF_DATA_RESTORE(cf, save) possible */ @@ -355,12 +448,12 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, struct Curl_easy *data) { struct cf_osslq_ctx *ctx = cf->ctx; - int64_t stream_id = SSL_get_stream_id(stream_ssl); + curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl); if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) { /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream", - (curl_int64_t)stream_id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream", + stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; } @@ -370,13 +463,13 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, nstream->id = stream_id; nstream->ssl = stream_ssl; Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream", - (curl_int64_t)stream_id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] accepted remote uni stream", + stream_id); break; } default: - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read" - " stream", (curl_int64_t)stream_id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read" + " stream", stream_id); SSL_free(stream_ssl); return CURLE_FAILED_INIT; } @@ -440,7 +533,7 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, /* detail is already set to the SSL error above */ - /* If we e.g. use SSLv2 request-method and the server doesn't like us + /* If we e.g. use SSLv2 request-method and the server does not like us * (RST connection, etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ @@ -470,7 +563,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -484,7 +576,6 @@ struct h3_stream_ctx { struct bufq recvbuf; /* h3 response body */ struct h1_req_parser h1; /* h1 request parsing */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - size_t upload_blocked_len; /* the amount written last and EGAINed */ size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ curl_uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ @@ -498,7 +589,7 @@ struct h3_stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct h3_stream_ctx *stream) { @@ -521,10 +612,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!data || !data->req.p.http) { - failf(data, "initialization failure, transfer not http initialized"); + if(!data) return CURLE_FAILED_INIT; - } if(stream) return CURLE_OK; @@ -545,7 +634,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->recv_buf_nonflow = 0; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -560,7 +649,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] easy handle is done", + CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done", stream->s.id); if(ctx->h3.conn && !stream->closed) { nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); @@ -570,7 +659,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) stream->closed = TRUE; } - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -580,7 +669,6 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct Curl_easy *sdata; if(stream && stream->s.id == stream_id) { return &stream->s; @@ -595,8 +683,10 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, return &ctx->h3.s_qpack_dec; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -657,11 +747,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, stream->s.id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->s.id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->s.id); } h3_drain_stream(cf, data); return 0; @@ -721,12 +811,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, result = write_resp_raw(cf, data, buf, buflen, TRUE); if(result) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, ERROR %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, ERROR %d", stream->s.id, buflen, result); return NGHTTP3_ERR_CALLBACK_FAILURE; } stream->download_recvd += (curl_off_t)buflen; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, total=%zd", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd", stream->s.id, buflen, stream->download_recvd); h3_drain_stream(cf, data); return 0; @@ -744,7 +834,7 @@ static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, (void)conn; (void)stream_id; if(stream) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] deferred consume %zu bytes", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] deferred consume %zu bytes", stream->s.id, consumed); return 0; } @@ -782,7 +872,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, return -1; ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", stream->status_code); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, line); result = write_resp_raw(cf, data, line, ncopy, FALSE); if(result) { return -1; @@ -790,7 +880,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); @@ -829,13 +919,13 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(!stream) return 0; - /* add a CRLF only if we've received some headers */ + /* add a CRLF only if we have received some headers */ result = write_resp_raw(cf, data, "\r\n", 2, FALSE); if(result) { return -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d", stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; @@ -859,7 +949,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid, if(!stream || !stream->s.ssl) return 0; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] stop_sending", stream_id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] stop_sending", stream_id); cf_osslq_stream_close(&stream->s); return 0; } @@ -879,7 +969,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, SSL_STREAM_RESET_ARGS args = {0}; args.quic_error_code = app_error_code; rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); if(!rv) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -939,14 +1029,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", stream->s.id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->s.id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -977,8 +1066,8 @@ static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, Curl_bufq_skip(&stream->sendbuf, skiplen); stream->sendbuf_len_in_flight -= skiplen; - /* Everything ACKed, we resume upload processing */ - if(!stream->sendbuf_len_in_flight) { + /* Resume upload processing if we have more data to send */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { int rv = nghttp3_conn_resume_stream(conn, stream_id); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGHTTP3_ERR_CALLBACK_FAILURE; @@ -1072,9 +1161,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, BIO *bio = NULL; BIO_ADDR *baddr = NULL; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + DEBUGASSERT(ctx->initialized); result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC); if(result) goto out; @@ -1188,7 +1275,7 @@ static ssize_t h3_quic_recv(void *reader_ctx, return -1; } else if(detail == SSL_ERROR_ZERO_RETURN) { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> EOS", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> EOS", x->s->id); x->s->recvd_eos = TRUE; return 0; @@ -1197,8 +1284,8 @@ static ssize_t h3_quic_recv(void *reader_ctx, SSL_STREAM_STATE_RESET_REMOTE) { uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" CURL_PRIu64, + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> RESET, " + "rv=%d, app_err=%" FMT_PRIu64, x->s->id, rv, (curl_uint64_t)app_error_code); if(app_error_code != NGHTTP3_H3_NO_ERROR) { x->s->reset = TRUE; @@ -1254,7 +1341,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, buf, blen, 0); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forward %zu bytes " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forward %zu bytes " "to nghttp3 -> %zd", s->id, blen, nread); if(nread < 0) { failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", @@ -1293,7 +1380,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, NGHTTP3_H3_NO_ERROR); s->closed = TRUE; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] close nghttp3 stream -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] close nghttp3 stream -> %d", s->id, rv); if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { failf(data, "nghttp3_conn_close_stream returned error: %s", @@ -1306,7 +1393,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, } out: if(result) - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_osslq_stream_recv -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_osslq_stream_recv -> %d", s->id, result); return result; } @@ -1347,11 +1434,12 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, } if(ctx->h3.conn) { - struct Curl_easy *sdata; + struct Curl_llist_node *e; struct h3_stream_ctx *stream; /* PULL all open streams */ DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) { stream = H3_STREAM_CTX(ctx, sdata); if(stream && !stream->closed && @@ -1374,11 +1462,12 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_osslq_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct h3_stream_ctx *stream; if(ctx->h3.conn) { - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn) { stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->s.ssl && stream->s.send_blocked && @@ -1430,7 +1519,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, s = cf_osslq_get_qstream(cf, data, stream_id); if(!s) { failf(data, "nghttp3_conn_writev_stream gave unknown stream %" - CURL_PRId64, (curl_int64_t)stream_id); + FMT_PRId64, (curl_int64_t)stream_id); result = CURLE_SEND_ERROR; goto out; } @@ -1442,23 +1531,16 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, for(i = 0; (i < n) && !blocked; ++i) { /* Without stream->s.ssl, we closed that already, so * pretend the write did succeed. */ -#ifdef SSL_WRITE_FLAG_CONCLUDE - /* Since OpenSSL v3.3.x, on last chunk set EOS if needed */ uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0; written = vec[i].len; ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, &written); if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) eos_written = TRUE; -#else - written = vec[i].len; - ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len, - &written); -#endif if(ok) { /* As OpenSSL buffers the data, we count this as acknowledged * from nghttp3's point of view */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send %zu bytes to QUIC ok", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send %zu bytes to QUIC ok", s->id, vec[i].len); acked_len += vec[i].len; } @@ -1468,14 +1550,14 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: /* QUIC blocked us from writing more */ - CURL_TRC_CF(data, cf, "[%"CURL_PRId64 "] send %zu bytes to " + CURL_TRC_CF(data, cf, "[%"FMT_PRId64 "] send %zu bytes to " "QUIC blocked", s->id, vec[i].len); written = 0; nghttp3_conn_block_stream(ctx->h3.conn, s->id); s->send_blocked = blocked = TRUE; break; default: - failf(data, "[%"CURL_PRId64 "] send %zu bytes to QUIC, SSL error %d", + failf(data, "[%"FMT_PRId64 "] send %zu bytes to QUIC, SSL error %d", s->id, vec[i].len, detail); result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); goto out; @@ -1501,13 +1583,13 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, result = CURLE_SEND_ERROR; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forwarded %zu/%zu h3 bytes " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forwarded %zu/%zu h3 bytes " "to QUIC, eos=%d", s->id, acked_len, total_len, eos); } if(eos && !s->send_blocked && !eos_written) { /* wrote everything and H3 indicates end of stream */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] closing QUIC stream", s->id); + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] closing QUIC stream", s->id); SSL_stream_conclude(s->ssl, 0); } } @@ -1603,12 +1685,6 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, now = Curl_now(); CF_DATA_SAVE(save, cf, data); - if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { - /* Not time yet to attempt the next connect */ - CURL_TRC_CF(data, cf, "waiting for reconnect time"); - goto out; - } - if(!ctx->tls.ossl.ssl) { ctx->started_at = now; result = cf_osslq_ctx_start(cf, data); @@ -1766,7 +1842,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, &ctx->stream_bufcp, data); if(*err) { - failf(data, "can't get bidi streams"); + failf(data, "cannot get bidi streams"); *err = CURLE_SEND_ERROR; goto out; } @@ -1800,11 +1876,11 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64"] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64"] failed to send, " "connection is closing", stream->s.id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64 "] failed to send -> %d (%s)", + CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64 "] failed to send -> %d (%s)", stream->s.id, rc, nghttp3_strerror(rc)); break; } @@ -1814,10 +1890,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, } if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", stream->s.id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", + infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->s.id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); @@ -1831,7 +1907,8 @@ out: } static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1839,6 +1916,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, ssize_t nwritten; CURLcode result; + (void)eos; /* TODO: use to end stream */ CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->tls.ossl.ssl); @@ -1867,21 +1945,6 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, } stream = H3_STREAM_CTX(ctx, data); } - else if(stream->upload_blocked_len) { - /* the data in `buf` has already been submitted or added to the - * buffers, but have been EAGAINed on the last invocation. */ - DEBUGASSERT(len >= stream->upload_blocked_len); - if(len < stream->upload_blocked_len) { - /* Did we get called again with a smaller `len`? This should not - * happen. We are not prepared to handle that. */ - failf(data, "HTTP/3 send again with decreased length"); - *err = CURLE_HTTP3; - nwritten = -1; - goto out; - } - nwritten = (ssize_t)stream->upload_blocked_len; - stream->upload_blocked_len = 0; - } else if(stream->closed) { if(stream->resp_hds_complete) { /* Server decided to close the stream after having sent us a final @@ -1889,13 +1952,13 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, * body. This happens on 30x or 40x responses. * We silently discard the data sent, since this is not a transport * error situation. */ - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->s.id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->s.id, len); *err = CURLE_HTTP3; nwritten = -1; @@ -1903,7 +1966,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %zd, %d", stream->s.id, len, nwritten, *err); if(nwritten < 0) { @@ -1919,21 +1982,9 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, nwritten = -1; } - if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) { - /* We have unacknowledged DATA and cannot report success to our - * caller. Instead we EAGAIN and remember how much we have already - * "written" into our various internal connection buffers. */ - stream->upload_blocked_len = nwritten; - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), " - "%zu bytes in flight -> EGAIN", stream->s.id, len, - stream->sendbuf_len_in_flight); - *err = CURLE_AGAIN; - nwritten = -1; - } - out: result = check_and_set_expiry(cf, data); - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nwritten, *err); CF_DATA_RESTORE(cf, save); return nwritten; @@ -1949,14 +2000,14 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, (void)cf; if(stream->reset) { failf(data, - "HTTP/3 stream %" CURL_PRId64 " reset by server", + "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->s.id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { failf(data, - "HTTP/3 stream %" CURL_PRId64 + "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before getting" " all response header fields, treated as error", stream->s.id); @@ -1996,7 +2047,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -2014,7 +2065,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); if(nread < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->s.id, len, nread, *err); goto out; } @@ -2044,7 +2095,7 @@ out: nread = -1; } } - CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(len=%zu) -> %zd, %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %zd, %d", stream? stream->s.id : -1, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; @@ -2090,7 +2141,8 @@ static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream && !stream->send_closed) { stream->send_closed = TRUE; - stream->upload_left = Curl_bufq_len(&stream->sendbuf); + stream->upload_left = Curl_bufq_len(&stream->sendbuf) - + stream->sendbuf_len_in_flight; (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); } break; @@ -2149,8 +2201,8 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, alive = TRUE; if(*input_pending) { CURLcode result; - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, only "protocol frames" */ *input_pending = FALSE; result = cf_progress_ingress(cf, data); @@ -2189,6 +2241,10 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, SSL_net_read_desired(ctx->tls.ossl.ssl), SSL_net_write_desired(ctx->tls.ossl.ssl)); } + else if(ctx->need_recv || ctx->need_send) { + Curl_pollset_set(data, ps, ctx->q.sockfd, + ctx->need_recv, ctx->need_send); + } } } @@ -2252,6 +2308,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_osslq_destroy, cf_osslq_connect, cf_osslq_close, + cf_osslq_shutdown, Curl_cf_def_get_host, cf_osslq_adjust_pollset, cf_osslq_data_pending, @@ -2278,7 +2335,7 @@ CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_osslq_ctx_clear(ctx); + cf_osslq_ctx_init(ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) @@ -2299,7 +2356,7 @@ out: if(udp_cf) Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_safefree(cf); - Curl_safefree(ctx); + cf_osslq_ctx_free(ctx); } return result; } diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index a68fc6430..768a5f2ae 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -64,11 +64,10 @@ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows - * seems good. More does not seem to improve performance. - * The benefit of the pool is that stream buffer to not keep - * spares. So memory consumption goes down when streams run empty, - * have a large upload done, etc. */ +/* The pool keeps spares around and half of a full stream windows seems good. + * More does not seem to improve performance. The benefit of the pool is that + * stream buffer to not keep spares. Memory consumption goes down when streams + * run empty, have a large upload done, etc. */ #define H3_STREAM_POOL_SPARES \ (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 /* Receive and Send max number of chunks just follows from the @@ -97,15 +96,18 @@ struct cf_quiche_ctx { uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; struct curltime started_at; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ - struct curltime reconnect_at; /* time the next attempt should start */ struct bufc_pool stream_bufcp; /* chunk pool for streams */ - struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */ + struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */ curl_off_t data_recvd; + BIT(initialized); BIT(goaway); /* got GOAWAY from server */ BIT(x509_store_setup); /* if x509 store has been set up */ + BIT(shutdown_started); /* queued shutdown packets */ }; #ifdef DEBUG_QUICHE +/* initialize debug log callback only once */ +static int debug_log_init = 0; static void quiche_debug_log(const char *line, void *argp) { (void)argp; @@ -113,17 +115,27 @@ static void quiche_debug_log(const char *line, void *argp) } #endif -static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) +static void h3_stream_hash_free(void *stream); + +static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) +{ + DEBUGASSERT(!ctx->initialized); +#ifdef DEBUG_QUICHE + if(!debug_log_init) { + quiche_enable_debug_logging(quiche_debug_log, NULL); + debug_log_init = 1; + } +#endif + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->data_recvd = 0; + ctx->initialized = TRUE; +} + +static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) { - if(ctx) { - if(ctx->h3c) - quiche_h3_conn_free(ctx->h3c); - if(ctx->h3config) - quiche_h3_config_free(ctx->h3config); - if(ctx->qconn) - quiche_conn_free(ctx->qconn); - if(ctx->cfg) - quiche_config_free(ctx->cfg); + if(ctx && ctx->initialized) { /* quiche just freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); @@ -132,9 +144,20 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) Curl_bufcp_free(&ctx->stream_bufcp); Curl_hash_clean(&ctx->streams); Curl_hash_destroy(&ctx->streams); - - memset(ctx, 0, sizeof(*ctx)); } + free(ctx); +} + +static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) +{ + if(ctx->h3c) + quiche_h3_conn_free(ctx->h3c); + if(ctx->h3config) + quiche_h3_config_free(ctx->h3config); + if(ctx->qconn) + quiche_conn_free(ctx->qconn); + if(ctx->cfg) + quiche_config_free(ctx->cfg); } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, @@ -148,7 +171,6 @@ struct stream_ctx { struct bufq recvbuf; /* h3 response */ struct h1_req_parser h1; /* h1 request parsing */ curl_uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ BIT(opened); /* TRUE after stream has been opened */ BIT(closed); /* TRUE on stream close */ BIT(reset); /* TRUE on stream reset */ @@ -159,7 +181,7 @@ struct stream_ctx { }; #define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\ - data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL)) + data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL)) static void h3_stream_ctx_free(struct stream_ctx *stream) { @@ -178,17 +200,17 @@ static void check_resumes(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; - struct stream_ctx *stream; + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn == data->conn) { - stream = H3_STREAM_CTX(ctx, sdata); + struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata); if(stream && stream->quic_flow_blocked) { stream->quic_flow_blocked = FALSE; Curl_expire(data, 0, EXPIRE_RUN_NOW); - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id); } } } @@ -212,7 +234,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) { + if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -228,7 +250,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] easy handle is done", stream->id); if(ctx->qconn && !stream->closed) { quiche_conn_stream_shutdown(ctx->qconn, stream->id, QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); @@ -242,7 +264,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) if(result) CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result); } - Curl_hash_offt_remove(&ctx->streams, data->id); + Curl_hash_offt_remove(&ctx->streams, data->mid); } } @@ -255,7 +277,7 @@ static void drain_stream(struct Curl_cfilter *cf, (void)cf; bits = CURL_CSELECT_IN; - if(stream && !stream->send_closed && stream->upload_left) + if(stream && !stream->send_closed) bits |= CURL_CSELECT_OUT; if(data->state.select_bits != bits) { data->state.select_bits = bits; @@ -269,7 +291,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, struct stream_ctx **pstream) { struct cf_quiche_ctx *ctx = cf->ctx; - struct Curl_easy *sdata; struct stream_ctx *stream; (void)cf; @@ -279,8 +300,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, return data; } else { + struct Curl_llist_node *e; DEBUGASSERT(data->multi); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata->conn != data->conn) continue; stream = H3_STREAM_CTX(ctx, sdata); @@ -297,11 +320,12 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_easy *sdata; + struct Curl_llist_node *e; DEBUGASSERT(data->multi); CURL_TRC_CF(data, cf, "conn closed, expire all transfers"); - for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { + for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) { + struct Curl_easy *sdata = Curl_node_elem(e); if(sdata == data || sdata->conn != data->conn) continue; CURL_TRC_CF(sdata, cf, "conn closed, expire transfer"); @@ -357,7 +381,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, return CURLE_OK; if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] status: %.*s", stream->id, (int)value_len, value); result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); if(!result) @@ -366,7 +390,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, " \r\n", 3); } else { - CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s", + CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] header: %.*s: %.*s", stream->id, (int)name_len, name, (int)value_len, value); result = write_resp_raw(x->cf, x->data, name, name_len); @@ -378,7 +402,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, "\r\n", 2); } if(result) { - CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d", + CURL_TRC_CF(x->data, x->cf, "[%"FMT_PRIu64"] on header error %d", stream->id, result); } return result; @@ -435,9 +459,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result); if(nwritten < 0 && result != CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zd", stream->id, nwritten); - failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]", + failf(data, "Error %d in HTTP/3 response body for stream[%"FMT_PRIu64"]", result, stream->id); stream->closed = TRUE; stream->reset = TRUE; @@ -489,10 +513,10 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); if(rc) { failf(data, "Error %d in HTTP/3 response header for stream[%" - CURL_PRIu64"]", rc, stream->id); + FMT_PRIu64"]", rc, stream->id); return CURLE_RECV_ERROR; } - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [HEADERS]", stream->id); break; case QUICHE_H3_EVENT_DATA: @@ -502,7 +526,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_RESET: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] RESET", stream->id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -510,7 +534,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_FINISHED: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] CLOSED", stream->id); if(!stream->resp_hds_complete) { result = write_resp_raw(cf, data, "\r\n", 2); if(result) @@ -522,11 +546,11 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_GOAWAY: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [GOAWAY]", stream->id); break; default: - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv, unhandled event %d", stream->id, quiche_h3_event_type(ev)); break; } @@ -549,13 +573,13 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, break; } else if(stream3_id < 0) { - CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id); + CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id); return CURLE_HTTP3; } sdata = get_stream_easy(cf, data, stream3_id, &stream); if(!sdata || !stream) { - CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]", + CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]", cf_ev_name(ev), stream3_id); } else { @@ -563,7 +587,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, drain_stream(cf, sdata); if(result) { CURL_TRC_CF(data, cf, "error processing event %s " - "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev), + "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev), stream3_id, result); if(data == sdata) { /* Only report this error to the caller if it is about the @@ -793,19 +817,19 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); if(stream->reset) { failf(data, - "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id); + "HTTP/3 stream %" FMT_PRIu64 " reset by server", stream->id); *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d", + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, was reset -> %d", stream->id, *err); } else if(!stream->resp_got_header) { failf(data, - "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before " + "HTTP/3 stream %" FMT_PRIu64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); /* *err = CURLE_PARTIAL_FILE; */ *err = CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete" + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, closed incomplete" " -> %d", stream->id, *err); } else { @@ -833,7 +857,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -850,7 +874,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; @@ -884,19 +908,70 @@ out: } if(nread > 0) ctx->data_recvd += nread; - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%" - CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] cf_recv(total=%" + FMT_OFF_T ") -> %zd, %d", stream->id, ctx->data_recvd, nread, *err); return nread; } +static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct stream_ctx *stream, + const void *buf, size_t len, bool eos, + CURLcode *err) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + ssize_t nwritten; + + nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, + (uint8_t *)buf, len, eos); + if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { + /* TODO: we seem to be blocked on flow control and should HOLD + * sending. But when do we open again? */ + if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> window exhausted", stream->id, len); + stream->quic_flow_blocked = TRUE; + } + *err = CURLE_AGAIN; + return -1; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> invalid stream state", stream->id, len); + *err = CURLE_HTTP3; + return -1; + } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> exceeds size", stream->id, len); + *err = CURLE_SEND_ERROR; + return -1; + } + else if(nwritten < 0) { + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + "-> quiche err %zd", stream->id, len, nwritten); + *err = CURLE_SEND_ERROR; + return -1; + } + else { + if(eos && (len == (size_t)nwritten)) + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send body(len=%zu, " + "eos=%d) -> %zd", + stream->id, len, stream->send_closed, nwritten); + *err = CURLE_OK; + return nwritten; + } +} + /* Index where :authority header field will appear in request header field list. */ #define AUTHORITY_DST_IDX 3 static ssize_t h3_open_stream(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, + const char *buf, size_t len, bool eos, CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; @@ -952,23 +1027,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, nva[i].value_len = e->valuelen; } - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown */ - break; - default: - stream->upload_left = 0; /* no request body */ - break; - } - - if(stream->upload_left == 0) + if(eos && ((size_t)nwritten == len)) stream->send_closed = TRUE; stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, @@ -977,14 +1036,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] blocked", stream->id); stream->quic_flow_blocked = TRUE; *err = CURLE_AGAIN; nwritten = -1; goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64, + CURL_TRC_CF(data, cf, "send_request(%s) -> %" FMT_PRIu64, data->state.url, stream3_id); } *err = CURLE_SEND_ERROR; @@ -1000,15 +1059,31 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, stream->reset = FALSE; if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s", + infof(data, "[HTTP/3] [%" FMT_PRIu64 "] OPENED stream for %s", stream->id, data->state.url); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" FMT_PRIu64 "] [%.*s: %.*s]", stream->id, (int)nva[i].name_len, nva[i].name, (int)nva[i].value_len, nva[i].value); } } + if(nwritten > 0 && ((size_t)nwritten < len)) { + /* after the headers, there was request BODY data */ + size_t hds_len = (size_t)nwritten; + ssize_t bwritten; + + bwritten = cf_quiche_send_body(cf, data, stream, + buf + hds_len, len - hds_len, eos, err); + if((bwritten < 0) && (CURLE_AGAIN != *err)) { + /* real error, fail */ + nwritten = -1; + } + else if(bwritten > 0) { + nwritten += bwritten; + } + } + out: free(nva); Curl_dynhds_free(&h2_headers); @@ -1016,7 +1091,8 @@ out: } static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, bool eos, + CURLcode *err) { struct cf_quiche_ctx *ctx = cf->ctx; struct stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1032,7 +1108,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, } if(!stream || !stream->opened) { - nwritten = h3_open_stream(cf, data, buf, len, err); + nwritten = h3_open_stream(cf, data, buf, len, eos, err); if(nwritten < 0) goto out; stream = H3_STREAM_CTX(ctx, data); @@ -1047,70 +1123,20 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * sending the 30x response. * This is sort of a race: had the transfer loop called recv first, * it would see the response and stop/discard sending on its own- */ - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] discarding data" "on closed stream with response", stream->id); *err = CURLE_OK; nwritten = (ssize_t)len; goto out; } - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); *err = CURLE_HTTP3; nwritten = -1; goto out; } else { - bool eof = (stream->upload_left >= 0 && - (curl_off_t)len >= stream->upload_left); - nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, - (uint8_t *)buf, len, eof); - if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { - /* TODO: we seem to be blocked on flow control and should HOLD - * sending. But when do we open again? */ - if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> window exhausted", stream->id, len); - stream->quic_flow_blocked = TRUE; - } - *err = CURLE_AGAIN; - nwritten = -1; - goto out; - } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> invalid stream state", stream->id, len); - *err = CURLE_HTTP3; - nwritten = -1; - goto out; - } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else if(nwritten < 0) { - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - else { - /* quiche accepted all or at least a part of the buf */ - if(stream->upload_left > 0) { - stream->upload_left = (nwritten < stream->upload_left)? - (stream->upload_left - nwritten) : 0; - } - if(stream->upload_left == 0) - stream->send_closed = TRUE; - - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, " - "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", - stream->id, len, stream->upload_left, nwritten); - *err = CURLE_OK; - } + nwritten = cf_quiche_send_body(cf, data, stream, buf, len, eos, err); } out: @@ -1119,8 +1145,8 @@ out: *err = result; nwritten = -1; } - CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nwritten, *err); + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : (curl_uint64_t)~0, len, nwritten, *err); return nwritten; } @@ -1215,10 +1241,9 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, ssize_t sent; stream->send_closed = TRUE; - stream->upload_left = 0; body[0] = 'X'; - sent = cf_quiche_send(cf, data, body, 0, &result); - CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d", + sent = cf_quiche_send(cf, data, body, 0, TRUE, &result); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %zd, %d", stream->id, sent, result); } break; @@ -1238,8 +1263,8 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, return result; } -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; int rv; @@ -1247,19 +1272,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, const struct Curl_sockaddr_ex *sockaddr; DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); - -#ifdef DEBUG_QUICHE - /* initialize debug log callback only once */ - static int debug_log_init = 0; - if(!debug_log_init) { - quiche_enable_debug_logging(quiche_debug_log, NULL); - debug_log_init = 1; - } -#endif - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->data_recvd = 0; + DEBUGASSERT(ctx->initialized); result = vquic_ctx_init(&ctx->q); if(result) @@ -1271,7 +1284,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); if(!ctx->cfg) { - failf(data, "can't create quiche config"); + failf(data, "cannot create quiche config"); return CURLE_FAILED_INIT; } quiche_config_enable_pacing(ctx->cfg, false); @@ -1322,7 +1335,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, &sockaddr->sa_addr, sockaddr->addrlen, ctx->cfg, ctx->tls.ossl.ssl, false); if(!ctx->qconn) { - failf(data, "can't create quiche connection"); + failf(data, "cannot create quiche connection"); return CURLE_OUT_OF_MEMORY; } @@ -1366,7 +1379,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->httpversion = 30; - cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1393,15 +1405,8 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, *done = FALSE; vquic_ctx_update_time(&ctx->q); - if(ctx->reconnect_at.tv_sec && - Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) { - /* Not time yet to attempt the next connect */ - CURL_TRC_CF(data, cf, "waiting for reconnect time"); - goto out; - } - if(!ctx->qconn) { - result = cf_connect_start(cf, data); + result = cf_quiche_ctx_open(cf, data); if(result) goto out; ctx->started_at = ctx->q.last_op; @@ -1464,30 +1469,69 @@ out: return result; } -static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->shutdown || !ctx || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; + } - if(ctx) { - if(ctx->qconn) { - vquic_ctx_update_time(&ctx->q); - (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); - /* flushing the egress is not a failsafe way to deliver all the - outstanding packets, but we also don't want to get stuck here... */ - (void)cf_flush_egress(cf, data); + *done = FALSE; + if(!ctx->shutdown_started) { + int err; + + ctx->shutdown_started = TRUE; + vquic_ctx_update_time(&ctx->q); + err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); + if(err) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packet, " + "aborting shutdown", err); + result = CURLE_SEND_ERROR; + goto out; } - cf_quiche_ctx_clear(ctx); } + + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = cf_flush_egress(cf, data); + if(result) + goto out; + } + + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* sent everything, quiche does not seem to support a graceful + * shutdown waiting for a reply, so ware done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + } + else { + CURL_TRC_CF(data, cf, "shutdown sending blocked"); + } + +out: + return result; } -static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_quiche_ctx *ctx = cf->ctx; + if(cf->ctx) { + bool done; + (void)cf_quiche_shutdown(cf, data, &done); + cf_quiche_ctx_close(cf->ctx); + } +} +static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ (void)data; - cf_quiche_ctx_clear(ctx); - free(ctx); - cf->ctx = NULL; + if(cf->ctx) { + cf_quiche_ctx_free(cf->ctx); + cf->ctx = NULL; + } } static CURLcode cf_quiche_query(struct Curl_cfilter *cf, @@ -1503,7 +1547,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; - CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: " + CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%zu in use)", cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn)); return CURLE_OK; @@ -1559,8 +1603,8 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, return FALSE; if(*input_pending) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, + /* This happens before we have sent off a request and the connection is + not in use by any other transfer, there should not be any data here, only "protocol frames" */ *input_pending = FALSE; if(cf_process_ingress(cf, data)) @@ -1580,6 +1624,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_destroy, cf_quiche_connect, cf_quiche_close, + cf_quiche_shutdown, Curl_cf_def_get_host, cf_quiche_adjust_pollset, cf_quiche_data_pending, @@ -1607,6 +1652,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + cf_quiche_ctx_init(ctx); result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) @@ -1626,7 +1672,7 @@ out: if(udp_cf) Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); Curl_safefree(cf); - Curl_safefree(ctx); + cf_quiche_ctx_free(ctx); } return result; diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c index aca18b457..cc8c22d77 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.c +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c @@ -76,7 +76,7 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line) } #endif -static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, +static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, Curl_vquic_tls_ctx_setup *cb_setup, @@ -91,8 +91,8 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - if(!ctx->ssl_ctx) { + ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if(!ctx->wssl.ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -103,9 +103,9 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx); + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); - if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ? + if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ? conn_config->cipher_list13 : QUIC_CIPHERS) != 1) { char error_buffer[256]; @@ -115,7 +115,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out; } - if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ? + if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ? conn_config->curves : (char *)QUIC_GROUPS) != 1) { failf(data, "wolfSSL failed to set curves"); @@ -127,7 +127,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, Curl_tls_keylog_open(); if(Curl_tls_keylog_enabled()) { #if defined(HAVE_SECRET_CALLBACK) - wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); + wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback); #else failf(data, "wolfSSL was built without keylog callback"); result = CURLE_NOT_BUILT_IN; @@ -139,12 +139,12 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, const char * const ssl_cafile = conn_config->CAfile; const char * const ssl_capath = conn_config->CApath; - wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL); if(ssl_cafile || ssl_capath) { /* tell wolfSSL where to find CA certificates that are used to verify the server's certificate. */ int rc = - wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile, + wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile, ssl_capath, WOLFSSL_LOAD_FLAG_IGNORE_ERR); if(SSL_SUCCESS != rc) { @@ -161,20 +161,20 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, } #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(ctx->ssl_ctx); + /* verifying the peer without any CA certificates will not work so + use wolfSSL's built-in default as fallback */ + wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx); } #endif } else { - wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL); + wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL); } /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx, + result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, false); if(result) { @@ -185,36 +185,36 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, result = CURLE_OK; out: - if(result && ctx->ssl_ctx) { - SSL_CTX_free(ctx->ssl_ctx); - ctx->ssl_ctx = NULL; + if(result && ctx->wssl.ctx) { + SSL_CTX_free(ctx->wssl.ctx); + ctx->wssl.ctx = NULL; } return result; } /** SSL callbacks ***/ -static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx, +static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx, struct Curl_easy *data, struct ssl_peer *peer, const char *alpn, size_t alpn_len, void *user_data) { (void)data; - DEBUGASSERT(!ctx->ssl); - DEBUGASSERT(ctx->ssl_ctx); - ctx->ssl = wolfSSL_new(ctx->ssl_ctx); + DEBUGASSERT(!ctx->wssl.handle); + DEBUGASSERT(ctx->wssl.ctx); + ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx); - wolfSSL_set_app_data(ctx->ssl, user_data); - wolfSSL_set_connect_state(ctx->ssl); - wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); + wolfSSL_set_app_data(ctx->wssl.handle, user_data); + wolfSSL_set_connect_state(ctx->wssl.handle); + wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0); if(alpn) - wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn, - (int)alpn_len); + wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn, + (unsigned int)alpn_len); if(peer->sni) { - wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME, + wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME, peer->sni, (unsigned short)strlen(peer->sni)); } @@ -243,11 +243,11 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, (const unsigned char *)alpn, alpn_len, cb_setup, cb_user_data, ssl_user_data); #elif defined(USE_WOLFSSL) - result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); + result = Curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; - return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); + return Curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; @@ -262,15 +262,14 @@ void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) if(ctx->ossl.ssl_ctx) SSL_CTX_free(ctx->ossl.ssl_ctx); #elif defined(USE_GNUTLS) - if(ctx->gtls.cred) - gnutls_certificate_free_credentials(ctx->gtls.cred); if(ctx->gtls.session) gnutls_deinit(ctx->gtls.session); + Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds); #elif defined(USE_WOLFSSL) - if(ctx->ssl) - wolfSSL_free(ctx->ssl); - if(ctx->ssl_ctx) - wolfSSL_CTX_free(ctx->ssl_ctx); + if(ctx->wssl.handle) + wolfSSL_free(ctx->wssl.handle); + if(ctx->wssl.ctx) + wolfSSL_CTX_free(ctx->wssl.ctx); #endif memset(ctx, 0, sizeof(*ctx)); } @@ -286,8 +285,14 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, return result; ctx->ossl.x509_store_setup = TRUE; } +#elif defined(USE_WOLFSSL) + if(!ctx->wssl.x509_store_setup) { + CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl); + if(result) + return result; + } #elif defined(USE_GNUTLS) - if(!ctx->gtls.trust_setup) { + if(!ctx->gtls.shared_creds->trust_setup) { CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls); if(result) return result; @@ -325,7 +330,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)data; if(conn_config->verifyhost) { if(peer->sni) { - WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->ssl); + WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle); if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL) == WOLFSSL_FAILURE) { result = CURLE_PEER_FAILED_VERIFICATION; diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h index 1d35fd0e6..0ec74bfba 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.h +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h @@ -31,14 +31,15 @@ #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) +#include "vtls/wolfssl.h" + struct curl_tls_ctx { #ifdef USE_OPENSSL struct ossl_ctx ossl; #elif defined(USE_GNUTLS) struct gtls_ctx gtls; #elif defined(USE_WOLFSSL) - WOLFSSL_CTX *ssl_ctx; - WOLFSSL *ssl; + struct wolfssl_ctx wssl; #endif }; diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 9ce1e4626..4648b5a07 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -22,7 +22,7 @@ * ***************************************************************************/ -/* WIP, experimental: use recvmmsg() on linux +/* WIP, experimental: use recvmmsg() on Linux * we have no configure check, yet * and also it is only available for _GNU_SOURCE, which * we do not use otherwise. @@ -36,6 +36,9 @@ #include "curl_setup.h" +#ifdef HAVE_NETINET_UDP_H +#include +#endif #ifdef HAVE_FCNTL_H #include #endif @@ -329,6 +332,36 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, return vquic_flush(cf, data, qctx); } +#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) +static size_t msghdr_get_udp_gro(struct msghdr *msg) +{ + int gso_size = 0; +#if defined(__linux__) && defined(UDP_GRO) + struct cmsghdr *cmsg; + + /* Workaround musl CMSG_NXTHDR issue */ +#ifndef __GLIBC__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wcast-align" +#endif + for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { +#ifndef __GLIBC__ +#pragma clang diagnostic pop +#endif + if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) { + memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size)); + + break; + } + } +#endif + (void)msg; + + return (size_t)gso_size; +} +#endif + #ifdef HAVE_SENDMMSG static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -339,12 +372,16 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, #define MMSG_NUM 64 struct iovec msg_iov[MMSG_NUM]; struct mmsghdr mmsg[MMSG_NUM]; + uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(uint16_t))]; uint8_t bufs[MMSG_NUM][2*1024]; struct sockaddr_storage remote_addr[MMSG_NUM]; size_t total_nread, pkts; int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; + size_t gso_size; + size_t pktlen; + size_t offset, to; DEBUGASSERT(max_pkts > 0); pkts = 0; @@ -359,6 +396,8 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); + mmsg[i].msg_hdr.msg_control = &msg_ctrl[i]; + mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); } while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && @@ -385,14 +424,30 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); - pkts += mcount; for(i = 0; i < mcount; ++i) { total_nread += mmsg[i].msg_len; - result = recv_cb(bufs[i], mmsg[i].msg_len, - mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen, - 0, userp); - if(result) - goto out; + + gso_size = msghdr_get_udp_gro(&mmsg[i].msg_hdr); + if(gso_size == 0) { + gso_size = mmsg[i].msg_len; + } + + for(offset = 0; offset < mmsg[i].msg_len; offset = to) { + ++pkts; + + to = offset + gso_size; + if(to > mmsg[i].msg_len) { + pktlen = mmsg[i].msg_len - offset; + } + else { + pktlen = gso_size; + } + + result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name, + mmsg[i].msg_hdr.msg_namelen, 0, userp); + if(result) + goto out; + } } } @@ -418,6 +473,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ssize_t nread; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; + uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))]; + size_t gso_size; + size_t pktlen; + size_t offset, to; msg_iov.iov_base = buf; msg_iov.iov_len = (int)sizeof(buf); @@ -425,11 +484,13 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, memset(&msg, 0, sizeof(msg)); msg.msg_iov = &msg_iov; msg.msg_iovlen = 1; + msg.msg_control = msg_ctrl; DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0; pkts < max_pkts;) { msg.msg_name = &remote_addr; msg.msg_namelen = sizeof(remote_addr); + msg.msg_controllen = sizeof(msg_ctrl); while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) ; @@ -452,12 +513,29 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, goto out; } - ++pkts; total_nread += (size_t)nread; - result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen, - 0, userp); - if(result) - goto out; + + gso_size = msghdr_get_udp_gro(&msg); + if(gso_size == 0) { + gso_size = (size_t)nread; + } + + for(offset = 0; offset < (size_t)nread; offset = to) { + ++pkts; + + to = offset + gso_size; + if(to > (size_t)nread) { + pktlen = (size_t)nread - offset; + } + else { + pktlen = gso_size; + } + + result = + recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp); + if(result) + goto out; + } } out: @@ -642,7 +720,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn) { if(conn->transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a unix domain socket */ + /* cannot do QUIC over a Unix domain socket */ return CURLE_QUIC_CONNECT_ERROR; } if(!(conn->handler->flags & PROTOPT_SSL)) { @@ -655,7 +733,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, return CURLE_URL_MALFORMAT; } if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over a HTTP proxy"); + failf(data, "HTTP/3 is not supported over an HTTP proxy"); return CURLE_URL_MALFORMAT; } #endif diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index d6ba987f1..230fddcef 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -31,11 +31,6 @@ #include -/* in 0.10.0 or later, ignore deprecated warnings */ -#define SSH_SUPPRESS_DEPRECATED -#include -#include - #ifdef HAVE_NETINET_IN_H #include #endif @@ -388,28 +383,25 @@ static int myssh_is_known(struct Curl_easy *data) goto cleanup; } - if(data->set.ssl.primary.verifyhost != TRUE) { - rc = SSH_OK; - goto cleanup; - } + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) - /* Get the known_key from the known hosts file */ - vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, - &knownhostsentry); - - /* Case an entry was found in a known hosts file */ - if(knownhostsentry) { - if(knownhostsentry->publickey) { - rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, - &known_base64); - if(rc != SSH_OK) { - goto cleanup; - } - knownkey.key = known_base64; - knownkey.len = strlen(known_base64); + /* Get the known_key from the known hosts file */ + vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, + &knownhostsentry); + + /* Case an entry was found in a known hosts file */ + if(knownhostsentry) { + if(knownhostsentry->publickey) { + rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, + &known_base64); + if(rc != SSH_OK) { + goto cleanup; + } + knownkey.key = known_base64; + knownkey.len = strlen(known_base64); - switch(ssh_key_type(knownhostsentry->publickey)) { + switch(ssh_key_type(knownhostsentry->publickey)) { case SSH_KEYTYPE_RSA: knownkey.keytype = CURLKHTYPE_RSA; break; @@ -431,12 +423,12 @@ static int myssh_is_known(struct Curl_easy *data) default: rc = SSH_ERROR; goto cleanup; + } + knownkeyp = &knownkey; } - knownkeyp = &knownkey; } - } - switch(vstate) { + switch(vstate) { case SSH_KNOWN_HOSTS_OK: keymatch = CURLKHMATCH_OK; break; @@ -446,14 +438,14 @@ static int myssh_is_known(struct Curl_easy *data) case SSH_KNOWN_HOSTS_ERROR: keymatch = CURLKHMATCH_MISSING; break; - default: + default: keymatch = CURLKHMATCH_MISMATCH; break; - } + } #else - vstate = ssh_is_server_known(sshc->ssh_session); - switch(vstate) { + vstate = ssh_is_server_known(sshc->ssh_session); + switch(vstate) { case SSH_SERVER_KNOWN_OK: keymatch = CURLKHMATCH_OK; break; @@ -461,21 +453,21 @@ static int myssh_is_known(struct Curl_easy *data) case SSH_SERVER_NOT_KNOWN: keymatch = CURLKHMATCH_MISSING; break; - default: + default: keymatch = CURLKHMATCH_MISMATCH; break; - } + } #endif - if(func) { /* use callback to determine action */ - rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); - if(rc != SSH_OK) - goto cleanup; + if(func) { /* use callback to determine action */ + rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); + if(rc != SSH_OK) + goto cleanup; - foundkey.key = found_base64; - foundkey.len = strlen(found_base64); + foundkey.key = found_base64; + foundkey.len = strlen(found_base64); - switch(ssh_key_type(pubkey)) { + switch(ssh_key_type(pubkey)) { case SSH_KEYTYPE_RSA: foundkey.keytype = CURLKHTYPE_RSA; break; @@ -501,15 +493,15 @@ static int myssh_is_known(struct Curl_easy *data) default: rc = SSH_ERROR; goto cleanup; - } + } - Curl_set_in_callback(data, true); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, true); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, false); - switch(rc) { + switch(rc) { case CURLKHSTAT_FINE_ADD_TO_FILE: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) rc = ssh_session_update_known_hosts(sshc->ssh_session); @@ -525,12 +517,13 @@ static int myssh_is_known(struct Curl_easy *data) default: /* REJECT/DEFER */ rc = SSH_ERROR; goto cleanup; + } } - } - else { - if(keymatch != CURLKHMATCH_OK) { - rc = SSH_ERROR; - goto cleanup; + else { + if(keymatch != CURLKHMATCH_OK) { + rc = SSH_ERROR; + goto cleanup; + } } } rc = SSH_OK; @@ -663,7 +656,7 @@ restart: /* * ssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points + * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the libssh function returns SSH_AGAIN * meaning it wants to be called again when the socket is ready */ @@ -677,7 +670,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) int rc = SSH_NO_ERROR, err; int seekerr = CURL_SEEKFUNC_OK; const char *err_msg; - *block = 0; /* we're not blocking by default */ + *block = 0; /* we are not blocking by default */ do { @@ -742,7 +735,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) break; } - sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL); + sshc->auth_methods = + (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL); if(sshc->auth_methods) infof(data, "SSH authentication methods available: %s%s%s%s", sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? @@ -1242,7 +1236,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) if(attrs) { curl_off_t size = attrs->size; if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + failf(data, "Bad file size (%" FMT_OFF_T ")", size); MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); break; } @@ -1308,7 +1302,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) failf(data, "Could not seek stream"); return CURLE_FTP_COULDNT_USE_REST; } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { char scratch[4*1024]; size_t readthisamountnow = @@ -1351,12 +1345,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; - /* store this original bitmask setup to use later on if we can't + /* store this original bitmask setup to use later on if we cannot figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; @@ -1365,7 +1359,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) with both accordingly */ data->state.select_bits = CURL_CSELECT_OUT; - /* since we don't really wait for anything at this point, we want the + /* since we do not really wait for anything at this point, we want the state machine to move on as soon as possible so we set a very short timeout here */ Curl_expire(data, 0, EXPIRE_RUN_NOW); @@ -1404,7 +1398,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) ++sshc->slash_pos; if(rc < 0) { /* - * Abort if failure wasn't that the dir already exists or the + * Abort if failure was not that the dir already exists or the * permission was denied (creation might succeed further down the * path) - retry on unspecific FAILURE also */ @@ -1577,7 +1571,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) sshc->sftp_dir = NULL; /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); state(data, SSH_STOP); break; @@ -1611,9 +1605,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) || (attrs->size == 0)) { /* - * sftp_fstat didn't return an error, so maybe the server - * just doesn't support stat() - * OR the server doesn't return a file size with a stat() + * sftp_fstat did not return an error, so maybe the server + * just does not support stat() + * OR the server does not return a file size with a stat() * OR file size is 0 */ data->req.size = -1; @@ -1627,7 +1621,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) sftp_attributes_free(attrs); if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + failf(data, "Bad file size (%" FMT_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } if(data->state.use_range) { @@ -1657,9 +1651,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) to = size - 1; } if(from > size) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, size); + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", from, size); return CURLE_BAD_DOWNLOAD_RESUME; } if(from > to) { @@ -1686,12 +1679,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) /* We can resume if we can seek to the resume position */ if(data->state.resume_from) { if(data->state.resume_from < 0) { - /* We're supposed to download the last abs(from) bytes */ + /* We are supposed to download the last abs(from) bytes */ if((curl_off_t)size < -data->state.resume_from) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, size); + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", data->state.resume_from, size); return CURLE_BAD_DOWNLOAD_RESUME; } /* download from where? */ @@ -1699,8 +1690,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } else { if((curl_off_t)size < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -1722,12 +1713,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; @@ -1851,12 +1842,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) } /* upload data */ - Curl_xfer_setup(data, -1, data->req.size, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; - /* store this original bitmask setup to use later on if we can't + /* store this original bitmask setup to use later on if we cannot figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; @@ -1895,7 +1886,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) /* download data */ bytecount = ssh_scp_request_get_size(sshc->scp_session); data->req.maxdownload = (curl_off_t) bytecount; - Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; @@ -1946,7 +1937,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) FALLTHROUGH(); case SSH_SESSION_DISCONNECT: - /* during weird times when we've been prematurely aborted, the channel + /* during weird times when we have been prematurely aborted, the channel is still alive when we reach this state and we MUST kill the channel properly first */ if(sshc->scp_session) { @@ -2063,7 +2054,7 @@ static void myssh_block2waitfor(struct connectdata *conn, bool block) { struct ssh_conn *sshc = &conn->proto.sshc; - /* If it didn't block, or nothing was returned by ssh_get_poll_flags + /* If it did not block, or nothing was returned by ssh_get_poll_flags * have the original set */ conn->waitfor = sshc->orig_waitfor; @@ -2358,7 +2349,7 @@ static CURLcode scp_disconnect(struct Curl_easy *data, (void) dead_connection; if(ssh->ssh_session) { - /* only if there's a session still around to use! */ + /* only if there is a session still around to use! */ state(data, SSH_SESSION_DISCONNECT); @@ -2405,12 +2396,13 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, } static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, CURLcode *err) { int rc; struct connectdata *conn = data->conn; (void) sockindex; /* we only support SCP on the fixed known primary socket */ (void) err; + (void)eos; rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len); @@ -2523,7 +2515,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, DEBUGF(infof(data, "SSH DISCONNECT starts now")); if(conn->proto.sshc.ssh_session) { - /* only if there's a session still around to use! */ + /* only if there is a session still around to use! */ state(data, SSH_SFTP_SHUTDOWN); result = myssh_block_statemach(data, TRUE); } @@ -2553,11 +2545,13 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, /* return number of sent bytes */ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, + CURLcode *err) { ssize_t nwrite; struct connectdata *conn = data->conn; (void)sockindex; + (void)eos; /* limit the writes to the maximum specified in Section 3 of * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02 @@ -2613,7 +2607,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, nread = sftp_async_read(conn->proto.sshc.sftp_file, mem, (uint32_t)len, - conn->proto.sshc.sftp_file_index); + (uint32_t)conn->proto.sshc.sftp_file_index); myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE); @@ -2717,7 +2711,7 @@ static void sftp_quote(struct Curl_easy *data) } /* - * SFTP is a binary protocol, so we don't send text commands + * SFTP is a binary protocol, so we do not send text commands * to the server. Instead, we scan for commands used by * OpenSSH's sftp program and call the appropriate libssh * functions. diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index abdf42e55..83e356c0e 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -30,9 +30,6 @@ #include -#include -#include - #ifdef HAVE_FCNTL_H #include #endif @@ -405,8 +402,8 @@ static int sshkeycallback(struct Curl_easy *easy, #endif /* - * Earlier libssh2 versions didn't have the ability to seek to 64bit positions - * with 32bit size_t. + * Earlier libssh2 versions did not have the ability to seek to 64-bit + * positions with 32-bit size_t. */ #ifdef HAVE_LIBSSH2_SFTP_SEEK64 #define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y) @@ -415,27 +412,27 @@ static int sshkeycallback(struct Curl_easy *easy, #endif /* - * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit - * architectures so we check of the necessary function is present. + * Earlier libssh2 versions did not do SCP properly beyond 32-bit sizes on + * 32-bit architectures so we check of the necessary function is present. */ #ifndef HAVE_LIBSSH2_SCP_SEND64 #define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0) #else #define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \ - (libssh2_uint64_t)d, 0, 0) + (libssh2_int64_t)d, 0, 0) #endif /* - * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64. + * libssh2 1.2.8 fixed the problem with 32-bit ints used for sockets on win64. */ #ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE #define session_startup(x,y) libssh2_session_handshake(x, y) #else #define session_startup(x,y) libssh2_session_startup(x, (int)y) #endif -static int convert_ssh2_keytype(int sshkeytype) +static enum curl_khtype convert_ssh2_keytype(int sshkeytype) { - int keytype = CURLKHTYPE_UNKNOWN; + enum curl_khtype keytype = CURLKHTYPE_UNKNOWN; switch(sshkeytype) { case LIBSSH2_HOSTKEY_TYPE_RSA: keytype = CURLKHTYPE_RSA; @@ -476,7 +473,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) #ifdef HAVE_LIBSSH2_KNOWNHOST_API if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we're asked to verify the host against a file */ + /* we are asked to verify the host against a file */ struct connectdata *conn = data->conn; struct ssh_conn *sshc = &conn->proto.sshc; struct libssh2_knownhost *host = NULL; @@ -487,8 +484,8 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) if(remotekey) { /* - * A subject to figure out is what host name we need to pass in here. - * What host name does OpenSSH store in its file if an IDN name is + * A subject to figure out is what hostname we need to pass in here. + * What hostname does OpenSSH store in its file if an IDN name is * used? */ enum curl_khmatch keymatch; @@ -526,7 +523,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) break; #endif default: - infof(data, "unsupported key type, can't check knownhosts"); + infof(data, "unsupported key type, cannot check knownhosts"); keybit = 0; break; } @@ -600,7 +597,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; break; case CURLKHSTAT_FINE_REPLACE: - /* remove old host+key that doesn't match */ + /* remove old host+key that does not match */ if(host) libssh2_knownhost_del(sshc->kh, host); FALLTHROUGH(); @@ -608,7 +605,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data) case CURLKHSTAT_FINE_ADD_TO_FILE: /* proceed */ if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - /* the found host+key didn't match but has been told to be fine + /* the found host+key did not match but has been told to be fine anyway so we add it in memory */ int addrc = libssh2_knownhost_add(sshc->kh, conn->host.name, NULL, @@ -662,7 +659,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) size_t b64_pos = 0; #ifdef LIBSSH2_HOSTKEY_HASH_SHA256 - /* The fingerprint points to static storage (!), don't free() it. */ + /* The fingerprint points to static storage (!), do not free() it. */ fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_SHA256); #else @@ -742,7 +739,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) LIBSSH2_HOSTKEY_HASH_MD5); if(fingerprint) { - /* The fingerprint points to static storage (!), don't free() it. */ + /* The fingerprint points to static storage (!), do not free() it. */ int i; for(i = 0; i < 16; i++) { msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); @@ -780,10 +777,10 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data) const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, &keylen, &sshkeytype); if(remotekey) { - int keytype = convert_ssh2_keytype(sshkeytype); + enum curl_khtype keytype = convert_ssh2_keytype(sshkeytype); Curl_set_in_callback(data, true); rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, - keytype, remotekey, keylen); + (int)keytype, remotekey, keylen); Curl_set_in_callback(data, false); if(rc!= CURLKHMATCH_OK) { state(data, SSH_SESSION_FREE); @@ -960,7 +957,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) /* * ssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points + * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN * meaning it wants to be called again when the socket is ready */ @@ -977,7 +974,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) unsigned long sftperr; int seekerr = CURL_SEEKFUNC_OK; size_t readdir_len; - *block = 0; /* we're not blocking by default */ + *block = 0; /* we are not blocking by default */ do { switch(sshc->state) { @@ -1037,7 +1034,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) * must never change it later. Thus, always specify the correct username * here, even though the libssh2 docs kind of indicate that it should be * possible to get a 'generic' list (not user-specific) of authentication - * methods, presumably with a blank username. That won't work in my + * methods, presumably with a blank username. That will not work in my * experience. * So always specify it here. */ @@ -1440,7 +1437,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) if(sftperr) result = sftp_libssh2_error_to_CURLE(sftperr); else - /* in this case, the error wasn't in the SFTP level but for example + /* in this case, the error was not in the SFTP level but for example a time-out or similar */ result = CURLE_SSH; sshc->actualcode = result; @@ -1571,7 +1568,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) } /* - * SFTP is a binary protocol, so we don't send text commands + * SFTP is a binary protocol, so we do not send text commands * to the server. Instead, we scan for commands used by * OpenSSH's sftp program and call the appropriate libssh2 * functions. @@ -1709,7 +1706,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) if(!strncasecompare(cmd, "chmod", 5)) { /* Since chown and chgrp only set owner OR group but libssh2 wants to * set them both at once, we need to obtain the current ownership - * first. This takes an extra protocol round trip. + * first. This takes an extra protocol round trip. */ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, curlx_uztoui(strlen(sshc->quote_path2)), @@ -1786,7 +1783,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) } #if SIZEOF_TIME_T > SIZEOF_LONG if(date > 0xffffffff) { - /* if 'long' can't old >32bit, this date cannot be sent */ + /* if 'long' cannot old >32-bit, this date cannot be sent */ failf(data, "date overflow"); fail = TRUE; } @@ -1860,7 +1857,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_SFTP_QUOTE_MKDIR: rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1, curlx_uztoui(strlen(sshc->quote_path1)), - data->set.new_directory_perms); + (long)data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -2026,7 +2023,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc == 0) { - data->info.filetime = attrs.mtime; + data->info.filetime = (time_t)attrs.mtime; } state(data, SSH_SFTP_TRANS_INIT); @@ -2069,7 +2066,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) else { curl_off_t size = attrs.filesize; if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + failf(data, "Bad file size (%" FMT_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } data->state.resume_from = attrs.filesize; @@ -2090,7 +2087,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, curlx_uztoui(strlen(sshp->path)), - flags, data->set.new_file_perms, + flags, (long)data->set.new_file_perms, LIBSSH2_SFTP_OPENFILE); if(!sshc->sftp_handle) { @@ -2160,7 +2157,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) failf(data, "Could not seek stream"); return CURLE_FTP_COULDNT_USE_REST; } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { char scratch[4*1024]; size_t readthisamountnow = @@ -2199,7 +2196,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; @@ -2209,7 +2206,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) sshc->actualcode = result; } else { - /* store this original bitmask setup to use later on if we can't + /* store this original bitmask setup to use later on if we cannot figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; @@ -2218,7 +2215,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) with both accordingly */ data->state.select_bits = CURL_CSELECT_OUT; - /* since we don't really wait for anything at this point, we want the + /* since we do not really wait for anything at this point, we want the state machine to move on as soon as possible so we set a very short timeout here */ Curl_expire(data, 0, EXPIRE_RUN_NOW); @@ -2254,7 +2251,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* 'mode' - parameter is preliminary - default to 0644 */ rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, curlx_uztoui(strlen(sshp->path)), - data->set.new_directory_perms); + (long)data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -2262,7 +2259,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) ++sshc->slash_pos; if(rc < 0) { /* - * Abort if failure wasn't that the dir already exists or the + * Abort if failure was not that the dir already exists or the * permission was denied (creation might succeed further down the * path) - retry on unspecific FAILURE also */ @@ -2402,7 +2399,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) rc = libssh2_sftp_symlink_ex(sshc->sftp_session, Curl_dyn_ptr(&sshp->readdir_link), - (int)Curl_dyn_len(&sshp->readdir_link), + (unsigned int) + Curl_dyn_len(&sshp->readdir_link), sshp->readdir_filename, PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) { @@ -2452,7 +2450,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_safefree(sshp->readdir_longentry); /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); state(data, SSH_STOP); break; @@ -2463,7 +2461,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, curlx_uztoui(strlen(sshp->path)), - LIBSSH2_FXF_READ, data->set.new_file_perms, + LIBSSH2_FXF_READ, (long)data->set.new_file_perms, LIBSSH2_SFTP_OPENFILE); if(!sshc->sftp_handle) { if(libssh2_session_last_errno(sshc->ssh_session) == @@ -2496,9 +2494,9 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || (attrs.filesize == 0)) { /* - * libssh2_sftp_open() didn't return an error, so maybe the server - * just doesn't support stat() - * OR the server doesn't return a file size with a stat() + * libssh2_sftp_open() did not return an error, so maybe the server + * just does not support stat() + * OR the server does not return a file size with a stat() * OR file size is 0 */ data->req.size = -1; @@ -2509,7 +2507,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) curl_off_t size = attrs.filesize; if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + failf(data, "Bad file size (%" FMT_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } if(data->state.use_range) { @@ -2537,10 +2535,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) to = size - 1; } if(from > size) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, - (curl_off_t)attrs.filesize); + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } if(from > to) { @@ -2563,11 +2559,10 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* We can resume if we can seek to the resume position */ if(data->state.resume_from) { if(data->state.resume_from < 0) { - /* We're supposed to download the last abs(from) bytes */ + /* We are supposed to download the last abs(from) bytes */ if((curl_off_t)attrs.filesize < -data->state.resume_from) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -2576,8 +2571,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) } else { if((curl_off_t)attrs.filesize < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -2594,12 +2589,12 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; @@ -2713,7 +2708,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_SCP_UPLOAD_INIT: /* * libssh2 requires that the destination path is a full path that - * includes the destination file and name OR ends in a "/" . If this is + * includes the destination file and name OR ends in a "/" . If this is * not done the destination file will be named the same name as the last * directory in the path. */ @@ -2745,7 +2740,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* upload data */ data->req.size = data->state.infilesize; Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; @@ -2755,7 +2750,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) sshc->actualcode = result; } else { - /* store this original bitmask setup to use later on if we can't + /* store this original bitmask setup to use later on if we cannot figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; @@ -2816,7 +2811,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) /* download data */ bytecount = (curl_off_t)sb.st_size; data->req.maxdownload = (curl_off_t)sb.st_size; - Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; @@ -2915,7 +2910,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SESSION_DISCONNECT: - /* during weird times when we've been prematurely aborted, the channel + /* during weird times when we have been prematurely aborted, the channel is still alive when we reach this state and we MUST kill the channel properly first */ if(sshc->ssh_channel) { @@ -3071,7 +3066,7 @@ static int ssh_getsock(struct Curl_easy *data, * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this * function is used to figure out in what direction and stores this info so * that the multi interface can take advantage of it. Make sure to call this - * function in all cases so that when it _doesn't_ return EAGAIN we can + * function in all cases so that when it _does not_ return EAGAIN we can * restore the default wait bits. */ static void ssh_block2waitfor(struct Curl_easy *data, bool block) @@ -3088,7 +3083,7 @@ static void ssh_block2waitfor(struct Curl_easy *data, bool block) } } if(!dir) - /* It didn't block or libssh2 didn't reveal in which direction, put back + /* It did not block or libssh2 did not reveal in which direction, put back the original set */ conn->waitfor = sshc->orig_waitfor; } @@ -3104,7 +3099,7 @@ static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) do { result = ssh_statemach_act(data, &block); *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; - /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then + /* if there is no error, it is not done and it did not EWOULDBLOCK, then try again */ } while(!result && !*done && !block); ssh_block2waitfor(data, block); @@ -3228,7 +3223,7 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, /* swap in the TLS writer function for this call only, and then swap back the SSH one again */ conn->send[0] = ssh->tls_send; - result = Curl_conn_send(data, socknum, buffer, length, &nwrite); + result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite); conn->send[0] = backup; if(result == CURLE_AGAIN) return -EAGAIN; /* magic return code for libssh2 */ @@ -3290,7 +3285,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) #if LIBSSH2_VERSION_NUM >= 0x010B00 if(data->set.server_response_timeout > 0) { libssh2_session_set_read_timeout(sshc->ssh_session, - data->set.server_response_timeout / 1000); + (long)(data->set.server_response_timeout / 1000)); } #endif @@ -3491,7 +3486,7 @@ static CURLcode scp_disconnect(struct Curl_easy *data, (void) dead_connection; if(sshc->ssh_session) { - /* only if there's a session still around to use! */ + /* only if there is a session still around to use! */ state(data, SSH_SESSION_DISCONNECT); result = ssh_block_statemach(data, conn, TRUE); } @@ -3539,12 +3534,13 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, } static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, CURLcode *err) { ssize_t nwrite; struct connectdata *conn = data->conn; struct ssh_conn *sshc = &conn->proto.sshc; (void)sockindex; /* we only support SCP on the fixed known primary socket */ + (void)eos; /* libssh2_channel_write() returns int! */ nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); @@ -3647,7 +3643,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, DEBUGF(infof(data, "SSH DISCONNECT starts now")); if(sshc->ssh_session) { - /* only if there's a session still around to use! */ + /* only if there is a session still around to use! */ state(data, SSH_SFTP_SHUTDOWN); result = ssh_block_statemach(data, conn, TRUE); } @@ -3677,12 +3673,13 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, /* return number of sent bytes */ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, CURLcode *err) { ssize_t nwrite; struct connectdata *conn = data->conn; struct ssh_conn *sshc = &conn->proto.sshc; (void)sockindex; + (void)eos; nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index ca0533aa5..2ed78649b 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h @@ -30,6 +30,8 @@ #include #include #elif defined(USE_LIBSSH) +/* in 0.10.0 or later, ignore deprecated warnings */ +#define SSH_SUPPRESS_DEPRECATED #include #include #elif defined(USE_WOLFSSH) @@ -163,7 +165,7 @@ struct ssh_conn { unsigned kbd_state; /* 0 or 1 */ ssh_key privkey; ssh_key pubkey; - int auth_methods; + unsigned int auth_methods; ssh_session ssh_session; ssh_scp scp_session; sftp_session sftp_session; @@ -243,10 +245,10 @@ struct ssh_conn { #endif #ifdef HAVE_LIBSSH2_VERSION -/* get it run-time if possible */ +/* get it runtime if possible */ #define CURL_LIBSSH2_VERSION libssh2_version(0) #else -/* use build-time if run-time not possible */ +/* use build-time if runtime not possible */ #define CURL_LIBSSH2_VERSION LIBSSH2_VERSION #endif diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index 6a5aed88f..1d01bcdb7 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -28,8 +28,6 @@ #include -#include -#include #include "urldata.h" #include "cfilters.h" #include "connect.h" @@ -220,13 +218,15 @@ static void state(struct Curl_easy *data, sshstate nowstate) } static ssize_t wscp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, + CURLcode *err) { ssize_t nwrite = 0; (void)data; (void)sockindex; /* we only support SCP on the fixed known primary socket */ (void)mem; (void)len; + (void)eos; (void)err; return nwrite; @@ -247,13 +247,14 @@ static ssize_t wscp_recv(struct Curl_easy *data, int sockindex, /* return number of sent bytes */ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) + const void *mem, size_t len, bool eos, CURLcode *err) { struct connectdata *conn = data->conn; struct ssh_conn *sshc = &conn->proto.sshc; word32 offset[2]; int rc; (void)sockindex; + (void)eos; offset[0] = (word32)sshc->offset&0xFFFFFFFF; offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; @@ -280,7 +281,7 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, return -1; } DEBUGASSERT(rc == (int)len); - infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T, + infof(data, "sent %zu bytes SFTP from offset %" FMT_OFF_T, len, sshc->offset); sshc->offset += len; return (ssize_t)rc; @@ -400,7 +401,7 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done) rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user); if(rc != WS_SUCCESS) { - failf(data, "wolfSSH failed to set user name"); + failf(data, "wolfSSH failed to set username"); goto error; } @@ -433,7 +434,7 @@ error: /* * wssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points + * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it * wants to be called again when the socket is ready */ @@ -446,7 +447,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) struct SSHPROTO *sftp_scp = data->req.p.ssh; WS_SFTPNAME *name; int rc = 0; - *block = FALSE; /* we're not blocking by default */ + *block = FALSE; /* we are not blocking by default */ do { switch(sshc->state) { @@ -577,7 +578,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) else { curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); + failf(data, "Bad file size (%" FMT_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } data->state.resume_from = size; @@ -641,7 +642,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) failf(data, "Could not seek stream"); return CURLE_FTP_COULDNT_USE_REST; } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { char scratch[4*1024]; size_t readthisamountnow = @@ -680,7 +681,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ - Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET); + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->sockfd = conn->writesockfd; @@ -690,7 +691,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) sshc->actualcode = result; } else { - /* store this original bitmask setup to use later on if we can't + /* store this original bitmask setup to use later on if we cannot figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; @@ -699,7 +700,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) with both accordingly */ data->state.select_bits = CURL_CSELECT_OUT; - /* since we don't really wait for anything at this point, we want the + /* since we do not really wait for anything at this point, we want the state machine to move on as soon as possible so we set a very short timeout here */ Curl_expire(data, 0, EXPIRE_RUN_NOW); @@ -768,7 +769,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) data->req.maxdownload = size; Curl_pgrsSetDownloadSize(data, size); - infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size); + infof(data, "SFTP download %" FMT_OFF_T " bytes", size); /* We cannot seek with wolfSSH so resuming and range requests are not possible */ @@ -780,12 +781,12 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ - Curl_xfer_setup(data, -1, -1, FALSE, -1); + Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); state(data, SSH_STOP); break; } - Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1); + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; @@ -908,7 +909,7 @@ static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) do { result = wssh_statemach_act(data, &block); *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; - /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then + /* if there is no error, it is not done and it did not EWOULDBLOCK, then try again */ if(*done) { DEBUGF(infof(data, "wssh_statemach_act says DONE")); @@ -1121,7 +1122,7 @@ static CURLcode wsftp_disconnect(struct Curl_easy *data, DEBUGF(infof(data, "SSH DISCONNECT starts now")); if(conn->proto.sshc.ssh_session) { - /* only if there's a session still around to use! */ + /* only if there is a session still around to use! */ state(data, SSH_SFTP_SHUTDOWN); result = wssh_block_statemach(data, TRUE); } diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index a595f54a9..0199d6b42 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -63,6 +63,7 @@ struct bearssl_ssl_backend_data { bool active; /* size of pending write, yet to be flushed */ size_t pending_write; + BIT(sent_shutdown); }; struct cafile_parser { @@ -327,7 +328,7 @@ static unsigned x509_end_chain(const br_x509_class **ctx) struct x509_context *x509 = (struct x509_context *)ctx; if(!x509->verifypeer) { - return br_x509_decoder_last_error(&x509->decoder); + return (unsigned)br_x509_decoder_last_error(&x509->decoder); } return x509->minimal.vtable->end_chain(&x509->minimal.vtable); @@ -360,6 +361,56 @@ static const br_x509_class x509_vtable = { x509_get_pkey }; +static CURLcode +bearssl_set_ssl_version_min_max(struct Curl_easy *data, + br_ssl_engine_context *ssl_eng, + struct ssl_primary_config *conn_config) +{ + unsigned version_min, version_max; + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + version_min = BR_TLS10; + break; + case CURL_SSLVERSION_TLSv1_1: + version_min = BR_TLS11; + break; + case CURL_SSLVERSION_TLSv1_2: + version_min = BR_TLS12; + break; + case CURL_SSLVERSION_TLSv1_3: + failf(data, "BearSSL: does not support TLS 1.3"); + return CURLE_SSL_CONNECT_ERROR; + default: + failf(data, "BearSSL: unsupported minimum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; + } + + switch(conn_config->version_max) { + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_TLSv1_3: + case CURL_SSLVERSION_MAX_TLSv1_2: + version_max = BR_TLS12; + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + version_max = BR_TLS11; + break; + case CURL_SSLVERSION_MAX_TLSv1_0: + version_max = BR_TLS10; + break; + default: + failf(data, "BearSSL: unsupported maximum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; + } + + br_ssl_engine_set_versions(ssl_eng, version_min, version_max); + + return CURLE_OK; +} + static const uint16_t ciphertable[] = { /* RFC 2246 TLS 1.0 */ BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ @@ -494,41 +545,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const bool verifypeer = conn_config->verifypeer; const bool verifyhost = conn_config->verifyhost; CURLcode ret; - unsigned version_min, version_max; int session_set = 0; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "connect_step1"); - switch(conn_config->version) { - case CURL_SSLVERSION_SSLv2: - failf(data, "BearSSL does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_SSLv3: - failf(data, "BearSSL does not support SSLv3"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_0: - version_min = BR_TLS10; - version_max = BR_TLS10; - break; - case CURL_SSLVERSION_TLSv1_1: - version_min = BR_TLS11; - version_max = BR_TLS11; - break; - case CURL_SSLVERSION_TLSv1_2: - version_min = BR_TLS12; - version_max = BR_TLS12; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - version_min = BR_TLS10; - version_max = BR_TLS12; - break; - default: - failf(data, "BearSSL: unknown CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - if(verifypeer) { if(ca_info_blob) { struct cafile_source source; @@ -563,7 +584,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, /* initialize SSL context */ br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, backend->anchors, backend->anchors_len); - br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max); + + ret = bearssl_set_ssl_version_min_max(data, &backend->ctx.eng, conn_config); + if(ret != CURLE_OK) + return ret; + br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, sizeof(backend->buf), 1); @@ -583,7 +608,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, backend->x509.verifyhost = verifyhost; br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); - if(ssl_config->primary.sessionid) { + if(ssl_config->primary.cache_session) { void *session; CURL_TRC_CF(data, cf, "connect_step1, check session cache"); @@ -647,28 +672,6 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } -static void bearssl_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) -{ - if(!cf->connected) { - curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); - if(sock != CURL_SOCKET_BAD) { - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned state = br_ssl_engine_current_state(&backend->ctx.eng); - - if(state & BR_SSL_SENDREC) { - Curl_pollset_set_out_only(data, ps, sock); - } - else { - Curl_pollset_set_in_only(data, ps, sock); - } - } - } -} - static CURLcode bearssl_run_until(struct Curl_cfilter *cf, struct Curl_easy *data, unsigned target) @@ -685,6 +688,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, DEBUGASSERT(backend); + connssl->io_need = CURL_SSL_IO_NEED_NONE; for(;;) { state = br_ssl_engine_current_state(&backend->ctx.eng); if(state & BR_SSL_CLOSED) { @@ -709,7 +713,9 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, failf(data, "SSL: X.509 verification: " "chain could not be linked to a trust anchor"); return CURLE_PEER_FAILED_VERIFICATION; + default:; } + failf(data, "BearSSL: connection error 0x%04x", err); /* X.509 errors are documented to have the range 32..63 */ if(err >= 32 && err < 64) return CURLE_PEER_FAILED_VERIFICATION; @@ -719,9 +725,12 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, return CURLE_OK; if(state & BR_SSL_SENDREC) { buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); - ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result); + ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, FALSE, + &result); CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); if(ret <= 0) { + if(result == CURLE_AGAIN) + connssl->io_need |= CURL_SSL_IO_NEED_SEND; return result; } br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); @@ -735,6 +744,8 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; } if(ret <= 0) { + if(result == CURLE_AGAIN) + connssl->io_need |= CURL_SSL_IO_NEED_RECV; return result; } br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); @@ -813,9 +824,7 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, proto? strlen(proto) : 0); } - if(ssl_config->primary.sessionid) { - bool incache; - void *oldsession; + if(ssl_config->primary.cache_session) { br_ssl_session_parameters *session; session = malloc(sizeof(*session)); @@ -823,13 +832,8 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, - &oldsession, NULL)); - if(incache) - Curl_ssl_delsessionid(data, oldsession); - - ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, - bearssl_session_free); + ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, session, 0, + bearssl_session_free); Curl_ssl_sessionid_unlock(data); if(ret) return ret; @@ -925,9 +929,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, return ret; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -937,14 +939,13 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; CURL_TRC_CF(data, cf, "connect_common, check socket"); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, @@ -975,11 +976,9 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; ret = bearssl_connect_step2(cf, data); - if(ret || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return ret; } @@ -1070,20 +1069,54 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl, return &backend->ctx; } -static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode bearssl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct bearssl_ssl_backend_data *backend = (struct bearssl_ssl_backend_data *)connssl->backend; - size_t i; + CURLcode result; DEBUGASSERT(backend); + if(!backend->active || cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } - if(backend->active) { - backend->active = FALSE; + *done = FALSE; + if(!backend->sent_shutdown) { + (void)send_shutdown; /* unknown how to suppress our close notify */ br_ssl_engine_close(&backend->ctx.eng); - (void)bearssl_run_until(cf, data, BR_SSL_CLOSED); + backend->sent_shutdown = TRUE; + } + + result = bearssl_run_until(cf, data, BR_SSL_CLOSED); + if(result == CURLE_OK) { + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "shutdown EAGAIN, io_need=%x", connssl->io_need); + result = CURLE_OK; } + else + CURL_TRC_CF(data, cf, "shutdown error: %d", result); + + cf->shutdown = (result || *done); + return result; +} + +static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct bearssl_ssl_backend_data *backend = + (struct bearssl_ssl_backend_data *)connssl->backend; + size_t i; + + (void)data; + DEBUGASSERT(backend); + + backend->active = FALSE; if(backend->anchors) { for(i = 0; i < backend->anchors_len; ++i) free(backend->anchors[i].dn.data); @@ -1106,20 +1139,25 @@ static CURLcode bearssl_sha256sum(const unsigned char *input, const struct Curl_ssl Curl_ssl_bearssl = { { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY, + + SSLSUPP_CAINFO_BLOB | + SSLSUPP_SSL_CTX | + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST, + sizeof(struct bearssl_ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ bearssl_version, /* version */ Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ + bearssl_shutdown, /* shutdown */ bearssl_data_pending, /* data_pending */ bearssl_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - bearssl_adjust_pollset, /* adjust_pollset */ + Curl_ssl_adjust_pollset, /* adjust_pollset */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ @@ -1130,9 +1168,9 @@ const struct Curl_ssl Curl_ssl_bearssl = { bearssl_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ bearssl_recv, /* recv decrypted data */ bearssl_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif /* USE_BEARSSL */ diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.c b/Utilities/cmcurl/lib/vtls/cipher_suite.c index a78838d19..c025b53e9 100644 --- a/Utilities/cmcurl/lib/vtls/cipher_suite.c +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.c @@ -23,7 +23,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) #include "cipher_suite.h" #include "curl_printf.h" #include "strcase.h" @@ -33,7 +34,7 @@ * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends * that do not support it natively, but do support setting a list of * IANA ids, we need a list of all supported cipher suite names - * (openssl and IANA) to be able to look up the IANA ids. + * (OpenSSL and IANA) to be able to look up the IANA ids. * * To keep the binary size of this list down we compress each entry * down to 2 + 6 bytes using the C preprocessor. @@ -42,7 +43,7 @@ /* * mbedTLS NOTE: mbedTLS has mbedtls_ssl_get_ciphersuite_id() to * convert a string representation to an IANA id, we do not use that - * because it does not support "standard" openssl cipher suite + * because it does not support "standard" OpenSSL cipher suite * names, nor IANA names. */ @@ -89,6 +90,21 @@ static const char *cs_txt = "CAMELLIA128" "\0" "CAMELLIA256" "\0" #endif +#if defined(USE_SECTRANSP) + "40" "\0" + "ADH" "\0" + "AECDH" "\0" + "anon" "\0" + "DES40" "\0" + "DH" "\0" + "DSS" "\0" + "EDH" "\0" + "EXP" "\0" + "EXPORT" "\0" + "IDEA" "\0" + "RC2" "\0" + "RC4" "\0" +#endif ; /* Indexes of above cs_txt */ enum { @@ -129,28 +145,43 @@ enum { CS_TXT_IDX_CAMELLIA, CS_TXT_IDX_CAMELLIA128, CS_TXT_IDX_CAMELLIA256, +#endif +#if defined(USE_SECTRANSP) + CS_TXT_IDX_40, + CS_TXT_IDX_ADH, + CS_TXT_IDX_AECDH, + CS_TXT_IDX_anon, + CS_TXT_IDX_DES40, + CS_TXT_IDX_DH, + CS_TXT_IDX_DSS, + CS_TXT_IDX_EDH, + CS_TXT_IDX_EXP, + CS_TXT_IDX_EXPORT, + CS_TXT_IDX_IDEA, + CS_TXT_IDX_RC2, + CS_TXT_IDX_RC4, #endif CS_TXT_LEN, }; -#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ -{ \ - (uint8_t) ((a) << 2 | ((b) & 0x3F) >> 4), \ - (uint8_t) ((b) << 4 | ((c) & 0x3F) >> 2), \ - (uint8_t) ((c) << 6 | ((d) & 0x3F)), \ - (uint8_t) ((e) << 2 | ((f) & 0x3F) >> 4), \ - (uint8_t) ((f) << 4 | ((g) & 0x3F) >> 2), \ - (uint8_t) ((g) << 6 | ((h) & 0x3F)) \ +#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ +{ \ + (uint8_t) ((((a) << 2) & 0xFF) | ((b) & 0x3F) >> 4), \ + (uint8_t) ((((b) << 4) & 0xFF) | ((c) & 0x3F) >> 2), \ + (uint8_t) ((((c) << 6) & 0xFF) | ((d) & 0x3F)), \ + (uint8_t) ((((e) << 2) & 0xFF) | ((f) & 0x3F) >> 4), \ + (uint8_t) ((((f) << 4) & 0xFF) | ((g) & 0x3F) >> 2), \ + (uint8_t) ((((g) << 6) & 0xFF) | ((h) & 0x3F)) \ } -#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ -{ \ - id, \ - CS_ZIP_IDX( \ - CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ - CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ - CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ - CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ - ) \ +#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ +{ \ + id, \ + CS_ZIP_IDX( \ + CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ + CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ + CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ + CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ + ) \ } struct cs_entry { @@ -160,6 +191,28 @@ struct cs_entry { /* !checksrc! disable COMMANOSPACE all */ static const struct cs_entry cs_list [] = { + /* TLS 1.3 ciphers */ +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_RUSTLS) + CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), + CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), + CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), + CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), + CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), +#endif + /* TLS 1.2 ciphers */ + CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,), + CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,), + CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,), +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL) CS_ENTRY(0x002F, TLS,RSA,WITH,AES,128,CBC,SHA,), CS_ENTRY(0x002F, AES128,SHA,,,,,,), CS_ENTRY(0x0035, TLS,RSA,WITH,AES,256,CBC,SHA,), @@ -204,27 +257,16 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xC029, ECDH,RSA,AES128,SHA256,,,,), CS_ENTRY(0xC02A, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA384), CS_ENTRY(0xC02A, ECDH,RSA,AES256,SHA384,,,,), - CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,), - CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,), CS_ENTRY(0xC02D, TLS,ECDH,ECDSA,WITH,AES,128,GCM,SHA256), CS_ENTRY(0xC02D, ECDH,ECDSA,AES128,GCM,SHA256,,,), CS_ENTRY(0xC02E, TLS,ECDH,ECDSA,WITH,AES,256,GCM,SHA384), CS_ENTRY(0xC02E, ECDH,ECDSA,AES256,GCM,SHA384,,,), - CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,), - CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,), CS_ENTRY(0xC031, TLS,ECDH,RSA,WITH,AES,128,GCM,SHA256), CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,), CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), - CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,), - CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,), - CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,), - CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,), -#if defined(USE_MBEDTLS) +#endif +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), CS_ENTRY(0x0001, NULL,MD5,,,,,,), CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), @@ -297,11 +339,6 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0x00B8, RSA,PSK,NULL,SHA256,,,,), CS_ENTRY(0x00B9, TLS,RSA,PSK,WITH,NULL,SHA384,,), CS_ENTRY(0x00B9, RSA,PSK,NULL,SHA384,,,,), - CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), - CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), - CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), - CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,), - CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,), CS_ENTRY(0xC001, TLS,ECDH,ECDSA,WITH,NULL,SHA,,), CS_ENTRY(0xC001, ECDH,ECDSA,NULL,SHA,,,,), CS_ENTRY(0xC006, TLS,ECDHE,ECDSA,WITH,NULL,SHA,,), @@ -317,7 +354,7 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), #endif -#if defined(USE_BEARSSL) +#if defined(USE_SECTRANSP) || defined(USE_BEARSSL) CS_ENTRY(0x000A, TLS,RSA,WITH,3DES,EDE,CBC,SHA,), CS_ENTRY(0x000A, DES,CBC3,SHA,,,,,), CS_ENTRY(0xC003, TLS,ECDH,ECDSA,WITH,3DES,EDE,CBC,SHA), @@ -329,6 +366,7 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xC012, TLS,ECDHE,RSA,WITH,3DES,EDE,CBC,SHA), CS_ENTRY(0xC012, ECDHE,RSA,DES,CBC3,SHA,,,), #endif +#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), CS_ENTRY(0xC09C, AES128,CCM,,,,,,), CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), @@ -345,8 +383,144 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xC0AE, ECDHE,ECDSA,AES128,CCM8,,,,), CS_ENTRY(0xC0AF, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,8), CS_ENTRY(0xC0AF, ECDHE,ECDSA,AES256,CCM8,,,,), +#endif +#if defined(USE_SECTRANSP) + /* entries marked bc are backward compatible aliases for old OpenSSL names */ + CS_ENTRY(0x0003, TLS,RSA,EXPORT,WITH,RC4,40,MD5,), + CS_ENTRY(0x0003, EXP,RC4,MD5,,,,,), + CS_ENTRY(0x0004, TLS,RSA,WITH,RC4,128,MD5,,), + CS_ENTRY(0x0004, RC4,MD5,,,,,,), + CS_ENTRY(0x0005, TLS,RSA,WITH,RC4,128,SHA,,), + CS_ENTRY(0x0005, RC4,SHA,,,,,,), + CS_ENTRY(0x0006, TLS,RSA,EXPORT,WITH,RC2,CBC,40,MD5), + CS_ENTRY(0x0006, EXP,RC2,CBC,MD5,,,,), + CS_ENTRY(0x0007, TLS,RSA,WITH,IDEA,CBC,SHA,,), + CS_ENTRY(0x0007, IDEA,CBC,SHA,,,,,), + CS_ENTRY(0x0008, TLS,RSA,EXPORT,WITH,DES40,CBC,SHA,), + CS_ENTRY(0x0008, EXP,DES,CBC,SHA,,,,), + CS_ENTRY(0x0009, TLS,RSA,WITH,DES,CBC,SHA,,), + CS_ENTRY(0x0009, DES,CBC,SHA,,,,,), + CS_ENTRY(0x000B, TLS,DH,DSS,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x000B, EXP,DH,DSS,DES,CBC,SHA,,), + CS_ENTRY(0x000C, TLS,DH,DSS,WITH,DES,CBC,SHA,), + CS_ENTRY(0x000C, DH,DSS,DES,CBC,SHA,,,), + CS_ENTRY(0x000D, TLS,DH,DSS,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x000D, DH,DSS,DES,CBC3,SHA,,,), + CS_ENTRY(0x000E, TLS,DH,RSA,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x000E, EXP,DH,RSA,DES,CBC,SHA,,), + CS_ENTRY(0x000F, TLS,DH,RSA,WITH,DES,CBC,SHA,), + CS_ENTRY(0x000F, DH,RSA,DES,CBC,SHA,,,), + CS_ENTRY(0x0010, TLS,DH,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0010, DH,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0x0011, TLS,DHE,DSS,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0011, EXP,DHE,DSS,DES,CBC,SHA,,), + CS_ENTRY(0x0011, EXP,EDH,DSS,DES,CBC,SHA,,), /* bc */ + CS_ENTRY(0x0012, TLS,DHE,DSS,WITH,DES,CBC,SHA,), + CS_ENTRY(0x0012, DHE,DSS,DES,CBC,SHA,,,), + CS_ENTRY(0x0012, EDH,DSS,DES,CBC,SHA,,,), /* bc */ + CS_ENTRY(0x0013, TLS,DHE,DSS,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0013, DHE,DSS,DES,CBC3,SHA,,,), + CS_ENTRY(0x0013, EDH,DSS,DES,CBC3,SHA,,,), /* bc */ + CS_ENTRY(0x0014, TLS,DHE,RSA,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0014, EXP,DHE,RSA,DES,CBC,SHA,,), + CS_ENTRY(0x0014, EXP,EDH,RSA,DES,CBC,SHA,,), /* bc */ + CS_ENTRY(0x0015, TLS,DHE,RSA,WITH,DES,CBC,SHA,), + CS_ENTRY(0x0015, DHE,RSA,DES,CBC,SHA,,,), + CS_ENTRY(0x0015, EDH,RSA,DES,CBC,SHA,,,), /* bc */ + CS_ENTRY(0x0016, TLS,DHE,RSA,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0016, DHE,RSA,DES,CBC3,SHA,,,), + CS_ENTRY(0x0016, EDH,RSA,DES,CBC3,SHA,,,), /* bc */ + CS_ENTRY(0x0017, TLS,DH,anon,EXPORT,WITH,RC4,40,MD5), + CS_ENTRY(0x0017, EXP,ADH,RC4,MD5,,,,), + CS_ENTRY(0x0018, TLS,DH,anon,WITH,RC4,128,MD5,), + CS_ENTRY(0x0018, ADH,RC4,MD5,,,,,), + CS_ENTRY(0x0019, TLS,DH,anon,EXPORT,WITH,DES40,CBC,SHA), + CS_ENTRY(0x0019, EXP,ADH,DES,CBC,SHA,,,), + CS_ENTRY(0x001A, TLS,DH,anon,WITH,DES,CBC,SHA,), + CS_ENTRY(0x001A, ADH,DES,CBC,SHA,,,,), + CS_ENTRY(0x001B, TLS,DH,anon,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x001B, ADH,DES,CBC3,SHA,,,,), + CS_ENTRY(0x0030, TLS,DH,DSS,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0030, DH,DSS,AES128,SHA,,,,), + CS_ENTRY(0x0031, TLS,DH,RSA,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0031, DH,RSA,AES128,SHA,,,,), + CS_ENTRY(0x0032, TLS,DHE,DSS,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0032, DHE,DSS,AES128,SHA,,,,), + CS_ENTRY(0x0034, TLS,DH,anon,WITH,AES,128,CBC,SHA), + CS_ENTRY(0x0034, ADH,AES128,SHA,,,,,), + CS_ENTRY(0x0036, TLS,DH,DSS,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0036, DH,DSS,AES256,SHA,,,,), + CS_ENTRY(0x0037, TLS,DH,RSA,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0037, DH,RSA,AES256,SHA,,,,), + CS_ENTRY(0x0038, TLS,DHE,DSS,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x0038, DHE,DSS,AES256,SHA,,,,), + CS_ENTRY(0x003A, TLS,DH,anon,WITH,AES,256,CBC,SHA), + CS_ENTRY(0x003A, ADH,AES256,SHA,,,,,), + CS_ENTRY(0x003E, TLS,DH,DSS,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x003E, DH,DSS,AES128,SHA256,,,,), + CS_ENTRY(0x003F, TLS,DH,RSA,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x003F, DH,RSA,AES128,SHA256,,,,), + CS_ENTRY(0x0040, TLS,DHE,DSS,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x0040, DHE,DSS,AES128,SHA256,,,,), + CS_ENTRY(0x0068, TLS,DH,DSS,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x0068, DH,DSS,AES256,SHA256,,,,), + CS_ENTRY(0x0069, TLS,DH,RSA,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x0069, DH,RSA,AES256,SHA256,,,,), + CS_ENTRY(0x006A, TLS,DHE,DSS,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006A, DHE,DSS,AES256,SHA256,,,,), + CS_ENTRY(0x006C, TLS,DH,anon,WITH,AES,128,CBC,SHA256), + CS_ENTRY(0x006C, ADH,AES128,SHA256,,,,,), + CS_ENTRY(0x006D, TLS,DH,anon,WITH,AES,256,CBC,SHA256), + CS_ENTRY(0x006D, ADH,AES256,SHA256,,,,,), + CS_ENTRY(0x008A, TLS,PSK,WITH,RC4,128,SHA,,), + CS_ENTRY(0x008A, PSK,RC4,SHA,,,,,), + CS_ENTRY(0x008B, TLS,PSK,WITH,3DES,EDE,CBC,SHA,), + CS_ENTRY(0x008B, PSK,3DES,EDE,CBC,SHA,,,), + CS_ENTRY(0x008E, TLS,DHE,PSK,WITH,RC4,128,SHA,), + CS_ENTRY(0x008E, DHE,PSK,RC4,SHA,,,,), + CS_ENTRY(0x008F, TLS,DHE,PSK,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x008F, DHE,PSK,3DES,EDE,CBC,SHA,,), + CS_ENTRY(0x0092, TLS,RSA,PSK,WITH,RC4,128,SHA,), + CS_ENTRY(0x0092, RSA,PSK,RC4,SHA,,,,), + CS_ENTRY(0x0093, TLS,RSA,PSK,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0x0093, RSA,PSK,3DES,EDE,CBC,SHA,,), + CS_ENTRY(0x00A0, TLS,DH,RSA,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A0, DH,RSA,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A1, TLS,DH,RSA,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A1, DH,RSA,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A2, TLS,DHE,DSS,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A2, DHE,DSS,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A3, TLS,DHE,DSS,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A3, DHE,DSS,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A4, TLS,DH,DSS,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A4, DH,DSS,AES128,GCM,SHA256,,,), + CS_ENTRY(0x00A5, TLS,DH,DSS,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A5, DH,DSS,AES256,GCM,SHA384,,,), + CS_ENTRY(0x00A6, TLS,DH,anon,WITH,AES,128,GCM,SHA256), + CS_ENTRY(0x00A6, ADH,AES128,GCM,SHA256,,,,), + CS_ENTRY(0x00A7, TLS,DH,anon,WITH,AES,256,GCM,SHA384), + CS_ENTRY(0x00A7, ADH,AES256,GCM,SHA384,,,,), + CS_ENTRY(0xC002, TLS,ECDH,ECDSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC002, ECDH,ECDSA,RC4,SHA,,,,), + CS_ENTRY(0xC007, TLS,ECDHE,ECDSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC007, ECDHE,ECDSA,RC4,SHA,,,,), + CS_ENTRY(0xC00C, TLS,ECDH,RSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC00C, ECDH,RSA,RC4,SHA,,,,), + CS_ENTRY(0xC011, TLS,ECDHE,RSA,WITH,RC4,128,SHA,), + CS_ENTRY(0xC011, ECDHE,RSA,RC4,SHA,,,,), + CS_ENTRY(0xC015, TLS,ECDH,anon,WITH,NULL,SHA,,), + CS_ENTRY(0xC015, AECDH,NULL,SHA,,,,,), + CS_ENTRY(0xC016, TLS,ECDH,anon,WITH,RC4,128,SHA,), + CS_ENTRY(0xC016, AECDH,RC4,SHA,,,,,), + CS_ENTRY(0xC017, TLS,ECDH,anon,WITH,3DES,EDE,CBC,SHA), + CS_ENTRY(0xC017, AECDH,DES,CBC3,SHA,,,,), + CS_ENTRY(0xC018, TLS,ECDH,anon,WITH,AES,128,CBC,SHA), + CS_ENTRY(0xC018, AECDH,AES128,SHA,,,,,), + CS_ENTRY(0xC019, TLS,ECDH,anon,WITH,AES,256,CBC,SHA), + CS_ENTRY(0xC019, AECDH,AES256,SHA,,,,,), +#endif #if defined(USE_MBEDTLS) - /* entries marked ns are "non-standard", they are not in openssl */ + /* entries marked ns are "non-standard", they are not in OpenSSL */ CS_ENTRY(0x0041, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA,), CS_ENTRY(0x0041, CAMELLIA128,SHA,,,,,,), CS_ENTRY(0x0045, TLS,DHE,RSA,WITH,CAMELLIA,128,CBC,SHA), @@ -713,4 +887,5 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, return r; } -#endif /* defined(USE_MBEDTLS) || defined(USE_BEARSSL) */ +#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) */ diff --git a/Utilities/cmcurl/lib/vtls/cipher_suite.h b/Utilities/cmcurl/lib/vtls/cipher_suite.h index c1399794f..6d980103a 100644 --- a/Utilities/cmcurl/lib/vtls/cipher_suite.h +++ b/Utilities/cmcurl/lib/vtls/cipher_suite.h @@ -26,7 +26,8 @@ #include "curl_setup.h" -#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) #include /* Lookup IANA id for cipher suite string, returns 0 if not recognized */ @@ -42,5 +43,6 @@ uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end); int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc); -#endif /* defined(USE_MBEDTLS) || defined(USE_BEARSSL) */ +#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) */ #endif /* HEADER_CURL_CIPHER_SUITE_H */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index 5cf3bf952..ad9a6b926 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -26,7 +26,7 @@ * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. * - * Note: don't use the GnuTLS' *_t variable type names in this source code, + * Note: do not use the GnuTLS' *_t variable type names in this source code, * since they were not present in 1.0.X. */ @@ -102,7 +102,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) CURLcode result; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result); CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d", blen, nwritten, result); backend->gtls.io_result = result; @@ -125,7 +125,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) CURLcode result; DEBUGASSERT(data); - if(!backend->gtls.trust_setup) { + if(!backend->gtls.shared_creds->trust_setup) { result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); if(result) { gnutls_transport_set_errno(backend->gtls.session, EINVAL); @@ -251,6 +251,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, DEBUGASSERT(backend); session = backend->gtls.session; + connssl->connecting_state = ssl_connect_2; for(;;) { timediff_t timeout_ms; @@ -265,14 +266,13 @@ static CURLcode handshake(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { int what; - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0: @@ -294,10 +294,11 @@ static CURLcode handshake(struct Curl_cfilter *cf, /* socket is readable or writable */ } + connssl->io_need = CURL_SSL_IO_NEED_NONE; backend->gtls.io_result = CURLE_OK; rc = gnutls_handshake(session); - if(!backend->gtls.trust_setup) { + if(!backend->gtls.shared_creds->trust_setup) { /* After having send off the ClientHello, we prepare the trust * store to verify the coming certificate from the server */ CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls); @@ -306,16 +307,16 @@ static CURLcode handshake(struct Curl_cfilter *cf, } if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { - connssl->connecting_state = + connssl->io_need = gnutls_record_get_direction(session)? - ssl_connect_2_writing:ssl_connect_2_reading; + CURL_SSL_IO_NEED_SEND:CURL_SSL_IO_NEED_RECV; continue; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { const char *strerr = NULL; if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); + gnutls_alert_description_t alert = gnutls_alert_get(session); strerr = gnutls_alert_get_name(alert); } @@ -332,14 +333,14 @@ static CURLcode handshake(struct Curl_cfilter *cf, const char *strerr = NULL; if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); + gnutls_alert_description_t alert = gnutls_alert_get(session); strerr = gnutls_alert_get_name(alert); } if(!strerr) strerr = gnutls_strerror(rc); - failf(data, "gnutls_handshake() failed: %s", strerr); + failf(data, "GnuTLS, handshake failed: %s", strerr); return CURLE_SSL_CONNECT_ERROR; } @@ -349,7 +350,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, } } -static gnutls_x509_crt_fmt_t do_file_type(const char *type) +static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) { if(!type || !type[0]) return GNUTLS_X509_FMT_PEM; @@ -367,18 +368,24 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type) #define GNUTLS_SRP "+SRP" static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, - struct ssl_peer *peer, - struct ssl_primary_config *conn_config, - const char **prioritylist, - const char *tls13support) +gnutls_set_ssl_version_min_max(struct Curl_easy *data, + struct ssl_peer *peer, + struct ssl_primary_config *conn_config, + const char **prioritylist, + const char *tls13support) { long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; + if((ssl_version == CURL_SSLVERSION_DEFAULT) || + (ssl_version == CURL_SSLVERSION_TLSv1)) + ssl_version = CURL_SSLVERSION_TLSv1_0; + if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) + ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; + if(peer->transport == TRNSPRT_QUIC) { - if((ssl_version != CURL_SSLVERSION_DEFAULT) && - (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + if((ssl_version_max != CURL_SSLVERSION_MAX_DEFAULT) && + (ssl_version_max < CURL_SSLVERSION_MAX_TLSv1_3)) { failf(data, "QUIC needs at least TLS version 1.3"); return CURLE_SSL_CONNECT_ERROR; } @@ -386,13 +393,8 @@ set_ssl_version_min_max(struct Curl_easy *data, return CURLE_OK; } - if((ssl_version == CURL_SSLVERSION_DEFAULT) || - (ssl_version == CURL_SSLVERSION_TLSv1)) - ssl_version = CURL_SSLVERSION_TLSv1_0; - if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) - ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; if(!tls13support) { - /* If the running GnuTLS doesn't support TLS 1.3, we must not specify a + /* If the running GnuTLS does not support TLS 1.3, we must not specify a prioritylist involving that since it will make GnuTLS return an en error back at us */ if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) || @@ -450,20 +452,67 @@ set_ssl_version_min_max(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } -CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct gtls_ctx *gtls) +CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, + struct gtls_shared_creds **pcreds) +{ + struct gtls_shared_creds *shared; + int rc; + + *pcreds = NULL; + shared = calloc(1, sizeof(*shared)); + if(!shared) + return CURLE_OUT_OF_MEMORY; + + rc = gnutls_certificate_allocate_credentials(&shared->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); + free(shared); + return CURLE_SSL_CONNECT_ERROR; + } + + shared->refcount = 1; + shared->time = Curl_now(); + *pcreds = shared; + return CURLE_OK; +} + +CURLcode Curl_gtls_shared_creds_up_ref(struct gtls_shared_creds *creds) +{ + DEBUGASSERT(creds); + if(creds->refcount < SIZE_T_MAX) { + ++creds->refcount; + return CURLE_OK; + } + return CURLE_BAD_FUNCTION_ARGUMENT; +} + +void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds) +{ + struct gtls_shared_creds *shared = *pcreds; + *pcreds = NULL; + if(shared) { + --shared->refcount; + if(!shared->refcount) { + gnutls_certificate_free_credentials(shared->creds); + free(shared->CAfile); + free(shared); + } + } +} + +static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_certificate_credentials_t creds) { struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); int rc; - CURL_TRC_CF(data, cf, "setup trust anchors and CRLs"); if(config->verifypeer) { bool imported_native_ca = false; if(ssl_config->native_ca_store) { - rc = gnutls_certificate_set_x509_system_trust(gtls->cred); + rc = gnutls_certificate_set_x509_system_trust(creds); if(rc < 0) infof(data, "error reading native ca store (%s), continuing anyway", gnutls_strerror(rc)); @@ -476,10 +525,10 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, if(config->CAfile) { /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(gtls->cred, + gnutls_certificate_set_verify_flags(creds, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - rc = gnutls_certificate_set_x509_trust_file(gtls->cred, + rc = gnutls_certificate_set_x509_trust_file(creds, config->CAfile, GNUTLS_X509_FMT_PEM); if(rc < 0) { @@ -497,8 +546,7 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, if(config->CApath) { /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(gtls->cred, - config->CApath, + rc = gnutls_certificate_set_x509_trust_dir(creds, config->CApath, GNUTLS_X509_FMT_PEM); if(rc < 0) { infof(data, "error reading ca cert file %s (%s)%s", @@ -516,8 +564,7 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, if(config->CRLfile) { /* set the CRL list file */ - rc = gnutls_certificate_set_x509_crl_file(gtls->cred, - config->CRLfile, + rc = gnutls_certificate_set_x509_crl_file(creds, config->CRLfile, GNUTLS_X509_FMT_PEM); if(rc < 0) { failf(data, "error reading crl file %s (%s)", @@ -528,7 +575,141 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, infof(data, "found %d CRL in %s", rc, config->CRLfile); } - gtls->trust_setup = TRUE; + return CURLE_OK; +} + +/* key to use at `multi->proto_hash` */ +#define MPROTO_GTLS_X509_KEY "tls:gtls:x509:share" + +static bool gtls_shared_creds_expired(const struct Curl_easy *data, + const struct gtls_shared_creds *sc) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, sc->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool gtls_shared_creds_different(struct Curl_cfilter *cf, + const struct gtls_shared_creds *sc) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!sc->CAfile || !conn_config->CAfile) + return sc->CAfile != conn_config->CAfile; + + return strcmp(sc->CAfile, conn_config->CAfile); +} + +static struct gtls_shared_creds* +gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct gtls_shared_creds *shared_creds; + + if(data->multi) { + shared_creds = Curl_hash_pick(&data->multi->proto_hash, + (void *)MPROTO_GTLS_X509_KEY, + sizeof(MPROTO_GTLS_X509_KEY)-1); + if(shared_creds && shared_creds->creds && + !gtls_shared_creds_expired(data, shared_creds) && + !gtls_shared_creds_different(cf, shared_creds)) { + return shared_creds; + } + } + return NULL; +} + +static void gtls_shared_creds_hash_free(void *key, size_t key_len, void *p) +{ + struct gtls_shared_creds *sc = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_GTLS_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_GTLS_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + Curl_gtls_shared_creds_free(&sc); /* down reference */ +} + +static void gtls_set_cached_creds(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_shared_creds *sc) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + + DEBUGASSERT(sc); + DEBUGASSERT(sc->creds); + DEBUGASSERT(!sc->CAfile); + DEBUGASSERT(sc->refcount == 1); + if(!data->multi) + return; + + if(conn_config->CAfile) { + sc->CAfile = strdup(conn_config->CAfile); + if(!sc->CAfile) + return; + } + + if(Curl_gtls_shared_creds_up_ref(sc)) + return; + + if(!Curl_hash_add2(&data->multi->proto_hash, + (void *)MPROTO_GTLS_X509_KEY, + sizeof(MPROTO_GTLS_X509_KEY)-1, + sc, gtls_shared_creds_hash_free)) { + Curl_gtls_shared_creds_free(&sc); /* down reference again */ + return; + } +} + +CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct gtls_ctx *gtls) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct gtls_shared_creds *cached_creds = NULL; + bool cache_criteria_met; + CURLcode result; + int rc; + + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to OpenSSL's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store && + !conn_config->clientcert; /* GnuTLS adds client cert to its credentials! */ + + if(cache_criteria_met) + cached_creds = gtls_get_cached_creds(cf, data); + + if(cached_creds && !Curl_gtls_shared_creds_up_ref(cached_creds)) { + CURL_TRC_CF(data, cf, "using shared trust anchors and CRLs"); + Curl_gtls_shared_creds_free(>ls->shared_creds); + gtls->shared_creds = cached_creds; + rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, + gtls->shared_creds->creds); + if(rc != GNUTLS_E_SUCCESS) { + failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + CURL_TRC_CF(data, cf, "loading trust anchors and CRLs"); + result = gtls_populate_creds(cf, data, gtls->shared_creds->creds); + if(result) + return result; + gtls->shared_creds->trust_setup = TRUE; + if(cache_criteria_met) + gtls_set_cached_creds(cf, data, gtls->shared_creds); + } return CURLE_OK; } @@ -546,7 +727,7 @@ static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; CURLcode result = CURLE_OK; - if(ssl_config->primary.sessionid) { + if(ssl_config->primary.cache_session) { /* we always unconditionally get the session id here, as even if we already got it from the cache and asked to use it in the connection, it might've been rejected and then a new one is in use now and we need to @@ -561,27 +742,16 @@ static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } else { - bool incache; - void *ssl_sessionid; - /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - DEBUGF(infof(data, "get session id (len=%zu) and store in cache", - connect_idsize)); + CURL_TRC_CF(data, cf, "get session id (len=%zu) and store in cache", + connect_idsize); Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, - &ssl_sessionid, NULL)); - if(incache) { - /* there was one before in the cache, so instead of risking that the - previous one was rejected, we just kill that and store the new */ - Curl_ssl_delsessionid(data, ssl_sessionid); - } - /* store this session id, takes ownership */ - result = Curl_ssl_addsessionid(cf, data, &connssl->peer, - connect_sessionid, connect_idsize, - gtls_sessionid_free); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + connect_sessionid, connect_idsize, + gtls_sessionid_free); Curl_ssl_sessionid_unlock(data); } } @@ -599,8 +769,8 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, if(when) { /* after message has been processed */ struct Curl_easy *data = CF_DATA_CURRENT(cf); if(data) { - DEBUGF(infof(data, "handshake: %s message type %d", - incoming? "incoming" : "outgoing", htype)); + CURL_TRC_CF(data, cf, "handshake: %s message type %d", + incoming? "incoming" : "outgoing", htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { gtls_update_session_id(cf, data, session); @@ -639,12 +809,10 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, else if(config->version == CURL_SSLVERSION_SSLv3) sni = FALSE; /* SSLv3 has no SNI */ - /* allocate a cred struct */ - rc = gnutls_certificate_allocate_credentials(>ls->cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } + /* allocate a shared creds struct */ + result = Curl_gtls_shared_creds_create(data, >ls->shared_creds); + if(result) + return result; #ifdef USE_GNUTLS_SRP if(config->username && Curl_auth_allowed_to_host(data)) { @@ -682,6 +850,13 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, init_flags |= GNUTLS_NO_TICKETS; #endif +#if defined(GNUTLS_NO_STATUS_REQUEST) + if(!config->verifystatus) + /* Disable the "status_request" TLS extension, enabled by default since + GnuTLS 3.8.0. */ + init_flags |= GNUTLS_NO_STATUS_REQUEST; +#endif + rc = gnutls_init(>ls->session, init_flags); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_init() failed: %d", rc); @@ -705,7 +880,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, tls13support = gnutls_check_version("3.6.5"); /* Ensure +SRP comes at the *end* of all relevant strings so that it can be - * removed if a run-time error indicates that SRP is not supported by this + * removed if a runtime error indicates that SRP is not supported by this * GnuTLS version */ if(config->version == CURL_SSLVERSION_SSLv2 || @@ -722,8 +897,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, } /* At this point we know we have a supported TLS version, so set it */ - result = set_ssl_version_min_max(data, peer, - config, &prioritylist, tls13support); + result = gnutls_set_ssl_version_min_max(data, peer, + config, &prioritylist, tls13support); if(result) return result; @@ -756,7 +931,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, } if(config->clientcert) { - if(!gtls->trust_setup) { + if(!gtls->shared_creds->trust_setup) { result = Curl_gtls_client_trust_setup(cf, data, gtls); if(result) return result; @@ -768,10 +943,10 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | GNUTLS_PKCS_USE_PBES2_AES_256; rc = gnutls_certificate_set_x509_key_file2( - gtls->cred, + gtls->shared_creds->creds, config->clientcert, ssl_config->key ? ssl_config->key : config->clientcert, - do_file_type(ssl_config->cert_type), + gnutls_do_file_type(ssl_config->cert_type), ssl_config->key_passwd, supported_key_encryption_algorithms); if(rc != GNUTLS_E_SUCCESS) { @@ -783,10 +958,10 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, } else { if(gnutls_certificate_set_x509_key_file( - gtls->cred, + gtls->shared_creds->creds, config->clientcert, ssl_config->key ? ssl_config->key : config->clientcert, - do_file_type(ssl_config->cert_type) ) != + gnutls_do_file_type(ssl_config->cert_type) ) != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 key or certificate file"); return CURLE_SSL_CONNECT_ERROR; @@ -808,7 +983,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, #endif { rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE, - gtls->cred); + gtls->shared_creds->creds); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; @@ -903,7 +1078,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ - if(conn_config->sessionid) { + if(conn_config->cache_session) { void *ssl_sessionid; size_t ssl_idsize; @@ -979,7 +1154,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - /* if a path wasn't specified, don't pin */ + /* if a path was not specified, do not pin */ if(!pinnedpubkey) return CURLE_OK; @@ -1045,7 +1220,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, CURLcode result = CURLE_OK; #ifndef CURL_DISABLE_VERBOSE_STRINGS const char *ptr; - unsigned int algo; + int algo; unsigned int bits; gnutls_protocol_t version = gnutls_protocol_get_version(session); #endif @@ -1087,13 +1262,13 @@ Curl_gtls_verifyserver(struct Curl_easy *data, } #endif } - infof(data, " common name: WARNING couldn't obtain"); + infof(data, " common name: WARNING could not obtain"); } if(data->set.ssl.certinfo && chainp) { unsigned int i; - result = Curl_ssl_init_certinfo(data, cert_list_size); + result = Curl_ssl_init_certinfo(data, (int)cert_list_size); if(result) return result; @@ -1101,7 +1276,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, const char *beg = (const char *) chainp[i].data; const char *end = beg + chainp[i].size; - result = Curl_extract_certinfo(data, i, beg, end); + result = Curl_extract_certinfo(data, (int)i, beg, end); if(result) return result; } @@ -1127,9 +1302,18 @@ Curl_gtls_verifyserver(struct Curl_easy *data, /* verify_status is a bitmask of gnutls_certificate_status bits */ if(verify_status & GNUTLS_CERT_INVALID) { if(config->verifypeer) { - failf(data, "server certificate verification failed. CAfile: %s " - "CRLfile: %s", config->CAfile ? config->CAfile: - "none", + const char *cause = "certificate error, no details available"; + if(verify_status & GNUTLS_CERT_EXPIRED) + cause = "certificate has expired"; + else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) + cause = "certificate signer not trusted"; + else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) + cause = "certificate uses insecure algorithm"; + else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) + cause = "attached OCSP status response is invalid"; + failf(data, "server verification failed: %s. (CAfile: %s " + "CRLfile: %s)", cause, + config->CAfile ? config->CAfile: "none", ssl_config->primary.CRLfile ? ssl_config->primary.CRLfile : "none"); return CURLE_PEER_FAILED_VERIFICATION; @@ -1144,104 +1328,97 @@ Curl_gtls_verifyserver(struct Curl_easy *data, infof(data, " server certificate verification SKIPPED"); if(config->verifystatus) { - if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { - gnutls_datum_t status_request; - gnutls_ocsp_resp_t ocsp_resp; + gnutls_datum_t status_request; + gnutls_ocsp_resp_t ocsp_resp; + gnutls_ocsp_cert_status_t status; + gnutls_x509_crl_reason_t reason; - gnutls_ocsp_cert_status_t status; - gnutls_x509_crl_reason_t reason; + rc = gnutls_ocsp_status_request_get(session, &status_request); - rc = gnutls_ocsp_status_request_get(session, &status_request); + if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + failf(data, "No OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } - infof(data, " server certificate status verification FAILED"); + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } - if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - failf(data, "No OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } + gnutls_ocsp_resp_init(&ocsp_resp); - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } + rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); + if(rc < 0) { + failf(data, "Invalid OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } - gnutls_ocsp_resp_init(&ocsp_resp); + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, + &status, NULL, NULL, NULL, &reason); - rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } + switch(status) { + case GNUTLS_OCSP_CERT_GOOD: + break; - (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); + case GNUTLS_OCSP_CERT_REVOKED: { + const char *crl_reason; - switch(status) { - case GNUTLS_OCSP_CERT_GOOD: + switch(reason) { + default: + case GNUTLS_X509_CRLREASON_UNSPECIFIED: + crl_reason = "unspecified reason"; break; - case GNUTLS_OCSP_CERT_REVOKED: { - const char *crl_reason; - - switch(reason) { - default: - case GNUTLS_X509_CRLREASON_UNSPECIFIED: - crl_reason = "unspecified reason"; - break; - - case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: - crl_reason = "private key compromised"; - break; - - case GNUTLS_X509_CRLREASON_CACOMPROMISE: - crl_reason = "CA compromised"; - break; - - case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: - crl_reason = "affiliation has changed"; - break; + case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: + crl_reason = "private key compromised"; + break; - case GNUTLS_X509_CRLREASON_SUPERSEDED: - crl_reason = "certificate superseded"; - break; + case GNUTLS_X509_CRLREASON_CACOMPROMISE: + crl_reason = "CA compromised"; + break; - case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: - crl_reason = "operation has ceased"; - break; + case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: + crl_reason = "affiliation has changed"; + break; - case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: - crl_reason = "certificate is on hold"; - break; + case GNUTLS_X509_CRLREASON_SUPERSEDED: + crl_reason = "certificate superseded"; + break; - case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: - crl_reason = "will be removed from delta CRL"; - break; + case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: + crl_reason = "operation has ceased"; + break; - case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: - crl_reason = "privilege withdrawn"; - break; + case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: + crl_reason = "certificate is on hold"; + break; - case GNUTLS_X509_CRLREASON_AACOMPROMISE: - crl_reason = "AA compromised"; - break; - } + case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: + crl_reason = "will be removed from delta CRL"; + break; - failf(data, "Server certificate was revoked: %s", crl_reason); + case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: + crl_reason = "privilege withdrawn"; break; - } - default: - case GNUTLS_OCSP_CERT_UNKNOWN: - failf(data, "Server certificate status is unknown"); + case GNUTLS_X509_CRLREASON_AACOMPROMISE: + crl_reason = "AA compromised"; break; } - gnutls_ocsp_resp_deinit(ocsp_resp); + failf(data, "Server certificate was revoked: %s", crl_reason); + break; + } - return CURLE_SSL_INVALIDCERTSTATUS; + default: + case GNUTLS_OCSP_CERT_UNKNOWN: + failf(data, "Server certificate status is unknown"); + break; } - else - infof(data, " server certificate status verification OK"); + + gnutls_ocsp_resp_deinit(ocsp_resp); + if(status != GNUTLS_OCSP_CERT_GOOD) + return CURLE_SSL_INVALIDCERTSTATUS; } else infof(data, " server certificate status verification SKIPPED"); @@ -1258,7 +1435,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_x509_crt_init(&x509_issuer); issuerp = load_file(config->issuercert); gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); gnutls_x509_crt_deinit(x509_issuer); unload_file(issuerp); if(rc <= 0) { @@ -1287,9 +1464,15 @@ Curl_gtls_verifyserver(struct Curl_easy *data, in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on failure. */ - rc = gnutls_x509_crt_check_hostname(x509_cert, peer->hostname); + + /* This function does not handle trailing dots, so if we have an SNI name + use that and fallback to the hostname only if there is no SNI (like for + IP addresses) */ + rc = (int)gnutls_x509_crt_check_hostname(x509_cert, + peer->sni ? peer->sni : + peer->hostname); #if GNUTLS_VERSION_NUMBER < 0x030306 - /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP + /* Before 3.3.6, gnutls_x509_crt_check_hostname() did not check IP addresses. */ if(!rc) { #ifdef USE_IPV6 @@ -1315,7 +1498,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, size_t certaddrlen = sizeof(certaddr); int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, &certaddrlen, NULL); - /* If this happens, it wasn't an IP address. */ + /* If this happens, it was not an IP address. */ if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) continue; if(ret < 0) @@ -1333,7 +1516,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, if(!rc) { if(config->verifyhost) { failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certname, peer->dispname); + "target hostname '%s'", certname, peer->dispname); gnutls_x509_crt_deinit(x509_cert); return CURLE_PEER_FAILED_VERIFICATION; } @@ -1422,7 +1605,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, /* public key algorithm's parameters */ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); infof(data, " certificate public key: %s", - gnutls_pk_algorithm_get_name(algo)); + gnutls_pk_algorithm_get_name((gnutls_pk_algorithm_t)algo)); /* version of the X.509 certificate. */ infof(data, " certificate version: #%d", @@ -1506,8 +1689,8 @@ out: */ /* We use connssl->connecting_state to keep track of the connection status; there are three states: 'ssl_connect_1' (not started yet or complete), - 'ssl_connect_2_reading' (waiting for data from server), and - 'ssl_connect_2_writing' (waiting to be able to write). + 'ssl_connect_2' (doing handshake with the server), and + 'ssl_connect_3' (verifying and getting stats). */ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, @@ -1516,7 +1699,7 @@ gtls_connect_common(struct Curl_cfilter *cf, bool *done) { struct ssl_connect_data *connssl = cf->ctx; - int rc; + CURLcode rc; CURLcode result = CURLE_OK; /* Initiate the connection, if not already done */ @@ -1595,143 +1778,146 @@ static bool gtls_data_pending(struct Curl_cfilter *cf, static ssize_t gtls_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, - size_t len, + const void *buf, + size_t blen, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; ssize_t rc; + size_t nwritten, total_written = 0; (void)data; DEBUGASSERT(backend); - backend->gtls.io_result = CURLE_OK; - rc = gnutls_record_send(backend->gtls.session, mem, len); + while(blen) { + backend->gtls.io_result = CURLE_OK; + rc = gnutls_record_send(backend->gtls.session, buf, blen); - if(rc < 0) { - *curlcode = (rc == GNUTLS_E_AGAIN)? - CURLE_AGAIN : - (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR); + if(rc < 0) { + if(total_written && (rc == GNUTLS_E_AGAIN)) { + *curlcode = CURLE_OK; + rc = (ssize_t)total_written; + goto out; + } + *curlcode = (rc == GNUTLS_E_AGAIN)? + CURLE_AGAIN : + (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR); - rc = -1; + rc = -1; + goto out; + } + nwritten = (size_t)rc; + total_written += nwritten; + DEBUGASSERT(nwritten <= blen); + buf = (char *)buf + nwritten; + blen -= nwritten; } + rc = total_written; +out: return rc; } -static void gtls_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +/* + * This function is called to shut down the SSL layer but keep the + * socket open (CCC - Clear Command Channel) + */ +static CURLcode gtls_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; + char buf[1024]; + CURLcode result = CURLE_OK; + ssize_t nread; + size_t i; - (void) data; DEBUGASSERT(backend); + if(!backend->gtls.session || cf->shutdown) { + *done = TRUE; + goto out; + } - if(backend->gtls.session) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)gnutls_record_recv(backend->gtls.session, buf, sizeof(buf)); - gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR); - gnutls_deinit(backend->gtls.session); - backend->gtls.session = NULL; + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->gtls.sent_shutdown) { + /* do this only once */ + backend->gtls.sent_shutdown = TRUE; + if(send_shutdown) { + int ret = gnutls_bye(backend->gtls.session, GNUTLS_SHUT_RDWR); + if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { + CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye EAGAIN"); + connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; + backend->gtls.sent_shutdown = FALSE; + result = CURLE_OK; + goto out; + } + if(ret != GNUTLS_E_SUCCESS) { + CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye error: '%s'(%d)", + gnutls_strerror((int)ret), (int)ret); + result = CURLE_RECV_ERROR; + goto out; + } + } } - if(backend->gtls.cred) { - gnutls_certificate_free_credentials(backend->gtls.cred); - backend->gtls.cred = NULL; + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + nread = gnutls_record_recv(backend->gtls.session, buf, sizeof(buf)); + if(nread <= 0) + break; } -#ifdef USE_GNUTLS_SRP - if(backend->gtls.srp_client_cred) { - gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); - backend->gtls.srp_client_cred = NULL; + if(nread > 0) { + /* still data coming in? */ } -#endif + else if(nread == 0) { + /* We got the close notify alert and are done. */ + *done = TRUE; + } + else if((nread == GNUTLS_E_AGAIN) || (nread == GNUTLS_E_INTERRUPTED)) { + connssl->io_need = gnutls_record_get_direction(backend->gtls.session)? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; + } + else { + CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", + gnutls_strerror((int)nread), (int)nread); + result = CURLE_RECV_ERROR; + } + +out: + cf->shutdown = (result || *done); + return result; } -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int gtls_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void gtls_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - int retval = 0; + (void) data; DEBUGASSERT(backend); - -#ifndef CURL_DISABLE_FTP - /* This has only been tested on the proftpd server, and the mod_tls code - sends a close notify alert without waiting for a close notify alert in - response. Thus we wait for a close notify alert from the server, but - we do not send one. Let's hope other servers do the same... */ - - if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR); -#endif - + CURL_TRC_CF(data, cf, "close"); if(backend->gtls.session) { - ssize_t result; - bool done = FALSE; - char buf[120]; - - while(!done && !connssl->peer_closed) { - int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); - if(what > 0) { - /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - result = gnutls_record_recv(backend->gtls.session, - buf, sizeof(buf)); - switch(result) { - case 0: - /* This is the expected response. There was no data but only - the close notify alert */ - done = TRUE; - break; - case GNUTLS_E_AGAIN: - case GNUTLS_E_INTERRUPTED: - infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED"); - break; - default: - retval = -1; - done = TRUE; - break; - } - } - else if(0 == what) { - /* timeout */ - failf(data, "SSL shutdown timeout"); - done = TRUE; - } - else { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - retval = -1; - done = TRUE; - } - } gnutls_deinit(backend->gtls.session); + backend->gtls.session = NULL; + } + if(backend->gtls.shared_creds) { + Curl_gtls_shared_creds_free(&backend->gtls.shared_creds); } - gnutls_certificate_free_credentials(backend->gtls.cred); - #ifdef USE_GNUTLS_SRP - { - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - if(ssl_config->primary.username) - gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); + if(backend->gtls.srp_client_cred) { + gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred); + backend->gtls.srp_client_cred = NULL; } #endif - - backend->gtls.cred = NULL; - backend->gtls.session = NULL; - - return retval; } static ssize_t gtls_recv(struct Curl_cfilter *cf, @@ -1831,7 +2017,8 @@ const struct Curl_ssl Curl_ssl_gnutls = { SSLSUPP_CA_PATH | SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CA_CACHE, sizeof(struct gtls_ssl_backend_data), @@ -1856,9 +2043,9 @@ const struct Curl_ssl Curl_ssl_gnutls = { gtls_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ gtls_recv, /* recv decrypted data */ gtls_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif /* USE_GNUTLS */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index f8388b37b..b0ca55bfb 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h @@ -30,6 +30,7 @@ #ifdef USE_GNUTLS #include +#include "timeval.h" #ifdef HAVE_GNUTLS_SRP /* the function exists */ @@ -45,14 +46,27 @@ struct ssl_primary_config; struct ssl_config_data; struct ssl_peer; +struct gtls_shared_creds { + gnutls_certificate_credentials_t creds; + char *CAfile; /* CAfile path used to generate X509 store */ + struct curltime time; /* when the shared creds was created */ + size_t refcount; + BIT(trust_setup); /* x509 anchors + CRLs have been set up */ +}; + +CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, + struct gtls_shared_creds **pcreds); +CURLcode Curl_gtls_shared_creds_up_ref(struct gtls_shared_creds *creds); +void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds); + struct gtls_ctx { gnutls_session_t session; - gnutls_certificate_credentials_t cred; + struct gtls_shared_creds *shared_creds; #ifdef USE_GNUTLS_SRP gnutls_srp_client_credentials_t srp_client_cred; #endif CURLcode io_result; /* result of last IO cfilter operation */ - BIT(trust_setup); /* x509 anchors + CRLs have been set up */ + BIT(sent_shutdown); }; typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c index 2726dca7f..e46439a5e 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.c +++ b/Utilities/cmcurl/lib/vtls/hostcheck.c @@ -62,7 +62,7 @@ static bool pmatch(const char *hostname, size_t hostlen, * We use the matching rule described in RFC6125, section 6.4.3. * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 * - * In addition: ignore trailing dots in the host names and wildcards, so that + * In addition: ignore trailing dots in the hostnames and wildcards, so that * the names are used normalized. This is what the browsers do. * * Do not allow wildcard matching on IP numbers. There are apparently diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.h b/Utilities/cmcurl/lib/vtls/hostcheck.h index 22a1ac2e5..6b4e37964 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.h +++ b/Utilities/cmcurl/lib/vtls/hostcheck.h @@ -26,7 +26,7 @@ #include -/* returns TRUE if there's a match */ +/* returns TRUE if there is a match */ bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, const char *hostname, size_t hostlen); diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index ec0b10dd9..1c00cbe1d 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -75,6 +75,7 @@ #include "mbedtls.h" #include "vtls.h" #include "vtls_int.h" +#include "x509asn1.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ #include "select.h" @@ -110,6 +111,8 @@ struct mbed_ssl_backend_data { const char *protocols[3]; #endif int *ciphersuites; + BIT(initialized); /* mbedtls_ssl_context is initialized */ + BIT(sent_shutdown); }; /* apply threading? */ @@ -197,7 +200,8 @@ static int mbedtls_bio_cf_write(void *bio, if(!data) return 0; - nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); + nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, FALSE, + &result); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d", blen, nwritten, result); if(nwritten < 0 && CURLE_AGAIN == result) { @@ -246,8 +250,8 @@ static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = 1024, /* RSA min key len */ }; -/* See https://tls.mbed.org/discussions/generic/ - howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der +/* See https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/ + generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der */ #define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) #define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) @@ -255,138 +259,91 @@ static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = #define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) -#if MBEDTLS_VERSION_NUMBER >= 0x03020000 -static CURLcode mbedtls_version_from_curl( - mbedtls_ssl_protocol_version* mbedver, long version) +static CURLcode +mbed_set_ssl_version_min_max(struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + struct ssl_primary_config *conn_config) { - switch(version) { + /* TLS 1.0 and TLS 1.1 were dropped with mbedTLS 3.0.0 (2021). So, since + * then, and before the introduction of TLS 1.3 in 3.6.0 (2024), this + * function basically always sets TLS 1.2 as min/max, unless given + * unsupported option values. */ + +#if MBEDTLS_VERSION_NUMBER < 0x03020000 + int ver_min = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */ + int ver_max = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */ +#else + /* mbedTLS 3.2.0 (2022) introduced new methods for setting TLS version */ + mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2; + mbedtls_ssl_protocol_version ver_max = MBEDTLS_SSL_VERSION_TLS1_2; +#endif + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: +#if MBEDTLS_VERSION_NUMBER < 0x03000000 + case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: + ver_min = MBEDTLS_SSL_MINOR_VERSION_1; + break; case CURL_SSLVERSION_TLSv1_1: + ver_min = MBEDTLS_SSL_MINOR_VERSION_2; + break; +#else + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: +#endif case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_VERSION_TLS1_2; - return CURLE_OK; + /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */ + break; case CURL_SSLVERSION_TLSv1_3: #ifdef TLS13_SUPPORT - *mbedver = MBEDTLS_SSL_VERSION_TLS1_3; - return CURLE_OK; -#else + ver_min = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif + default: + failf(data, "mbedTLS: unsupported minimum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; } - return CURLE_SSL_CONNECT_ERROR; -} -#else -static CURLcode mbedtls_version_from_curl(int *mbedver, long version) -{ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#else - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_1; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_2; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#endif - - return CURLE_SSL_CONNECT_ERROR; -} -#endif - -static CURLcode -set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct mbed_ssl_backend_data *backend = - (struct mbed_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); -#if MBEDTLS_VERSION_NUMBER >= 0x03020000 - mbedtls_ssl_protocol_version mbedtls_ver_min = MBEDTLS_SSL_VERSION_TLS1_2; + switch(conn_config->version_max) { + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_TLSv1_3: #ifdef TLS13_SUPPORT - mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_3; -#else - mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_2; -#endif -#elif MBEDTLS_VERSION_NUMBER >= 0x03000000 - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3; -#else - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1; + ver_max = MBEDTLS_SSL_VERSION_TLS1_3; + break; #endif - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; - CURLcode result = CURLE_OK; - - DEBUGASSERT(backend); - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: -#ifdef TLS13_SUPPORT - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; + case CURL_SSLVERSION_MAX_TLSv1_2: + /* ver_max = MBEDTLS_SSL_VERSION_TLS1_2; */ + break; +#if MBEDTLS_VERSION_NUMBER < 0x03000000 + case CURL_SSLVERSION_MAX_TLSv1_1: + ver_max = MBEDTLS_SSL_MINOR_VERSION_2; + break; + case CURL_SSLVERSION_MAX_TLSv1_0: + ver_max = MBEDTLS_SSL_MINOR_VERSION_1; + break; #else - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; + case CURL_SSLVERSION_MAX_TLSv1_1: + case CURL_SSLVERSION_MAX_TLSv1_0: #endif - break; - } - - result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; + default: + failf(data, "mbedTLS: unsupported maximum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; } -#if MBEDTLS_VERSION_NUMBER >= 0x03020000 - mbedtls_ssl_conf_min_tls_version(&backend->config, mbedtls_ver_min); - mbedtls_ssl_conf_max_tls_version(&backend->config, mbedtls_ver_max); -#else +#if MBEDTLS_VERSION_NUMBER < 0x03020000 mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_min); + ver_min); mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_max); -#endif - -#ifdef TLS13_SUPPORT - if(mbedtls_ver_min == MBEDTLS_SSL_VERSION_TLS1_3) { - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED); - } - else { - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); - } + ver_max); #else - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_min_tls_version(&backend->config, ver_min); + mbedtls_ssl_conf_max_tls_version(&backend->config, ver_max); #endif - return result; + return CURLE_OK; } /* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental @@ -425,11 +382,13 @@ mbed_cipher_suite_walk_str(const char **str, const char **end) static CURLcode mbed_set_selected_ciphers(struct Curl_easy *data, struct mbed_ssl_backend_data *backend, - const char *ciphers) + const char *ciphers12, + const char *ciphers13) { + const char *ciphers = ciphers12; const int *supported; int *selected; - size_t supported_len, count = 0, i; + size_t supported_len, count = 0, default13_count = 0, i, j; const char *ptr, *end; supported = mbedtls_ssl_list_ciphersuites(); @@ -440,6 +399,26 @@ mbed_set_selected_ciphers(struct Curl_easy *data, if(!selected) return CURLE_OUT_OF_MEMORY; +#ifndef TLS13_SUPPORT + (void) ciphers13, (void) j; +#else + if(!ciphers13) { + /* Add default TLSv1.3 ciphers to selection */ + for(j = 0; j < supported_len; j++) { + uint16_t id = (uint16_t) supported[j]; + if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0) + continue; + + selected[count++] = id; + } + + default13_count = count; + } + else + ciphers = ciphers13; + +add_ciphers: +#endif for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { uint16_t id = mbed_cipher_suite_walk_str(&ptr, &end); @@ -459,14 +438,38 @@ mbed_set_selected_ciphers(struct Curl_easy *data, /* No duplicates allowed (so selected cannot overflow) */ for(i = 0; i < count && selected[i] != id; i++); if(i < count) { - infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); + if(i >= default13_count) + infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); continue; } selected[count++] = id; } +#ifdef TLS13_SUPPORT + if(ciphers == ciphers13 && ciphers12) { + ciphers = ciphers12; + goto add_ciphers; + } + + if(!ciphers12) { + /* Add default TLSv1.2 ciphers to selection */ + for(j = 0; j < supported_len; j++) { + uint16_t id = (uint16_t) supported[j]; + if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0) + continue; + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) + continue; + + selected[count++] = id; + } + } +#endif + selected[count] = 0; if(count == 0) { @@ -482,6 +485,80 @@ mbed_set_selected_ciphers(struct Curl_easy *data, return CURLE_OK; } +static void +mbed_dump_cert_info(struct Curl_easy *data, const mbedtls_x509_crt *crt) +{ +#if defined(CURL_DISABLE_VERBOSE_STRINGS) || \ + (MBEDTLS_VERSION_NUMBER >= 0x03000000 && defined(MBEDTLS_X509_REMOVE_INFO)) + (void) data, (void) crt; +#else + const size_t bufsize = 16384; + char *p, *buffer = malloc(bufsize); + + if(buffer && mbedtls_x509_crt_info(buffer, bufsize, " ", crt) > 0) { + infof(data, "Server certificate:"); + for(p = buffer; *p; p += *p != '\0') { + size_t s = strcspn(p, "\n"); + infof(data, "%.*s", (int) s, p); + p += s; + } + } + else + infof(data, "Unable to dump certificate information"); + + free(buffer); +#endif +} + +static void +mbed_extract_certinfo(struct Curl_easy *data, const mbedtls_x509_crt *crt) +{ + CURLcode result; + const mbedtls_x509_crt *cur; + int i; + + for(i = 0, cur = crt; cur; ++i, cur = cur->next); + result = Curl_ssl_init_certinfo(data, i); + + for(i = 0, cur = crt; result == CURLE_OK && cur; ++i, cur = cur->next) { + const char *beg = (const char *) cur->raw.p; + const char *end = beg + cur->raw.len; + result = Curl_extract_certinfo(data, i, beg, end); + } +} + +static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, + int depth, uint32_t *flags) +{ + struct Curl_cfilter *cf = (struct Curl_cfilter *) ptr; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + if(depth == 0) { + if(data->set.verbose) + mbed_dump_cert_info(data, crt); + if(data->set.ssl.certinfo) + mbed_extract_certinfo(data, crt); + } + + if(!conn_config->verifypeer) + *flags = 0; + else if(!conn_config->verifyhost) + *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; + + if(*flags) { +#if MBEDTLS_VERSION_NUMBER < 0x03000000 || !defined(MBEDTLS_X509_REMOVE_INFO) + char buf[128]; + mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); + failf(data, "mbedTLS: %s", buf); +#else + failf(data, "mbedTLS: cerificate verification error 0x%08x", *flags); +#endif + } + + return 0; +} + static CURLcode mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -504,6 +581,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) char errorbuf[128]; DEBUGASSERT(backend); + DEBUGASSERT(!backend->initialized); if((conn_config->version == CURL_SSLVERSION_SSLv2) || (conn_config->version == CURL_SSLVERSION_SSLv3)) { @@ -636,7 +714,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", + failf(data, "Error reading client cert data %s - mbedTLS: (-0x%04X) %s", ssl_config->key, -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } @@ -738,35 +816,22 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } + /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are + * disabled we clear the corresponding error flags in the verify callback + * function. That is also where we log verification errors. */ + mbedtls_ssl_conf_verify(&backend->config, mbed_verify_cb, cf); + mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_init(&backend->ssl); + backend->initialized = TRUE; /* new profile with RSA min key len = 1024 ... */ mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_fr); - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if MBEDTLS_VERSION_NUMBER < 0x03000000 - mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - MBEDTLS_SSL_MINOR_VERSION_1); - infof(data, "mbedTLS: Set min SSL version to TLS 1.0"); - break; -#endif - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(cf, data); - if(result != CURLE_OK) - return result; - break; - } - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } + ret = mbed_set_ssl_version_min_max(data, backend, conn_config); + if(ret != CURLE_OK) + return ret; mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, &backend->ctr_drbg); @@ -784,11 +849,20 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_bio_cf_read, NULL /* rev_timeout() */); +#ifndef TLS13_SUPPORT if(conn_config->cipher_list) { - ret = mbed_set_selected_ciphers(data, backend, conn_config->cipher_list); - if(ret) { + CURLcode result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, + NULL); +#else + if(conn_config->cipher_list || conn_config->cipher_list13) { + CURLcode result = mbed_set_selected_ciphers(data, backend, + conn_config->cipher_list, + conn_config->cipher_list13); +#endif + if(result != CURLE_OK) { failf(data, "mbedTLS: failed to set cipher suites"); - return ret; + return result; } } else { @@ -807,8 +881,8 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) MBEDTLS_SSL_SESSION_TICKETS_DISABLED); #endif - /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { void *old_session = NULL; Curl_ssl_sessionid_lock(data); @@ -854,7 +928,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) for(i = 0; i < connssl->alpn->count; ++i) { backend->protocols[i] = connssl->alpn->entries[i]; } - /* this function doesn't clone the protocols array, which is why we need + /* this function does not clone the protocols array, which is why we need to keep it around */ if(mbedtls_ssl_conf_alpn_protocols(&backend->config, &backend->protocols[0])) { @@ -880,11 +954,11 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* give application a chance to interfere with mbedTLS set up. */ if(data->set.ssl.fsslctx) { - ret = (*data->set.ssl.fsslctx)(data, &backend->config, - data->set.ssl.fsslctxp); - if(ret) { + CURLcode result = (*data->set.ssl.fsslctx)(data, &backend->config, + data->set.ssl.fsslctxp); + if(result != CURLE_OK) { failf(data, "error signaled by ssl ctx callback"); - return ret; + return result; } } @@ -900,10 +974,6 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const mbedtls_x509_crt *peercert; - char cipher_str[64]; - uint16_t cipher_id; #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: @@ -917,78 +987,52 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) ret = mbedtls_ssl_handshake(&backend->ssl); if(ret == MBEDTLS_ERR_SSL_WANT_READ) { - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - connssl->connecting_state = ssl_connect_2_writing; + connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } + else if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + failf(data, "peer certificate could not be verified"); + return CURLE_PEER_FAILED_VERIFICATION; + } else if(ret) { char errorbuf[128]; +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + CURL_TRC_CF(data, cf, "TLS version %04X", + mbedtls_ssl_get_version_number(&backend->ssl)); +#endif mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s", + failf(data, "ssl_handshake returned: (-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } - cipher_id = (uint16_t) - mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); - mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); - infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str); - - ret = mbedtls_ssl_get_verify_result(&backend->ssl); - - if(!conn_config->verifyhost) - /* Ignore hostname errors if verifyhost is disabled */ - ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; - - if(ret && conn_config->verifypeer) { - if(ret & MBEDTLS_X509_BADCERT_EXPIRED) - failf(data, "Cert verify failed: BADCERT_EXPIRED"); - - else if(ret & MBEDTLS_X509_BADCERT_REVOKED) - failf(data, "Cert verify failed: BADCERT_REVOKED"); - - else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) - failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); - - else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) - failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); - - else if(ret & MBEDTLS_X509_BADCERT_FUTURE) - failf(data, "Cert verify failed: BADCERT_FUTURE"); - - return CURLE_PEER_FAILED_VERIFICATION; +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + { + char cipher_str[64]; + uint16_t cipher_id; + cipher_id = (uint16_t) + mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); + mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true); + infof(data, "mbedTLS: %s Handshake complete, cipher is %s", + mbedtls_ssl_get_version(&backend->ssl), cipher_str); } - - peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); - - if(peercert && data->set.verbose) { -#ifndef MBEDTLS_X509_REMOVE_INFO - const size_t bufsize = 16384; - char *buffer = malloc(bufsize); - - if(!buffer) - return CURLE_OUT_OF_MEMORY; - - if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0) - infof(data, "Dumping cert info: %s", buffer); - else - infof(data, "Unable to dump certificate information"); - - free(buffer); #else - infof(data, "Unable to dump certificate information"); + infof(data, "mbedTLS: %s Handshake complete", + mbedtls_ssl_get_version(&backend->ssl)); #endif - } if(pinnedpubkey) { int size; CURLcode result; + const mbedtls_x509_crt *peercert; mbedtls_x509_crt *p = NULL; unsigned char *pubkey = NULL; + peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); #if MBEDTLS_VERSION_NUMBER == 0x03000000 if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) { @@ -1088,10 +1132,9 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.sessionid) { + if(ssl_config->primary.cache_session) { int ret; mbedtls_ssl_session *our_ssl_sessionid; - void *old_ssl_sessionid = NULL; our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); if(!our_ssl_sessionid) @@ -1108,15 +1151,11 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } - /* If there's already a matching session in the cache, delete it */ + /* If there is already a matching session in the cache, delete it */ Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - &old_ssl_sessionid, NULL)) - Curl_ssl_delsessionid(data, old_ssl_sessionid); - - retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer, - our_ssl_sessionid, 0, - mbedtls_session_free); + retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + mbedtls_session_free); Curl_ssl_sessionid_unlock(data); if(retcode) return retcode; @@ -1141,8 +1180,13 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ? - CURLE_AGAIN : CURLE_SEND_ERROR; + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", + len, -ret); + *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE) +#ifdef TLS13_SUPPORT + || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) +#endif + )? CURLE_AGAIN : CURLE_SEND_ERROR; ret = -1; } @@ -1154,33 +1198,120 @@ static void mbedtls_close_all(struct Curl_easy *data) (void)data; } -static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; - char buf[32]; + unsigned char buf[1024]; + CURLcode result = CURLE_OK; + int ret; + size_t i; - (void)data; DEBUGASSERT(backend); - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); + if(!backend->initialized || cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } - mbedtls_pk_free(&backend->pk); - mbedtls_x509_crt_free(&backend->clicert); - mbedtls_x509_crt_free(&backend->cacert); + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->sent_shutdown) { + /* do this only once */ + backend->sent_shutdown = TRUE; + if(send_shutdown) { + ret = mbedtls_ssl_close_notify(&backend->ssl); + switch(ret) { + case 0: /* we sent it, receive from the server */ + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: /* server also closed */ + *done = TRUE; + goto out; + case MBEDTLS_ERR_SSL_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; + goto out; + case MBEDTLS_ERR_SSL_WANT_WRITE: + connssl->io_need = CURL_SSL_IO_NEED_SEND; + goto out; + default: + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + result = CURLE_RECV_ERROR; + goto out; + } + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + for(i = 0; i < 10; ++i) { + ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); + /* This seems to be a bug in mbedTLS TLSv1.3 where it reports + * WANT_READ, but has not encountered an EAGAIN. */ + if(ret == MBEDTLS_ERR_SSL_WANT_READ) + ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf)); +#ifdef TLS13_SUPPORT + if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + continue; +#endif + if(ret <= 0) + break; + } + + if(ret > 0) { + /* still data coming in? */ + CURL_TRC_CF(data, cf, "mbedtls_shutdown, still getting data"); + } + else if(ret == 0 || (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)) { + /* We got the close notify alert and are done. */ + CURL_TRC_CF(data, cf, "mbedtls_shutdown done"); + *done = TRUE; + } + else if(ret == MBEDTLS_ERR_SSL_WANT_READ) { + CURL_TRC_CF(data, cf, "mbedtls_shutdown, need RECV"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + CURL_TRC_CF(data, cf, "mbedtls_shutdown, need SEND"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + } + else { + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + result = CURLE_RECV_ERROR; + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)connssl->backend; + + (void)data; + DEBUGASSERT(backend); + if(backend->initialized) { + mbedtls_pk_free(&backend->pk); + mbedtls_x509_crt_free(&backend->clicert); + mbedtls_x509_crt_free(&backend->cacert); #ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_free(&backend->crl); + mbedtls_x509_crl_free(&backend->crl); #endif - Curl_safefree(backend->ciphersuites); - mbedtls_ssl_config_free(&backend->config); - mbedtls_ssl_free(&backend->ssl); - mbedtls_ctr_drbg_free(&backend->ctr_drbg); + Curl_safefree(backend->ciphersuites); + mbedtls_ssl_config_free(&backend->config); + mbedtls_ssl_free(&backend->ssl); + mbedtls_ctr_drbg_free(&backend->ctr_drbg); #ifndef THREADING_SUPPORT - mbedtls_entropy_free(&backend->entropy); + mbedtls_entropy_free(&backend->entropy); #endif /* THREADING_SUPPORT */ + backend->initialized = FALSE; + } } static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1198,16 +1329,21 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); - if(ret <= 0) { + CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", + buffersize, -ret); if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) return 0; - *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) #ifdef TLS13_SUPPORT || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #endif ) ? CURLE_AGAIN : CURLE_RECV_ERROR; + if(*curlcode != CURLE_AGAIN) { + char errorbuf[128]; + mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); + } return -1; } @@ -1290,7 +1426,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ + /* Find out how much more time we are allowed */ timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -1303,9 +1439,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return retcode; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -1316,14 +1450,13 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking ? 0 : timeout_ms); @@ -1353,11 +1486,10 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, * ensuring that a client using select() or epoll() will always * have a valid fdset to wait on. */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; retcode = mbed_connect_step2(cf, data); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(retcode || + (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return retcode; } /* repeat step2 until all transactions are done. */ @@ -1474,9 +1606,14 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | + SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | SSLSUPP_SSL_CTX | - SSLSUPP_HTTPS_PROXY, +#ifdef TLS13_SUPPORT + SSLSUPP_TLS13_CIPHERSUITES | +#endif + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST, sizeof(struct mbed_ssl_backend_data), @@ -1484,7 +1621,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbedtls_cleanup, /* cleanup */ mbedtls_version, /* version */ Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ + mbedtls_shutdown, /* shutdown */ mbedtls_data_pending, /* data_pending */ mbedtls_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ @@ -1501,9 +1638,9 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbedtls_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ mbed_recv, /* recv decrypted data */ mbed_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif /* USE_MBEDTLS */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 7628e7999..303dc1d05 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -231,7 +231,7 @@ /* * Whether SSL_CTX_set1_curves_list is available. * OpenSSL: supported since 1.0.2, see - * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html + * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/ * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30) * LibreSSL: since 2.5.3 (April 12, 2017) */ @@ -256,13 +256,20 @@ #endif #endif +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef size_t numcert_t; +#else +typedef int numcert_t; +#endif +#define ossl_valsize_t numcert_t + #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* up2date versions of OpenSSL maintain reasonably secure defaults without * breaking compatibility, so it is better not to override the defaults in curl */ #define DEFAULT_CIPHER_SELECTION NULL #else -/* ... but it is not the case with old versions of OpenSSL */ +/* not the case with old versions of OpenSSL */ #define DEFAULT_CIPHER_SELECTION \ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" #endif @@ -302,10 +309,15 @@ typedef unsigned long sslerr_t; #define HAVE_SSL_X509_STORE_SHARE #endif -/* FIXME: On LCC <= 1.23, OpenSSL 2.0.0 may be - * found but does not seem to have X509_STORE_up_ref. */ +#if (OPENSSL_VERSION_NUMBER > 0x10100000L) /* OpenSSL > 1.1.0 */ +#define HAVE_SSL_X509_GET_SIGNATURE_NID +#endif + +/* FIXME: On LCC <= 1.23, OpenSSL 2.0.0 may be found but does not seem to + * have X509_STORE_up_ref or X509_get_signature_nid. */ #if defined(__LCC__) && defined(__EDG__) && (__LCC__ <= 123) #undef HAVE_SSL_X509_STORE_SHARE +#undef HAVE_SSL_X509_GET_SIGNATURE_NID #endif /* What API version do we use? */ @@ -315,37 +327,36 @@ typedef unsigned long sslerr_t; #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) #endif /* !LIBRESSL_VERSION_NUMBER */ -#if defined(HAVE_SSL_X509_STORE_SHARE) -struct multi_ssl_backend_data { - char *CAfile; /* CAfile path used to generate X509 store */ - X509_STORE *store; /* cached X509 store or NULL if none */ - struct curltime time; /* when the cached store was created */ -}; -#endif /* HAVE_SSL_X509_STORE_SHARE */ +static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl); -#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 CURLcode push_certinfo(struct Curl_easy *data, + BIO *mem, const char *label, int num) + WARN_UNUSED_RESULT; -static void pubkey_show(struct Curl_easy *data, - BIO *mem, - int num, - const char *type, - const char *name, - const BIGNUM *bn) +static CURLcode push_certinfo(struct Curl_easy *data, + BIO *mem, const char *label, int num) { char *ptr; + long len = BIO_get_mem_data(mem, &ptr); + CURLcode result = Curl_ssl_push_certinfo_len(data, num, label, ptr, len); + (void)BIO_reset(mem); + return result; +} + +static CURLcode pubkey_show(struct Curl_easy *data, + BIO *mem, + int num, + const char *type, + const char *name, + const BIGNUM *bn) +{ char namebuf[32]; msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); if(bn) BN_print(mem, bn); - push_certinfo(namebuf, num); + return push_certinfo(data, mem, namebuf, num); } #ifdef HAVE_OPAQUE_RSA_DSA_DH @@ -377,25 +388,26 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) return 0; } -static void X509V3_ext(struct Curl_easy *data, - int certnum, - CONST_EXTS STACK_OF(X509_EXTENSION) *exts) +static CURLcode X509V3_ext(struct Curl_easy *data, + int certnum, + CONST_EXTS STACK_OF(X509_EXTENSION) *exts) { int i; + CURLcode result = CURLE_OK; if((int)sk_X509_EXTENSION_num(exts) <= 0) /* no extensions, bail out */ - return; + return result; for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { ASN1_OBJECT *obj; - X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); + X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, (ossl_valsize_t)i); BUF_MEM *biomem; char namebuf[128]; BIO *bio_out = BIO_new(BIO_s_mem()); if(!bio_out) - return; + return result; obj = X509_EXTENSION_get_object(ext); @@ -405,19 +417,16 @@ static void X509V3_ext(struct Curl_easy *data, 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); + result = Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, + biomem->length); BIO_free(bio_out); + if(result) + break; } + return result; } -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) -typedef size_t numcert_t; -#else -typedef int numcert_t; -#endif - -CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) +static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) { CURLcode result; STACK_OF(X509) *sk; @@ -435,38 +444,43 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) numcerts = sk_X509_num(sk); result = Curl_ssl_init_certinfo(data, (int)numcerts); - if(result) { + if(result) return result; - } mem = BIO_new(BIO_s_mem()); - if(!mem) { - return CURLE_OUT_OF_MEMORY; - } + if(!mem) + result = CURLE_OUT_OF_MEMORY; - for(i = 0; i < (int)numcerts; i++) { + for(i = 0; !result && (i < (int)numcerts); i++) { ASN1_INTEGER *num; - X509 *x = sk_X509_value(sk, i); + X509 *x = sk_X509_value(sk, (ossl_valsize_t)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); + result = push_certinfo(data, mem, "Subject", i); + if(result) + break; X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE); - push_certinfo("Issuer", i); + result = push_certinfo(data, mem, "Issuer", i); + if(result) + break; BIO_printf(mem, "%lx", X509_get_version(x)); - push_certinfo("Version", i); + result = push_certinfo(data, mem, "Version", i); + if(result) + break; 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); + result = push_certinfo(data, mem, "Serial Number", i); + if(result) + break; #if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS) { @@ -479,7 +493,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) const ASN1_OBJECT *sigalgoid = NULL; X509_ALGOR_get0(&sigalgoid, NULL, NULL, sigalg); i2a_ASN1_OBJECT(mem, sigalgoid); - push_certinfo("Signature Algorithm", i); + result = push_certinfo(data, mem, "Signature Algorithm", i); + if(result) + break; } xpubkey = X509_get_X509_PUBKEY(x); @@ -487,11 +503,15 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey); if(pubkeyoid) { i2a_ASN1_OBJECT(mem, pubkeyoid); - push_certinfo("Public Key Algorithm", i); + result = push_certinfo(data, mem, "Public Key Algorithm", i); + if(result) + break; } } - X509V3_ext(data, i, X509_get0_extensions(x)); + result = X509V3_ext(data, i, X509_get0_extensions(x)); + if(result) + break; } #else { @@ -499,22 +519,32 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) X509_CINF *cinf = x->cert_info; i2a_ASN1_OBJECT(mem, cinf->signature->algorithm); - push_certinfo("Signature Algorithm", i); + result = push_certinfo(data, mem, "Signature Algorithm", i); + + if(!result) { + i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm); + result = push_certinfo(data, mem, "Public Key Algorithm", i); + } - i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm); - push_certinfo("Public Key Algorithm", i); + if(!result) + result = X509V3_ext(data, i, cinf->extensions); - X509V3_ext(data, i, cinf->extensions); + if(result) + break; psig = x->signature; } #endif ASN1_TIME_print(mem, X509_get0_notBefore(x)); - push_certinfo("Start date", i); + result = push_certinfo(data, mem, "Start date", i); + if(result) + break; ASN1_TIME_print(mem, X509_get0_notAfter(x)); - push_certinfo("Expire date", i); + result = push_certinfo(data, mem, "Expire date", i); + if(result) + break; pubkey = X509_get_pubkey(x); if(!pubkey) @@ -527,8 +557,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) pktype = pubkey->type; #endif switch(pktype) { - case EVP_PKEY_RSA: - { + case EVP_PKEY_RSA: { #ifndef HAVE_EVP_PKEY_GET_PARAMS RSA *rsa; #ifdef HAVE_OPAQUE_EVP_PKEY @@ -552,7 +581,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #else BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); #endif /* HAVE_OPAQUE_RSA_DSA_DH */ - push_certinfo("RSA Public Key", i); + result = push_certinfo(data, mem, "RSA Public Key", i); + if(result) + break; print_pubkey_BN(rsa, n, i); print_pubkey_BN(rsa, e, i); FREE_PKEY_PARAM_BIGNUM(n); @@ -600,8 +631,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #endif /* !OPENSSL_NO_DSA */ break; } - case EVP_PKEY_DH: - { + case EVP_PKEY_DH: { #ifndef HAVE_EVP_PKEY_GET_PARAMS DH *dh; #ifdef HAVE_OPAQUE_EVP_PKEY @@ -644,19 +674,25 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) EVP_PKEY_free(pubkey); } - if(psig) { + if(!result && psig) { for(j = 0; j < psig->length; j++) BIO_printf(mem, "%02x:", psig->data[j]); - push_certinfo("Signature", i); + result = push_certinfo(data, mem, "Signature", i); } - PEM_write_bio_X509(mem, x); - push_certinfo("Cert", i); + if(!result) { + PEM_write_bio_X509(mem, x); + result = push_certinfo(data, mem, "Cert", i); + } } BIO_free(mem); - return CURLE_OK; + if(result) + /* cleanup all leftovers */ + Curl_ssl_free_certinfo(data); + + return result; } #endif /* quiche or OpenSSL */ @@ -735,7 +771,11 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) CURLcode result = CURLE_SEND_ERROR; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + if(blen < 0) + return 0; + + nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE, + &result); CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d", blen, (int)nwritten, result); BIO_clear_retry_flags(bio); @@ -760,8 +800,10 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) /* OpenSSL catches this case, so should we. */ if(!buf) return 0; + if(blen < 0) + return 0; - nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + nread = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &result); CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d", blen, (int)nread, result); BIO_clear_retry_flags(bio); @@ -852,7 +894,7 @@ static void ossl_keylog_callback(const SSL *ssl, const char *line) #else /* * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the - * OpenSSL being used doesn't have native support for doing that. + * OpenSSL being used does not have native support for doing that. */ static void ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) @@ -868,7 +910,7 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !(defined(LIBRESSL_VERSION_NUMBER) && \ LIBRESSL_VERSION_NUMBER < 0x20700000L) - /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that + /* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that * we have a valid SSL context if we have a non-NULL session. */ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); master_key_length = (int) @@ -971,7 +1013,7 @@ static int passwd_callback(char *buf, int num, int encrypting, { DEBUGASSERT(0 == encrypting); - if(!encrypting) { + if(!encrypting && num >= 0) { int klen = curlx_uztosi(strlen((char *)global_passwd)); if(num > klen) { memcpy(buf, global_passwd, klen + 1); @@ -1007,12 +1049,6 @@ static CURLcode ossl_seed(struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; #else -#ifdef RANDOM_FILE - RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH); - if(rand_enough()) - return CURLE_OK; -#endif - /* fallback to a custom seeding of the PRNG using a hash based on a current time */ do { @@ -1022,13 +1058,12 @@ static CURLcode ossl_seed(struct Curl_easy *data) for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { struct curltime tv = Curl_now(); Curl_wait_ms(1); - tv.tv_sec *= i + 1; - tv.tv_usec *= (unsigned int)i + 2; - tv.tv_sec ^= ((Curl_now().tv_sec + Curl_now().tv_usec) * - (i + 3)) << 8; - tv.tv_usec ^= (unsigned int) ((Curl_now().tv_sec + - Curl_now().tv_usec) * - (i + 4)) << 16; + tv.tv_sec *= (time_t)i + 1; + tv.tv_usec *= (int)i + 2; + tv.tv_sec ^= ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) * + (time_t)(i + 3)) << 8; + tv.tv_usec ^= (int) ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) * + (time_t)(i + 4)) << 16; memcpy(&randb[i * sizeof(struct curltime)], &tv, sizeof(struct curltime)); } @@ -1041,7 +1076,7 @@ static CURLcode ossl_seed(struct Curl_easy *data) fname[0] = 0; /* blank it first */ RAND_file_name(fname, sizeof(fname)); if(fname[0]) { - /* we got a file name to try */ + /* we got a filename to try */ RAND_load_file(fname, RAND_LOAD_LENGTH); if(rand_enough()) return CURLE_OK; @@ -1060,7 +1095,7 @@ static CURLcode ossl_seed(struct Curl_easy *data) #ifndef SSL_FILETYPE_PKCS12 #define SSL_FILETYPE_PKCS12 43 #endif -static int do_file_type(const char *type) +static int ossl_do_file_type(const char *type) { if(!type || !type[0]) return SSL_FILETYPE_PEM; @@ -1282,7 +1317,7 @@ int cert_stuff(struct Curl_easy *data, char error_buffer[256]; bool check_privkey = TRUE; - int file_type = do_file_type(cert_type); + int file_type = ossl_do_file_type(cert_type); if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) { SSL *ssl; @@ -1377,7 +1412,7 @@ int cert_stuff(struct Curl_easy *data, } if(!params.cert) { - failf(data, "ssl engine didn't initialized the certificate " + failf(data, "ssl engine did not initialized the certificate " "properly."); return 0; } @@ -1388,10 +1423,10 @@ int cert_stuff(struct Curl_easy *data, sizeof(error_buffer))); return 0; } - X509_free(params.cert); /* we don't need the handle any more... */ + X509_free(params.cert); /* we do not need the handle any more... */ } else { - failf(data, "crypto engine not set, can't load certificate"); + failf(data, "crypto engine not set, cannot load certificate"); return 0; } } @@ -1487,7 +1522,7 @@ int cert_stuff(struct Curl_easy *data, * Note that sk_X509_pop() is used below to make sure the cert is * removed from the stack properly before getting passed to * SSL_CTX_add_extra_chain_cert(), which takes ownership. Previously - * we used sk_X509_value() instead, but then we'd clean it in the + * we used sk_X509_value() instead, but then we would clean it in the * subsequent sk_X509_pop_free() call. */ X509 *x = sk_X509_pop(ca); @@ -1523,7 +1558,7 @@ fail: key_blob = cert_blob; } else - file_type = do_file_type(key_type); + file_type = ossl_do_file_type(key_type); switch(file_type) { case SSL_FILETYPE_PEM: @@ -1580,10 +1615,10 @@ fail: EVP_PKEY_free(priv_key); return 0; } - EVP_PKEY_free(priv_key); /* we don't need the handle any more... */ + EVP_PKEY_free(priv_key); /* we do not need the handle any more... */ } else { - failf(data, "crypto engine not set, can't load private key"); + failf(data, "crypto engine not set, cannot load private key"); return 0; } } @@ -1622,8 +1657,8 @@ fail: #if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ !defined(OPENSSL_NO_DEPRECATED_3_0) { - /* If RSA is used, don't check the private key if its flags indicate - * it doesn't support it. */ + /* If RSA is used, do not check the private key if its flags indicate + * it does not support it. */ EVP_PKEY *priv_key = SSL_get_privatekey(ssl); int pktype; #ifdef HAVE_OPAQUE_EVP_PKEY @@ -1657,22 +1692,6 @@ fail: return 1; } -CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, SSL_CTX *ctx, - char *cert_file, - const struct curl_blob *cert_blob, - const char *cert_type, char *key_file, - const struct curl_blob *key_blob, - const char *key_type, char *key_passwd) -{ - int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file, - key_blob, key_type, key_passwd); - if(rv != 1) { - return CURLE_SSL_CERTPROBLEM; - } - - return CURLE_OK; -} - /* returns non-zero on failure */ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) { @@ -1689,7 +1708,7 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) if((size_t)biomem->length < size) size = biomem->length; else - size--; /* don't overwrite the buffer end */ + size--; /* do not overwrite the buffer end */ memcpy(buf, biomem->data, size); buf[size] = 0; @@ -1881,210 +1900,146 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } -static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode ossl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + char buf[1024]; + int nread = -1, err; + unsigned long sslerr; + size_t i; - (void)data; DEBUGASSERT(octx); + if(!octx->ssl || cf->shutdown) { + *done = TRUE; + goto out; + } - if(octx->ssl) { - /* Send the TLS shutdown if we are still connected *and* if - * the peer did not already close the connection. */ - if(cf->next && cf->next->connected && !connssl->peer_closed) { - char buf[1024]; - int nread, err; - long sslerr; - - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - ERR_clear_error(); + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + if(!(SSL_get_shutdown(octx->ssl) & SSL_SENT_SHUTDOWN)) { + /* We have not started the shutdown from our side yet. Check + * if the server already sent us one. */ + ERR_clear_error(); + for(i = 0; i < 10; ++i) { nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); - err = SSL_get_error(octx->ssl, nread); - if(!nread && err == SSL_ERROR_ZERO_RETURN) { - CURLcode result; - ssize_t n; - size_t blen = sizeof(buf); - CURL_TRC_CF(data, cf, "peer has shutdown TLS"); - /* SSL_read() will not longer touch the socket, let's receive - * directly from the next filter to see if the underlying - * connection has also been closed. */ - n = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - if(!n) { - connssl->peer_closed = TRUE; - CURL_TRC_CF(data, cf, "peer closed connection"); - } - } - ERR_clear_error(); - if(connssl->peer_closed) { - /* As the peer closed, we do not expect it to read anything more we - * may send. It may be harmful, leading to TCP RST and delaying - * a lingering close. Just leave. */ - CURL_TRC_CF(data, cf, "not from sending TLS shutdown on " - "connection closed by peer"); - } - else if(SSL_shutdown(octx->ssl) == 1) { - CURL_TRC_CF(data, cf, "SSL shutdown finished"); + CURL_TRC_CF(data, cf, "SSL shutdown not sent, read -> %d", nread); + if(nread <= 0) + break; + } + err = SSL_get_error(octx->ssl, nread); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + bool input_pending; + /* Yes, it did. */ + if(!send_shutdown) { + CURL_TRC_CF(data, cf, "SSL shutdown received, not sending"); + *done = TRUE; + goto out; } - else { - nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); - err = SSL_get_error(octx->ssl, nread); - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - case SSL_ERROR_ZERO_RETURN: /* no more data */ - CURL_TRC_CF(data, cf, "SSL shutdown, EOF from server"); - break; - case SSL_ERROR_WANT_READ: - /* SSL has send its notify and now wants to read the reply - * from the server. We are not really interested in that. */ - CURL_TRC_CF(data, cf, "SSL shutdown sent"); - break; - case SSL_ERROR_WANT_WRITE: - CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); - break; - default: - sslerr = ERR_get_error(); - CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s', errno %d", - (sslerr ? - ossl_strerror(sslerr, buf, sizeof(buf)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); - break; - } + else if(!cf->next->cft->is_alive(cf->next, data, &input_pending)) { + /* Server closed the connection after its closy notify. It + * seems not interested to see our close notify, so do not + * send it. We are done. */ + connssl->peer_closed = TRUE; + CURL_TRC_CF(data, cf, "peer closed connection"); + *done = TRUE; + goto out; } - - ERR_clear_error(); - SSL_set_connect_state(octx->ssl); } + } - SSL_free(octx->ssl); - octx->ssl = NULL; + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + if(send_shutdown) { + ERR_clear_error(); + if(SSL_shutdown(octx->ssl) == 1) { + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + *done = TRUE; + goto out; + } + if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, nread)) { + CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + goto out; + } + /* Having sent the close notify, we use SSL_read() to get the + * missing close notify from the server. */ } - if(octx->ssl_ctx) { - SSL_CTX_free(octx->ssl_ctx); - octx->ssl_ctx = NULL; - octx->x509_store_setup = FALSE; + + for(i = 0; i < 10; ++i) { + ERR_clear_error(); + nread = SSL_read(octx->ssl, buf, (int)sizeof(buf)); + CURL_TRC_CF(data, cf, "SSL shutdown read -> %d", nread); + if(nread <= 0) + break; } - if(octx->bio_method) { - ossl_bio_cf_method_free(octx->bio_method); - octx->bio_method = NULL; + err = SSL_get_error(octx->ssl, nread); + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); + *done = TRUE; + break; + case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + break; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + break; + default: + /* Server seems to have closed the connection without sending us + * a close notify. */ + sslerr = ERR_get_error(); + CURL_TRC_CF(data, cf, "SSL shutdown, ignore recv error: '%s', errno %d", + (sslerr ? + ossl_strerror(sslerr, buf, sizeof(buf)) : + SSL_ERROR_to_str(err)), + SOCKERRNO); + *done = TRUE; + result = CURLE_OK; + break; } + +out: + cf->shutdown = (result || *done); + return result; } -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int ossl_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - int retval = 0; struct ssl_connect_data *connssl = cf->ctx; - char buf[256]; /* We will use this for the OpenSSL error buffer, so it has - to be at least 256 bytes long. */ - unsigned long sslerror; - int nread; - int buffsize; - int err; - bool done = FALSE; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; - int loop = 10; + (void)data; DEBUGASSERT(octx); -#ifndef CURL_DISABLE_FTP - /* This has only been tested on the proftpd server, and the mod_tls code - sends a close notify alert without waiting for a close notify alert in - response. Thus we wait for a close notify alert from the server, but - we do not send one. Let's hope other servers do the same... */ - - if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(octx->ssl); -#endif - if(octx->ssl) { - buffsize = (int)sizeof(buf); - while(!done && loop--) { - int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); - if(what > 0) { - ERR_clear_error(); - - /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - nread = SSL_read(octx->ssl, buf, buffsize); - err = SSL_get_error(octx->ssl, nread); - - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - case SSL_ERROR_ZERO_RETURN: /* no more data */ - /* This is the expected response. There was no data but only - the close notify alert */ - done = TRUE; - break; - case SSL_ERROR_WANT_READ: - /* there's data pending, re-invoke SSL_read() */ - infof(data, "SSL_ERROR_WANT_READ"); - break; - case SSL_ERROR_WANT_WRITE: - /* SSL wants a write. Really odd. Let's bail out. */ - infof(data, "SSL_ERROR_WANT_WRITE"); - done = TRUE; - break; - default: - /* openssl/ssl.h says "look at error stack/return value/errno" */ - sslerror = ERR_get_error(); - failf(data, OSSL_PACKAGE " SSL_read on shutdown: %s, errno %d", - (sslerror ? - ossl_strerror(sslerror, buf, sizeof(buf)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); - done = TRUE; - break; - } - } - else if(0 == what) { - /* timeout */ - failf(data, "SSL shutdown timeout"); - done = TRUE; - } - else { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - retval = -1; - done = TRUE; - } - } /* while()-loop for the select() */ - - if(data->set.verbose) { -#ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(octx->ssl)) { - case SSL_SENT_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN"); - break; - case SSL_RECEIVED_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN"); - break; - case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|" - "SSL_RECEIVED__SHUTDOWN"); - break; - } -#endif - } - SSL_free(octx->ssl); octx->ssl = NULL; } - return retval; + if(octx->ssl_ctx) { + SSL_CTX_free(octx->ssl_ctx); + octx->ssl_ctx = NULL; + octx->x509_store_setup = FALSE; + } + if(octx->bio_method) { + ossl_bio_cf_method_free(octx->bio_method); + octx->bio_method = NULL; + } } static void ossl_session_free(void *sessionid, size_t idsize) { /* free the ID */ (void)idsize; - SSL_SESSION_free(sessionid); + free(sessionid); } /* @@ -2115,7 +2070,7 @@ static void ossl_close_all(struct Curl_easy *data) /* ====================================================== */ /* - * Match subjectAltName against the host name. + * Match subjectAltName against the hostname. */ static bool subj_alt_hostcheck(struct Curl_easy *data, const char *match_pattern, @@ -2145,7 +2100,7 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, Certification Authorities are encouraged to use the dNSName instead. Matching is performed using the matching rules specified by - [RFC2459]. If more than one identity of a given type is present in + [RFC2459]. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.) Names may contain the wildcard character * which is considered to match any single domain name @@ -2158,8 +2113,9 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, This function is now used from ngtcp2 (QUIC) as well. */ -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - struct ssl_peer *peer, X509 *server_cert) +static CURLcode ossl_verifyhost(struct Curl_easy *data, + struct connectdata *conn, + struct ssl_peer *peer, X509 *server_cert) { bool matched = FALSE; int target; /* target type, GEN_DNS or GEN_IPADD */ @@ -2216,7 +2172,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, bool ipmatched = FALSE; /* get amount of alternatives, RFC2459 claims there MUST be at least - one, but we don't depend on it... */ + one, but we do not depend on it... */ numalts = sk_GENERAL_NAME_num(altnames); /* loop through all alternatives - until a dnsmatch */ @@ -2237,7 +2193,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, switch(target) { case GEN_DNS: /* name/pattern comparison */ - /* The OpenSSL man page explicitly says: "In general it cannot be + /* The OpenSSL manpage explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null terminated or does not contain embedded nulls." But also that "The actual format of the data will depend on the actual string @@ -2247,7 +2203,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, is always null-terminated. */ if((altlen == strlen(altptr)) && - /* if this isn't true, there was an embedded zero in the name + /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ subj_alt_hostcheck(data, altptr, altlen, peer->hostname, hostlen, @@ -2279,7 +2235,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, /* an alternative name matched */ ; else if(dNSName || iPAddress) { - const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "host name" : + const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : (peer->type == CURL_SSL_PEER_IPV4) ? "ipv4 address" : "ipv6 address"; infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); @@ -2350,7 +2306,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, else if(!Curl_cert_hostcheck((const char *)peer_CN, peerlen, peer->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", peer_CN, peer->dispname); + "target hostname '%s'", peer_CN, peer->dispname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2366,9 +2322,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) static CURLcode verifystatus(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct ossl_ctx *octx) { - struct ssl_connect_data *connssl = cf->ctx; int i, ocsp_status; #if defined(OPENSSL_IS_AWSLC) const uint8_t *status; @@ -2381,7 +2337,6 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; - struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; X509 *cert; OCSP_CERTID *id = NULL; int cert_status, crl_reason; @@ -2389,9 +2344,10 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, int ret; long len; + (void)cf; DEBUGASSERT(octx); - len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status); + len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status); if(!status) { failf(data, "No OCSP response received"); @@ -2433,8 +2389,8 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, (defined(LIBRESSL_VERSION_NUMBER) && \ LIBRESSL_VERSION_NUMBER <= 0x2040200fL)) /* The authorized responder cert in the OCSP response MUST be signed by the - peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert, - no problem, but if it's an intermediate cert OpenSSL has a bug where it + peer cert's issuer (see RFC6960 section 4.2.2.2). If that is a root cert, + no problem, but if it is an intermediate cert OpenSSL has a bug where it expects this issuer to be present in the chain embedded in the OCSP response. So we add it if necessary. */ @@ -2472,7 +2428,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, } for(i = 0; i < (int)sk_X509_num(ch); i++) { - X509 *issuer = sk_X509_value(ch, i); + X509 *issuer = sk_X509_value(ch, (ossl_valsize_t)i); if(X509_check_issued(issuer, cert) == X509_V_OK) { id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); break; @@ -2533,7 +2489,7 @@ end: #endif /* USE_OPENSSL */ -/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions +/* The SSL_CTRL_SET_MSG_CALLBACK does not exist in ancient OpenSSL versions and thus this cannot be done there. */ #ifdef SSL_CTRL_SET_MSG_CALLBACK @@ -2718,7 +2674,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, ssl_ver >>= 8; /* check the upper 8 bits only below */ - /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL + /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL * always pass-up content-type as 0. But the interesting message-type * is at 'buf[0]'. */ @@ -2813,7 +2769,7 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) } /* CURL_SSLVERSION_DEFAULT means that no option was selected. - We don't want to pass 0 to SSL_CTX_set_min_proto_version as + We do not want to pass 0 to SSL_CTX_set_min_proto_version as it would enable all versions down to the lowest supported by the library. So we skip this, and stay with the library default @@ -2825,7 +2781,7 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) } /* ... then, TLS max version */ - curl_ssl_version_max = conn_config->version_max; + curl_ssl_version_max = (long)conn_config->version_max; /* convert curl max SSL version option to OpenSSL constant */ switch(curl_ssl_version_max) { @@ -2866,6 +2822,9 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) typedef uint32_t ctx_option_t; #elif OPENSSL_VERSION_NUMBER >= 0x30000000L typedef uint64_t ctx_option_t; +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) +typedef unsigned long ctx_option_t; #else typedef long ctx_option_t; #endif @@ -2873,14 +2832,14 @@ typedef long ctx_option_t; #if !defined(HAS_MODERN_SET_PROTO_VER) static CURLcode ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; - (void) data; /* In case it's unused. */ + (void) data; /* In case it is unused. */ switch(ssl_version) { case CURL_SSLVERSION_TLSv1_3: @@ -2953,42 +2912,44 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, SSL_SESSION *session) { const struct ssl_config_data *config; - bool isproxy; - bool added = FALSE; + CURLcode result = CURLE_OK; + size_t der_session_size; + unsigned char *der_session_buf; + unsigned char *der_session_ptr; if(!cf || !data) goto out; - isproxy = Curl_ssl_cf_is_proxy(cf); - config = Curl_ssl_cf_get_config(cf, data); - if(config->primary.sessionid) { - bool incache; - void *old_session = NULL; + if(config->primary.cache_session) { - Curl_ssl_sessionid_lock(data); - if(isproxy) - incache = FALSE; - else - incache = !(Curl_ssl_getsessionid(cf, data, peer, - &old_session, NULL)); - if(incache && (old_session != session)) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_session); - incache = FALSE; + der_session_size = i2d_SSL_SESSION(session, NULL); + if(der_session_size == 0) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - if(!incache) { - added = TRUE; - Curl_ssl_addsessionid(cf, data, peer, session, 0, ossl_session_free); + der_session_buf = der_session_ptr = malloc(der_session_size); + if(!der_session_buf) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + der_session_size = i2d_SSL_SESSION(session, &der_session_ptr); + if(der_session_size == 0) { + result = CURLE_OUT_OF_MEMORY; + free(der_session_buf); + goto out; } + + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, peer, der_session_buf, + der_session_size, ossl_session_free); Curl_ssl_sessionid_unlock(data); } out: - if(!added) - ossl_session_free(session, 0); - return CURLE_OK; + return result; } /* The "new session" callback must return zero if the session can be removed @@ -3004,7 +2965,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) connssl = cf? cf->ctx : NULL; data = connssl? CF_DATA_CURRENT(cf) : NULL; Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid); - return 1; + return 0; } static CURLcode load_cacert_from_memory(X509_STORE *store, @@ -3033,7 +2994,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, /* add each entry from PEM file to x509_store */ for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { - itmp = sk_X509_INFO_value(inf, i); + itmp = sk_X509_INFO_value(inf, (ossl_valsize_t)i); if(itmp->x509) { if(X509_STORE_add_cert(store, itmp->x509)) { ++count; @@ -3059,7 +3020,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, sk_X509_INFO_pop_free(inf, X509_INFO_free); BIO_free(cbio); - /* if we didn't end up importing anything, treat that as an error */ + /* if we did not end up importing anything, treat that as an error */ return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } @@ -3180,7 +3141,7 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, else continue; - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + x509 = d2i_X509(NULL, &encoded_cert, (long)pContext->cbCertEncoded); if(!x509) continue; @@ -3318,8 +3279,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, #ifdef CURL_CA_FALLBACK if(!ssl_cafile && !ssl_capath && !imported_native_ca && !imported_ca_info_blob) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ + /* verifying the peer without any CA certificates will not + work so use OpenSSL's built-in default as fallback */ X509_STORE_set_default_paths(store); } #endif @@ -3344,10 +3305,11 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, if(verifypeer) { /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. Newer versions of + problems with server-sent legacy intermediates. Newer versions of OpenSSL do alternate chain checking by default but we do not know how to determine that in a reliable manner. - https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + https://web.archive.org/web/20190422050538/ + rt.openssl.org/Ticket/Display.html?id=3621 */ #if defined(X509_V_FLAG_TRUSTED_FIRST) X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); @@ -3371,23 +3333,49 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, } #if defined(HAVE_SSL_X509_STORE_SHARE) -static bool cached_x509_store_expired(const struct Curl_easy *data, - const struct multi_ssl_backend_data *mb) + +/* key to use at `multi->proto_hash` */ +#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share" + +struct ossl_x509_share { + char *CAfile; /* CAfile path used to generate X509 store */ + X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +static void oss_x509_share_free(void *key, size_t key_len, void *p) { - const struct ssl_general_config *cfg = &data->set.general_ssl; - struct curltime now = Curl_now(); - timediff_t elapsed_ms = Curl_timediff(now, mb->time); - timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + struct ossl_x509_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_OSSL_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->store) { + X509_STORE_free(share->store); + } + free(share->CAfile); + free(share); +} - if(timeout_ms < 0) - return false; +static bool +cached_x509_store_expired(const struct Curl_easy *data, + const struct ossl_x509_share *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + if(cfg->ca_cache_timeout < 0) + return FALSE; + else { + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; - return elapsed_ms >= timeout_ms; + return elapsed_ms >= timeout_ms; + } } -static bool cached_x509_store_different( - struct Curl_cfilter *cf, - const struct multi_ssl_backend_data *mb) +static bool +cached_x509_store_different(struct Curl_cfilter *cf, + const struct ossl_x509_share *mb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!mb->CAfile || !conn_config->CAfile) @@ -3400,15 +3388,17 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct Curl_multi *multi = data->multi; + struct ossl_x509_share *share; X509_STORE *store = NULL; DEBUGASSERT(multi); - if(multi && - multi->ssl_backend_data && - multi->ssl_backend_data->store && - !cached_x509_store_expired(data, multi->ssl_backend_data) && - !cached_x509_store_different(cf, multi->ssl_backend_data)) { - store = multi->ssl_backend_data->store; + share = multi? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; + if(share && share->store && + !cached_x509_store_expired(data, share) && + !cached_x509_store_different(cf, share)) { + store = share->store; } return store; @@ -3420,20 +3410,28 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; - struct multi_ssl_backend_data *mbackend; + struct ossl_x509_share *share; DEBUGASSERT(multi); if(!multi) return; + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1); - if(!multi->ssl_backend_data) { - multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data)); - if(!multi->ssl_backend_data) + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) + return; + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_OSSL_X509_KEY, + sizeof(MPROTO_OSSL_X509_KEY)-1, + share, oss_x509_share_free)) { + free(share); return; + } } - mbackend = multi->ssl_backend_data; - if(X509_STORE_up_ref(store)) { char *CAfile = NULL; @@ -3445,14 +3443,14 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, } } - if(mbackend->store) { - X509_STORE_free(mbackend->store); - free(mbackend->CAfile); + if(share->store) { + X509_STORE_free(share->store); + free(share->CAfile); } - mbackend->time = Curl_now(); - mbackend->store = store; - mbackend->CAfile = CAfile; + share->time = Curl_now(); + share->store = store; + share->CAfile = CAfile; } } @@ -3467,7 +3465,7 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, bool cache_criteria_met; /* Consider the X509 store cacheable if it comes exclusively from a CAfile, - or no source is provided and we are falling back to openssl's built-in + or no source is provided and we are falling back to OpenSSL's built-in default. */ cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && conn_config->verifypeer && @@ -3517,18 +3515,17 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, const char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; ctx_option_t ctx_options = 0; - void *ssl_sessionid = NULL; + SSL_SESSION *ssl_session = NULL; + const unsigned char *der_sessionid = NULL; + size_t der_sessionid_size = 0; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const long int ssl_version = conn_config->version; + const long int ssl_version_min = conn_config->version; char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_cert_type = ssl_config->cert_type; const bool verifypeer = conn_config->verifypeer; char error_buffer[256]; -#ifdef USE_ECH - struct ssl_connect_data *connssl = cf->ctx; -#endif /* Make funny stuff to get random input */ result = ossl_seed(data); @@ -3539,8 +3536,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, switch(transport) { case TRNSPRT_TCP: - /* check to see if we've been told to use an explicit SSL/TLS version */ - switch(ssl_version) { + /* check to see if we have been told to use an explicit SSL/TLS version */ + switch(ssl_version_min) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: @@ -3566,11 +3563,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } break; case TRNSPRT_QUIC: - if((ssl_version != CURL_SSLVERSION_DEFAULT) && - (ssl_version < CURL_SSLVERSION_TLSv1_3)) { + if(conn_config->version_max && + (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) { failf(data, "QUIC needs at least TLS version 1.3"); return CURLE_SSL_CONNECT_ERROR; - } + } + #ifdef USE_OPENSSL_QUIC req_method = OSSL_QUIC_client_method(); #elif (OPENSSL_VERSION_NUMBER >= 0x10100000L) @@ -3589,7 +3587,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, octx->ssl_ctx = SSL_CTX_new(req_method); if(!octx->ssl_ctx) { - failf(data, "SSL: couldn't create a context: %s", + failf(data, "SSL: could not create a context: %s", ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } @@ -3610,12 +3608,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, /* OpenSSL contains code to work around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those - work-arounds. The man page for this option states that SSL_OP_ALL enables + work-arounds. The manpage for this option states that SSL_OP_ALL enables all the work-arounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." - The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to + The "-no_ticket" option was introduced in OpenSSL 0.9.8j. it is a flag to disable "rfc4507bis session ticket support". rfc4507bis was later turned into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077 @@ -3636,12 +3634,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, CVE-2010-4180 when using previous OpenSSL versions we no longer enable this option regardless of OpenSSL version and SSL_OP_ALL definition. - OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability - (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to - SSL_OP_ALL that _disables_ that work-around despite the fact that - SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to - keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit - must not be set. + OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability: + https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt. + In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around + despite the fact that SSL_OP_ALL is documented to do "rather harmless" + workarounds. In order to keep the secure work-around, the + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. */ ctx_options = SSL_OP_ALL; @@ -3656,17 +3654,17 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG /* mitigate CVE-2010-4180 */ - ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; + ctx_options &= ~(ctx_option_t)SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; #endif #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS /* unless the user explicitly asks to allow the protocol vulnerability we use the work-around */ if(!ssl_config->enable_beast) - ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif - switch(ssl_version) { + switch(ssl_version_min) { case CURL_SSLVERSION_SSLv2: case CURL_SSLVERSION_SSLv3: return CURLE_NOT_BUILT_IN; @@ -3699,6 +3697,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_CTX_set_options(octx->ssl_ctx, ctx_options); +#ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER + /* We do retry writes sometimes from another buffer address */ + SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + #ifdef HAS_ALPN if(alpn && alpn_len) { if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) { @@ -3768,7 +3771,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, infof(data, "Using TLS-SRP username: %s", ssl_username); if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) { - failf(data, "Unable to set SRP user name"); + failf(data, "Unable to set SRP username"); return CURLE_BAD_FUNCTION_ARGUMENT; } if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) { @@ -3801,7 +3804,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif if(cb_new_session) { - /* Enable the session cache because it's a prerequisite for the + /* Enable the session cache because it is a prerequisite for the * "new session" callback. Use the "external storage" mode to prevent * OpenSSL from creating an internal session cache. */ @@ -3837,7 +3840,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_free(octx->ssl); octx->ssl = SSL_new(octx->ssl_ctx); if(!octx->ssl) { - failf(data, "SSL: couldn't create a context (handle)"); + failf(data, "SSL: could not create a context (handle)"); return CURLE_OUT_OF_MEMORY; } @@ -3892,7 +3895,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ech_config_len = 2 * strlen(b64); result = Curl_base64_decode(b64, &ech_config, &ech_config_len); if(result || !ech_config) { - infof(data, "ECH: can't base64 decode ECHConfig from command line"); + infof(data, "ECH: cannot base64 decode ECHConfig from command line"); if(data->set.tls_ech & CURLECH_HARD) return result; } @@ -3926,7 +3929,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, else { struct Curl_dns_entry *dns = NULL; - dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port); + if(peer->hostname) + dns = Curl_fetch_addr(data, peer->hostname, peer->port); if(!dns) { infof(data, "ECH: requested but no DNS info available"); if(data->set.tls_ech & CURLECH_HARD) @@ -3956,7 +3960,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, # endif else { trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %ld", elen); + infof(data, "ECH: imported ECHConfigList of length %zu", elen); } } else { @@ -3964,20 +3968,20 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } - Curl_resolv_unlock(data, dns); + Curl_resolv_unlink(data, &dns); } } # ifdef OPENSSL_IS_BORINGSSL if(trying_ech_now && outername) { - infof(data, "ECH: setting public_name not supported with boringssl"); + infof(data, "ECH: setting public_name not supported with BoringSSL"); return CURLE_SSL_CONNECT_ERROR; } # else if(trying_ech_now && outername) { infof(data, "ECH: inner: '%s', outer: '%s'", - connssl->peer.hostname, outername); + peer->hostname ? peer->hostname : "NULL", outername); result = SSL_ech_set_server_names(octx->ssl, - connssl->peer.hostname, outername, + peer->hostname, outername, 0 /* do send outer */); if(result != 1) { infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); @@ -3987,7 +3991,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, # endif /* not BORING */ if(trying_ech_now && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { - infof(data, "ECH: Can't force TLSv1.3 [ERROR]"); + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); return CURLE_SSL_CONNECT_ERROR; } } @@ -3996,20 +4000,31 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif octx->reused_session = FALSE; - if(ssl_config->primary.sessionid && transport == TRNSPRT_TCP) { + if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid, + &der_sessionid_size)) { /* we got a session id, use it! */ - if(!SSL_set_session(octx->ssl, ssl_sessionid)) { - Curl_ssl_sessionid_unlock(data); - failf(data, "SSL: SSL_set_session failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; + ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, + (long)der_sessionid_size); + if(ssl_session) { + if(!SSL_set_session(octx->ssl, ssl_session)) { + Curl_ssl_sessionid_unlock(data); + SSL_SESSION_free(ssl_session); + failf(data, "SSL: SSL_set_session failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } + SSL_SESSION_free(ssl_session); + /* Informational message */ + infof(data, "SSL reusing session ID"); + octx->reused_session = TRUE; + } + else { + Curl_ssl_sessionid_unlock(data); + return CURLE_SSL_CONNECT_ERROR; } - /* Informational message */ - infof(data, "SSL reusing session ID"); - octx->reused_session = TRUE; } Curl_ssl_sessionid_unlock(data); } @@ -4057,7 +4072,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works * without backward compat quirks. Every call takes one reference, so we * up it and pass. SSL* then owns it and will free. - * We check on the function in configure, since libressl and friends + * We check on the function in configure, since LibreSSL and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); SSL_set0_rbio(octx->ssl, bio); @@ -4147,11 +4162,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state - || ssl_connect_2_reading == connssl->connecting_state - || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); DEBUGASSERT(octx); + connssl->io_need = CURL_SSL_IO_NEED_NONE; ERR_clear_error(); err = SSL_connect(octx->ssl); @@ -4166,14 +4180,11 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } #ifndef HAVE_KEYLOG_CALLBACK - if(Curl_tls_keylog_enabled()) { - /* If key logging is enabled, wait for the handshake to complete and then - * proceed with logging secrets (for TLS 1.2 or older). - */ - bool done = FALSE; - ossl_log_tls12_secret(octx->ssl, &done); - octx->keylog_done = done; - } + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + */ + if(Curl_tls_keylog_enabled() && !octx->keylog_done) + ossl_log_tls12_secret(octx->ssl, &octx->keylog_done); #endif /* 1 is fine @@ -4181,30 +4192,34 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { int detail = SSL_get_error(octx->ssl, err); + CURL_TRC_CF(data, cf, "SSL_connect() -> err=%d, detail=%d", err, detail); if(SSL_ERROR_WANT_READ == detail) { - connssl->connecting_state = ssl_connect_2_reading; + CURL_TRC_CF(data, cf, "SSL_connect() -> want recv"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } if(SSL_ERROR_WANT_WRITE == detail) { - connssl->connecting_state = ssl_connect_2_writing; + CURL_TRC_CF(data, cf, "SSL_connect() -> want send"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } #ifdef SSL_ERROR_WANT_ASYNC if(SSL_ERROR_WANT_ASYNC == detail) { + CURL_TRC_CF(data, cf, "SSL_connect() -> want async"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; connssl->connecting_state = ssl_connect_2; return CURLE_OK; } #endif #ifdef SSL_ERROR_WANT_RETRY_VERIFY if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { + CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; connssl->connecting_state = ssl_connect_2; return CURLE_OK; } #endif - if(octx->io_result == CURLE_AGAIN) { - return CURLE_OK; - } else { /* untreated error */ sslerr_t errdetail; @@ -4214,7 +4229,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, int lib; int reason; - /* the connection failed, we're not waiting for anything else. */ + /* the connection failed, we are not waiting for anything else. */ connssl->connecting_state = ssl_connect_2; /* Get the earliest error code from the thread's error queue and remove @@ -4275,7 +4290,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* detail is already set to the SSL error above */ - /* If we e.g. use SSLv2 request-method and the server doesn't like us + /* If we e.g. use SSLv2 request-method and the server does not like us * (RST connection, etc.), OpenSSL gives no explanation whatsoever and * the SO_ERROR is also lost. */ @@ -4301,7 +4316,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, int psigtype_nid = NID_undef; const char *negotiated_group_name = NULL; - /* we connected fine, we're not waiting for anything else. */ + /* we connected fine, we are not waiting for anything else. */ connssl->connecting_state = ssl_connect_3; #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) @@ -4414,7 +4429,7 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - /* if a path wasn't specified, don't pin */ + /* if a path was not specified, do not pin */ if(!pinnedpubkey) return CURLE_OK; @@ -4435,12 +4450,12 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, if(!buff1) break; /* failed */ - /* https://www.openssl.org/docs/crypto/d2i_X509.html */ + /* https://docs.openssl.org/master/man3/d2i_X509/ */ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); /* * These checks are verifying we got back the same values as when we - * sized the buffer. It's pretty weak since they should always be the + * sized the buffer. it is pretty weak since they should always be the * same. But it gives us something to test. */ if((len1 != len2) || !temp || ((temp - buff1) != len1)) @@ -4559,7 +4574,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, if(data->set.ssl.certinfo) /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, octx->ssl); + (void)ossl_certchain(data, octx->ssl); octx->server_cert = SSL_get1_peer_certificate(octx->ssl); if(!octx->server_cert) { @@ -4567,7 +4582,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, if(!strict) return CURLE_OK; - failf(data, "SSL: couldn't get peer certificate"); + failf(data, "SSL: could not get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } @@ -4596,7 +4611,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert); + result = ossl_verifyhost(data, conn, peer, octx->server_cert); if(result) { X509_free(octx->server_cert); octx->server_cert = NULL; @@ -4608,7 +4623,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, buffer, sizeof(buffer)); if(rc) { if(strict) - failf(data, "SSL: couldn't get X509-issuer name"); + failf(data, "SSL: could not get X509-issuer name"); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -4711,8 +4726,8 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus && !octx->reused_session) { - /* don't do this after Session ID reuse */ - result = verifystatus(cf, data); + /* do not do this after Session ID reuse */ + result = verifystatus(cf, data, octx); if(result) { /* when verifystatus failed, remove the session id from the cache again if present */ @@ -4737,7 +4752,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, #endif if(!strict) - /* when not strict, we don't bother about the verify cert problems */ + /* when not strict, we do not bother about the verify cert problems */ result = CURLE_OK; #ifndef CURL_DISABLE_PROXY @@ -4770,7 +4785,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, /* * We check certificates to authenticate the server; otherwise we risk - * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to + * man-in-the-middle attack; NEVERTHELESS, if we are told explicitly not to * verify the peer, ignore faults and failures from the server cert * operations. */ @@ -4792,6 +4807,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int what; + connssl->io_need = CURL_SSL_IO_NEED_NONE; /* check if the connection has already been established */ if(ssl_connection_complete == connssl->state) { *done = TRUE; @@ -4799,7 +4815,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ + /* Find out how much more time we are allowed */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -4813,9 +4829,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, goto out; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -4827,15 +4841,13 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, goto out; } - /* if ssl is expecting something, check if it's available. */ - if(!nonblocking && - (connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing)) { + /* if ssl is expecting something, check if it is available. */ + if(!nonblocking && connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout_ms); @@ -4861,10 +4873,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, * or epoll() will always have a valid fdset to wait on. */ result = ossl_connect_step2(cf, data); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) goto out; } /* repeat step2 until all transactions are done. */ @@ -4945,6 +4954,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, ERR_clear_error(); + connssl->io_need = CURL_SSL_IO_NEED_NONE; memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; rc = SSL_write(octx->ssl, mem, memlen); @@ -4953,10 +4963,11 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, switch(err) { case SSL_ERROR_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; case SSL_ERROR_WANT_WRITE: - /* The operation did not complete; the same TLS/SSL I/O function - should be called again later. This is basically an EWOULDBLOCK - equivalent. */ *curlcode = CURLE_AGAIN; rc = -1; goto out; @@ -5028,6 +5039,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, ERR_clear_error(); + connssl->io_need = CURL_SSL_IO_NEED_NONE; buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize); @@ -5046,15 +5058,18 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, connclose(conn, "TLS close_notify"); break; case SSL_ERROR_WANT_READ: + *curlcode = CURLE_AGAIN; + nread = -1; + goto out; case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ + connssl->io_need = CURL_SSL_IO_NEED_SEND; *curlcode = CURLE_AGAIN; nread = -1; goto out; default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ - /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ + /* https://docs.openssl.org/master/man3/ERR_get_error/ */ if(octx->io_result == CURLE_AGAIN) { *curlcode = CURLE_AGAIN; nread = -1; @@ -5081,7 +5096,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, /* For debug builds be a little stricter and error on any SSL_ERROR_SYSCALL. For example a server may have closed the connection abruptly without a close_notify alert. For compatibility with older - peers we don't do this by default. #4624 + peers we do not do this by default. #4624 We can use this to gauge how many users may be affected, and if it goes ok eventually transition to allow in dev and release with @@ -5110,12 +5125,97 @@ out: return nread; } +static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, + struct dynbuf *binding) +{ + /* required for X509_get_signature_nid support */ +#ifdef HAVE_SSL_X509_GET_SIGNATURE_NID + X509 *cert; + int algo_nid; + const EVP_MD *algo_type; + const char *algo_name; + unsigned int length; + unsigned char buf[EVP_MAX_MD_SIZE]; + + const char prefix[] = "tls-server-end-point:"; + struct connectdata *conn = data->conn; + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + struct ossl_ctx *octx = NULL; + + do { + const struct Curl_cftype *cft = cf->cft; + struct ssl_connect_data *connssl = cf->ctx; + + if(cft->name && !strcmp(cft->name, "SSL")) { + octx = (struct ossl_ctx *)connssl->backend; + break; + } + + if(cf->next) + cf = cf->next; + + } while(cf->next); + + if(!octx) { + failf(data, + "Failed to find SSL backend for endpoint"); + return CURLE_SSL_ENGINE_INITFAILED; + } + + cert = SSL_get1_peer_certificate(octx->ssl); + if(!cert) { + /* No server certificate, don't do channel binding */ + return CURLE_OK; + } + + if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) { + failf(data, + "Unable to find digest NID for certificate signature algorithm"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */ + if(algo_nid == NID_md5 || algo_nid == NID_sha1) { + algo_type = EVP_sha256(); + } + else { + algo_type = EVP_get_digestbynid(algo_nid); + if(!algo_type) { + algo_name = OBJ_nid2sn(algo_nid); + failf(data, "Could not find digest algorithm %s (NID %d)", + algo_name ? algo_name : "(null)", algo_nid); + return CURLE_SSL_INVALIDCERTSTATUS; + } + } + + if(!X509_digest(cert, algo_type, buf, &length)) { + failf(data, "X509_digest() failed"); + return CURLE_SSL_INVALIDCERTSTATUS; + } + + /* Append "tls-server-end-point:" */ + if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK) + return CURLE_OUT_OF_MEMORY; + /* Append digest */ + if(Curl_dyn_addn(binding, buf, length)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +#else + /* No X509_get_signature_nid support */ + (void)data; /* unused */ + (void)sockindex; /* unused */ + (void)binding; /* unused */ + return CURLE_OK; +#endif +} + static size_t ossl_version(char *buffer, size_t size) { #ifdef LIBRESSL_VERSION_NUMBER #ifdef HAVE_OPENSSL_VERSION char *p; - int count; + size_t count; const char *ver = OpenSSL_version(OPENSSL_VERSION); const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ if(strncasecompare(ver, expected, sizeof(expected) - 1)) { @@ -5197,14 +5297,14 @@ static CURLcode ossl_random(struct Curl_easy *data, int rc; if(data) { if(ossl_seed(data)) /* Initiate the seed if not already done */ - return CURLE_FAILED_INIT; /* couldn't seed for some reason */ + return CURLE_FAILED_INIT; /* could not seed for some reason */ } else { if(!rand_enough()) return CURLE_FAILED_INIT; } /* RAND_bytes() returns 1 on success, 0 otherwise. */ - rc = RAND_bytes(entropy, curlx_uztosi(length)); + rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length)); return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT); } @@ -5252,20 +5352,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, (void *)octx->ssl_ctx : (void *)octx->ssl; } -static void ossl_free_multi_ssl_backend_data( - struct multi_ssl_backend_data *mbackend) -{ -#if defined(HAVE_SSL_X509_STORE_SHARE) - if(mbackend->store) { - X509_STORE_free(mbackend->store); - } - free(mbackend->CAfile); - free(mbackend); -#else /* HAVE_SSL_X509_STORE_SHARE */ - (void)mbackend; -#endif /* HAVE_SSL_X509_STORE_SHARE */ -} - const struct Curl_ssl Curl_ssl_openssl = { { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ @@ -5280,7 +5366,9 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef USE_ECH SSLSUPP_ECH | #endif - SSLSUPP_HTTPS_PROXY, + SSLSUPP_CA_CACHE | + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST, sizeof(struct ossl_ctx), @@ -5309,9 +5397,9 @@ const struct Curl_ssl Curl_ssl_openssl = { #endif NULL, /* use of data in this connection */ NULL, /* remote of data from this connection */ - ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ ossl_recv, /* recv decrypted data */ ossl_send, /* send data to encrypt */ + ossl_get_channel_binding /* get_channel_binding */ }; #endif /* USE_OPENSSL */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index 55e06bda4..7aba947d1 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h @@ -45,8 +45,9 @@ struct ossl_ctx { BIO_METHOD *bio_method; CURLcode io_result; /* result of last BIO cfilter operation */ #ifndef HAVE_KEYLOG_CALLBACK - /* Set to true once a valid keylog entry has been created to avoid dupes. */ - BIT(keylog_done); + /* Set to true once a valid keylog entry has been created to avoid dupes. + This is a bool and not a bitfield because it is passed by address. */ + bool keylog_done; #endif BIT(x509_store_setup); /* x509 store has been set up */ BIT(reused_session); /* session-ID was reused for this */ @@ -73,19 +74,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - struct ssl_peer *peer, X509 *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; -CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, - SSL_CTX *ctx, char *cert_file, - const struct curl_blob *cert_blob, - const char *cert_type, char *key_file, - const struct curl_blob *key_blob, - const char *key_type, char *key_passwd); - -CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl); - /** * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and * easy handle `data`. Will allow reuse of a shared cache if suitable diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index 8b6588a37..02ed4ec45 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -41,6 +41,8 @@ #include "strerror.h" #include "multiif.h" #include "connect.h" /* for the connect timeout */ +#include "cipher_suite.h" +#include "rand.h" struct rustls_ssl_backend_data { @@ -48,6 +50,7 @@ struct rustls_ssl_backend_data struct rustls_connection *conn; size_t plain_out_buffered; BIT(data_in_pending); + BIT(sent_shutdown); }; /* For a given rustls_result error code, return the best-matching CURLcode. */ @@ -101,7 +104,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) } else if(nread == 0) connssl->peer_closed = TRUE; - *out_n = (int)nread; + *out_n = (uintptr_t)nread; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", len, nread, result); return ret; @@ -114,7 +117,8 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) CURLcode result; int ret = 0; ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, - (const char *)buf, len, &result); + (const char *)buf, len, FALSE, + &result); if(nwritten < 0) { nwritten = 0; if(CURLE_AGAIN == result) @@ -122,7 +126,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) else ret = EINVAL; } - *out_n = (int)nwritten; + *out_n = (uintptr_t)nwritten; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", len, nwritten, result); return ret; @@ -173,15 +177,15 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, /* * On each run: - * - Read a chunk of bytes from the socket into rustls' TLS input buffer. - * - Tell rustls to process any new packets. - * - Read out as many plaintext bytes from rustls as possible, until hitting + * - Read a chunk of bytes from the socket into Rustls' TLS input buffer. + * - Tell Rustls to process any new packets. + * - Read out as many plaintext bytes from Rustls as possible, until hitting * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. * - * It's okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it will copy bytes from the socket into rustls' TLS input - * buffer, and process packets, but won't consume bytes from rustls' plaintext - * output buffer. + * it is okay to call this function with plainbuf == NULL and plainlen == 0. In + * that case, it will copy bytes from the socket into Rustls' TLS input + * buffer, and process packets, but will not consume bytes from Rustls' + * plaintext output buffer. */ static ssize_t cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -212,21 +216,21 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } rresult = rustls_connection_read(rconn, - (uint8_t *)plainbuf + plain_bytes_copied, - plainlen - plain_bytes_copied, - &n); + (uint8_t *)plainbuf + plain_bytes_copied, + plainlen - plain_bytes_copied, + &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { backend->data_in_pending = FALSE; } else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { failf(data, "rustls: peer closed TCP connection " - "without first closing TLS connection"); + "without first closing TLS connection"); *err = CURLE_RECV_ERROR; nread = -1; goto out; } else if(rresult != RUSTLS_RESULT_OK) { - /* n always equals 0 in this case, don't need to check it */ + /* n always equals 0 in this case, do not need to check it */ char errorbuf[255]; size_t errorlen; rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); @@ -304,13 +308,13 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, /* * On each call: - * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). - * - Fully drain rustls' plaintext output buffer into the socket until + * - Copy `plainlen` bytes into Rustls' plaintext input buffer (if > 0). + * - Fully drain Rustls' plaintext output buffer into the socket until * we get either an error or EAGAIN/EWOULDBLOCK. * - * It's okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it won't read anything into rustls' plaintext input buffer. - * It will only drain rustls' plaintext output buffer into the socket. + * it is okay to call this function with plainbuf == NULL and plainlen == 0. + * In that case, it will not read anything into Rustls' plaintext input buffer. + * It will only drain Rustls' plaintext output buffer into the socket. */ static ssize_t cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -355,7 +359,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, } if(blen > 0) { - CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to rustls", blen); + CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen); rresult = rustls_connection_write(rconn, buf, blen, &plainwritten); if(rresult != RUSTLS_RESULT_OK) { rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); @@ -374,9 +378,9 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(*err) { if(CURLE_AGAIN == *err) { /* The TLS bytes may have been partially written, but we fail the - * complete send() and remember how much we already added to rustls. */ + * complete send() and remember how much we already added to Rustls. */ CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain" - " bytes already to rustls", blen); + " bytes already to Rustls", blen); backend->plain_out_buffered = plainwritten; if(nwritten) { *err = CURLE_OK; @@ -393,7 +397,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, return nwritten; } -/* A server certificate verify callback for rustls that always returns +/* A server certificate verify callback for Rustls that always returns RUSTLS_RESULT_OK, or in other words disable certificate verification. */ static uint32_t cr_verify_none(void *userdata UNUSED_PARAM, @@ -402,20 +406,119 @@ cr_verify_none(void *userdata UNUSED_PARAM, return RUSTLS_RESULT_OK; } -static bool -cr_hostname_is_ip(const char *hostname) +static int +read_file_into(const char *filename, + struct dynbuf *out) +{ + FILE *f = fopen(filename, FOPEN_READTEXT); + if(!f) { + return 0; + } + + while(!feof(f)) { + uint8_t buf[256]; + size_t rr = fread(buf, 1, sizeof(buf), f); + if(rr == 0 || + CURLE_OK != Curl_dyn_addn(out, buf, rr)) { + fclose(f); + return 0; + } + } + + return fclose(f) == 0; +} + +static void +cr_get_selected_ciphers(struct Curl_easy *data, + const char *ciphers12, + const char *ciphers13, + const struct rustls_supported_ciphersuite **selected, + size_t *selected_size) { - struct in_addr in; -#ifdef USE_IPV6 - struct in6_addr in6; - if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { - return true; + size_t supported_len = *selected_size; + size_t default_len = rustls_default_crypto_provider_ciphersuites_len(); + const struct rustls_supported_ciphersuite *entry; + const char *ciphers = ciphers12; + size_t count = 0, default13_count = 0, i, j; + const char *ptr, *end; + + DEBUGASSERT(default_len <= supported_len); + + if(!ciphers13) { + /* Add default TLSv1.3 ciphers to selection */ + for(j = 0; j < default_len; j++) { + entry = rustls_default_crypto_provider_ciphersuites_get(j); + if(rustls_supported_ciphersuite_protocol_version(entry) != + RUSTLS_TLS_VERSION_TLSV1_3) + continue; + + selected[count++] = entry; + } + + default13_count = count; + + if(!ciphers) + ciphers = ""; } -#endif /* USE_IPV6 */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { - return true; + else + ciphers = ciphers13; + +add_ciphers: + for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { + uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end); + + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < supported_len; i++) { + entry = rustls_default_crypto_provider_ciphersuites_get(i); + if(rustls_supported_ciphersuite_get_suite(entry) == id) + break; + } + if(i == supported_len) + id = 0; + } + if(!id) { + if(ptr[0] != '\0') + infof(data, "rustls: unknown cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != entry; i++); + if(i < count) { + if(i >= default13_count) + infof(data, "rustls: duplicate cipher in list: \"%.*s\"", + (int) (end - ptr), ptr); + continue; + } + + selected[count++] = entry; + } + + if(ciphers == ciphers13 && ciphers12) { + ciphers = ciphers12; + goto add_ciphers; + } + + if(!ciphers12) { + /* Add default TLSv1.2 ciphers to selection */ + for(j = 0; j < default_len; j++) { + entry = rustls_default_crypto_provider_ciphersuites_get(j); + if(rustls_supported_ciphersuite_protocol_version(entry) == + RUSTLS_TLS_VERSION_TLSV1_3) + continue; + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != entry; i++); + if(i < count) + continue; + + selected[count++] = entry; + } } - return false; + + *selected_size = count; } static CURLcode @@ -424,6 +527,8 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, { struct ssl_connect_data *connssl = cf->ctx; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct rustls_crypto_provider_builder *custom_provider_builder = NULL; + const struct rustls_crypto_provider *custom_provider = NULL; struct rustls_connection *rconn = NULL; struct rustls_client_config_builder *config_builder = NULL; const struct rustls_root_cert_store *roots = NULL; @@ -435,15 +540,113 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ (ca_info_blob ? NULL : conn_config->CAfile); const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->peer.hostname; char errorbuf[256]; size_t errorlen; - int result; + rustls_result result; DEBUGASSERT(backend); rconn = backend->conn; - config_builder = rustls_client_config_builder_new(); + { + uint16_t tls_versions[2] = { + RUSTLS_TLS_VERSION_TLSV1_2, + RUSTLS_TLS_VERSION_TLSV1_3, + }; + size_t tls_versions_len = 2; + const struct rustls_supported_ciphersuite **cipher_suites; + size_t cipher_suites_len = + rustls_default_crypto_provider_ciphersuites_len(); + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: + case CURL_SSLVERSION_TLSv1_0: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_2: + break; + case CURL_SSLVERSION_TLSv1_3: + tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3; + tls_versions_len = 1; + break; + default: + failf(data, "rustls: unsupported minimum TLS version value"); + return CURLE_SSL_ENGINE_INITFAILED; + } + + switch(conn_config->version_max) { + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_TLSv1_3: + break; + case CURL_SSLVERSION_MAX_TLSv1_2: + if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) { + tls_versions_len = 1; + break; + } + FALLTHROUGH(); + case CURL_SSLVERSION_MAX_TLSv1_1: + case CURL_SSLVERSION_MAX_TLSv1_0: + default: + failf(data, "rustls: unsupported maximum TLS version value"); + return CURLE_SSL_ENGINE_INITFAILED; + } + + cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len)); + if(!cipher_suites) + return CURLE_OUT_OF_MEMORY; + + cr_get_selected_ciphers(data, + conn_config->cipher_list, + conn_config->cipher_list13, + cipher_suites, &cipher_suites_len); + if(cipher_suites_len == 0) { + failf(data, "rustls: no supported cipher in list"); + free(cipher_suites); + return CURLE_SSL_CIPHER; + } + + result = rustls_crypto_provider_builder_new_from_default( + &custom_provider_builder); + if(result != RUSTLS_RESULT_OK) { + failf(data, + "rustls: failed to create crypto provider builder from default"); + return CURLE_SSL_ENGINE_INITFAILED; + } + + result = + rustls_crypto_provider_builder_set_cipher_suites( + custom_provider_builder, + cipher_suites, + cipher_suites_len); + if(result != RUSTLS_RESULT_OK) { + failf(data, + "rustls: failed to set ciphersuites for crypto provider builder"); + rustls_crypto_provider_builder_free(custom_provider_builder); + return CURLE_SSL_ENGINE_INITFAILED; + } + + result = rustls_crypto_provider_builder_build( + custom_provider_builder, &custom_provider); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to build custom crypto provider"); + rustls_crypto_provider_builder_free(custom_provider_builder); + return CURLE_SSL_ENGINE_INITFAILED; + } + + result = rustls_client_config_builder_new_custom(custom_provider, + tls_versions, + tls_versions_len, + &config_builder); + free(cipher_suites); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to create client config"); + return CURLE_SSL_ENGINE_INITFAILED; + } + } + + rustls_crypto_provider_builder_free(custom_provider_builder); + rustls_crypto_provider_free(custom_provider); + if(connssl->alpn) { struct alpn_proto_buf proto; rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; @@ -461,20 +664,12 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(!verifypeer) { rustls_client_config_builder_dangerous_set_certificate_verifier( config_builder, cr_verify_none); - /* rustls doesn't support IP addresses (as of 0.19.0), and will reject - * connections created with an IP address, even when certificate - * verification is turned off. Set a placeholder hostname and disable - * SNI. */ - if(cr_hostname_is_ip(hostname)) { - rustls_client_config_builder_set_enable_sni(config_builder, false); - hostname = "example.invalid"; - } } else if(ca_info_blob || ssl_cafile) { roots_builder = rustls_root_cert_store_builder_new(); if(ca_info_blob) { - /* Enable strict parsing only if verification isn't disabled. */ + /* Enable strict parsing only if verification is not disabled. */ result = rustls_root_cert_store_builder_add_pem(roots_builder, ca_info_blob->data, ca_info_blob->len, @@ -482,20 +677,18 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to parse trusted certificates from blob"); rustls_root_cert_store_builder_free(roots_builder); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); + rustls_client_config_builder_free(config_builder); return CURLE_SSL_CACERT_BADFILE; } } else if(ssl_cafile) { - /* Enable strict parsing only if verification isn't disabled. */ + /* Enable strict parsing only if verification is not disabled. */ result = rustls_root_cert_store_builder_load_roots_from_file( roots_builder, ssl_cafile, verifypeer); if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to load trusted certificates"); rustls_root_cert_store_builder_free(roots_builder); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); + rustls_client_config_builder_free(config_builder); return CURLE_SSL_CACERT_BADFILE; } } @@ -503,30 +696,60 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, result = rustls_root_cert_store_builder_build(roots_builder, &roots); rustls_root_cert_store_builder_free(roots_builder); if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to load trusted certificates"); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); + failf(data, "rustls: failed to build trusted root certificate store"); + rustls_client_config_builder_free(config_builder); return CURLE_SSL_CACERT_BADFILE; } verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots); + rustls_root_cert_store_free(roots); + + if(conn_config->CRLfile) { + struct dynbuf crl_contents; + Curl_dyn_init(&crl_contents, SIZE_MAX); + if(!read_file_into(conn_config->CRLfile, &crl_contents)) { + failf(data, "rustls: failed to read revocation list file"); + Curl_dyn_free(&crl_contents); + rustls_web_pki_server_cert_verifier_builder_free(verifier_builder); + return CURLE_SSL_CRL_BADFILE; + } + + result = rustls_web_pki_server_cert_verifier_builder_add_crl( + verifier_builder, + Curl_dyn_uptr(&crl_contents), + Curl_dyn_len(&crl_contents)); + Curl_dyn_free(&crl_contents); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to parse revocation list"); + rustls_web_pki_server_cert_verifier_builder_free(verifier_builder); + return CURLE_SSL_CRL_BADFILE; + } + } result = rustls_web_pki_server_cert_verifier_builder_build( verifier_builder, &server_cert_verifier); rustls_web_pki_server_cert_verifier_builder_free(verifier_builder); if(result != RUSTLS_RESULT_OK) { - failf(data, "rustls: failed to load trusted certificates"); + failf(data, "rustls: failed to build certificate verifier"); rustls_server_cert_verifier_free(server_cert_verifier); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); + rustls_client_config_builder_free(config_builder); return CURLE_SSL_CACERT_BADFILE; } rustls_client_config_builder_set_server_verifier(config_builder, server_cert_verifier); + rustls_server_cert_verifier_free(server_cert_verifier); + } + + result = rustls_client_config_builder_build( + config_builder, + &backend->config); + if(result != RUSTLS_RESULT_OK) { + failf(data, "rustls: failed to build client config"); + rustls_client_config_free(backend->config); + return CURLE_SSL_ENGINE_INITFAILED; } - backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); result = rustls_client_connection_new(backend->config, connssl->peer.hostname, &rconn); @@ -601,30 +824,44 @@ cr_connect_common(struct Curl_cfilter *cf, /* Read/write data until the handshake is done or the socket would block. */ for(;;) { /* - * Connection has been established according to rustls. Set send/recv + * Connection has been established according to Rustls. Set send/recv * handlers, and update the state machine. */ + connssl->io_need = CURL_SSL_IO_NEED_NONE; if(!rustls_connection_is_handshaking(rconn)) { - infof(data, "Done handshaking"); - /* rustls claims it is no longer handshaking *before* it has + /* Rustls claims it is no longer handshaking *before* it has * send its FINISHED message off. We attempt to let it write * one more time. Oh my. */ cr_set_negotiated_alpn(cf, data, rconn); cr_send(cf, data, NULL, 0, &tmperr); if(tmperr == CURLE_AGAIN) { - connssl->connecting_state = ssl_connect_2_writing; + connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } else if(tmperr != CURLE_OK) { return tmperr; } /* REALLY Done with the handshake. */ + { + uint16_t proto = rustls_connection_get_protocol_version(rconn); + uint16_t cipher = rustls_connection_get_negotiated_ciphersuite(rconn); + char buf[64] = ""; + const char *ver = "TLS version unknown"; + if(proto == RUSTLS_TLS_VERSION_TLSV1_3) + ver = "TLSv1.3"; + if(proto == RUSTLS_TLS_VERSION_TLSV1_2) + ver = "TLSv1.2"; + Curl_cipher_suite_get_str(cipher, buf, sizeof(buf), true); + infof(data, "rustls: handshake complete, %s, cipher: %s", + ver, buf); + } connssl->state = ssl_connection_complete; *done = TRUE; return CURLE_OK; } + connssl->connecting_state = ssl_connect_2; wants_read = rustls_connection_wants_read(rconn); wants_write = rustls_connection_wants_write(rconn) || backend->plain_out_buffered; @@ -632,8 +869,6 @@ cr_connect_common(struct Curl_cfilter *cf, writefd = wants_write?sockfd:CURL_SOCKET_BAD; readfd = wants_read?sockfd:CURL_SOCKET_BAD; - connssl->connecting_state = wants_write? - ssl_connect_2_writing : ssl_connect_2_reading; /* check allowed time left */ timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -653,14 +888,18 @@ cr_connect_common(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } if(blocking && 0 == what) { - failf(data, "rustls connection timeout after %" - CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout); + failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms", + socket_check_timeout); return CURLE_OPERATION_TIMEDOUT; } if(0 == what) { CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block", wants_read&&wants_write ? "writing and reading" : wants_write ? "writing" : "reading"); + if(wants_write) + connssl->io_need |= CURL_SSL_IO_NEED_SEND; + if(wants_read) + connssl->io_need |= CURL_SSL_IO_NEED_RECV; return CURLE_OK; } /* socket is readable or writable */ @@ -695,7 +934,7 @@ cr_connect_common(struct Curl_cfilter *cf, } /* We should never fall through the loop. We should return either because - the handshake is done or because we can't read/write without blocking. */ + the handshake is done or because we cannot read/write without blocking. */ DEBUGASSERT(false); } @@ -723,24 +962,85 @@ cr_get_internals(struct ssl_connect_data *connssl, return &backend->conn; } -static void -cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode +cr_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct rustls_ssl_backend_data *backend = (struct rustls_ssl_backend_data *)connssl->backend; - CURLcode tmperr = CURLE_OK; - ssize_t n = 0; + CURLcode result = CURLE_OK; + ssize_t nwritten, nread; + char buf[1024]; + size_t i; DEBUGASSERT(backend); - if(backend->conn && !connssl->peer_closed) { - CURL_TRC_CF(data, cf, "closing connection, send notify"); - rustls_connection_send_close_notify(backend->conn); - n = cr_send(cf, data, NULL, 0, &tmperr); - if(n < 0) { - failf(data, "rustls: error sending close_notify: %d", tmperr); + if(!backend->conn || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(!backend->sent_shutdown) { + /* do this only once */ + backend->sent_shutdown = TRUE; + if(send_shutdown) { + rustls_connection_send_close_notify(backend->conn); } + } + nwritten = cr_send(cf, data, NULL, 0, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; + } + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown send failed: %d", result); + goto out; + } + + for(i = 0; i < 10; ++i) { + nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result); + if(nread <= 0) + break; + } + + if(nread > 0) { + /* still data coming in? */ + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + } + else { + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + } + +out: + cf->shutdown = (result || *done); + return result; +} + +static void +cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct rustls_ssl_backend_data *backend = + (struct rustls_ssl_backend_data *)connssl->backend; + + (void)data; + DEBUGASSERT(backend); + if(backend->conn) { rustls_connection_free(backend->conn); backend->conn = NULL; } @@ -756,19 +1056,31 @@ static size_t cr_version(char *buffer, size_t size) return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); } +static CURLcode +cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length) +{ + rustls_result rresult = 0; + (void)data; + rresult = + rustls_default_crypto_provider_random(entropy, length); + return map_error(rresult); +} + const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, SSLSUPP_CAINFO_BLOB | /* supports */ - SSLSUPP_HTTPS_PROXY, + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST | + SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct rustls_ssl_backend_data), Curl_none_init, /* init */ Curl_none_cleanup, /* cleanup */ cr_version, /* version */ Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ + cr_shutdown, /* shutdown */ cr_data_pending, /* data_pending */ - Curl_none_random, /* random */ + cr_random, /* random */ Curl_none_cert_status_request, /* cert_status_request */ cr_connect_blocking, /* connect */ cr_connect_nonblocking, /* connect_nonblocking */ @@ -783,9 +1095,9 @@ const struct Curl_ssl Curl_ssl_rustls = { NULL, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ cr_recv, /* recv decrypted data */ cr_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif /* USE_RUSTLS */ diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 19cdc4b20..a9dcbe45a 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -34,7 +34,7 @@ #ifdef USE_SCHANNEL #ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." +# error "cannot compile SCHANNEL support without SSPI." #endif #include "schannel.h" @@ -171,7 +171,7 @@ schannel_set_ssl_version_min_max(DWORD *enabled_protocols, { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; + long ssl_version_max = (long)conn_config->version_max; long i = ssl_version; switch(ssl_version_max) { @@ -364,7 +364,7 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, if(!alg) alg = get_alg_id_by_name(startCur); if(alg) - algIds[algCount++] = alg; + algIds[algCount++] = (ALG_ID)alg; else if(!strncmp(startCur, "USE_STRONG_CRYPTO", sizeof("USE_STRONG_CRYPTO") - 1) || !strncmp(startCur, "SCH_USE_STRONG_CRYPTO", @@ -377,7 +377,7 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, startCur++; } schannel_cred->palgSupportedAlgs = algIds; - schannel_cred->cSupportedAlgs = algCount; + schannel_cred->cSupportedAlgs = (DWORD)algCount; return CURLE_OK; } @@ -513,7 +513,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, } if(!ssl_config->auto_client_cert) { - flags &= ~SCH_CRED_USE_DEFAULT_CREDS; + flags &= ~(DWORD)SCH_CRED_USE_DEFAULT_CREDS; flags |= SCH_CRED_NO_DEFAULT_CREDS; infof(data, "schannel: disabled automatic use of client certificate"); } @@ -950,7 +950,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, tls_parameters.pDisabledCrypto = crypto_settings; /* The number of blocked suites */ - tls_parameters.cDisabledCrypto = crypto_settings_idx; + tls_parameters.cDisabledCrypto = (DWORD)crypto_settings_idx; credentials.pTlsParameters = &tls_parameters; credentials.cTlsParameters = 1; @@ -968,7 +968,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, #endif sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &credentials, NULL, NULL, &backend->cred->cred_handle, @@ -976,7 +976,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, } else { /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS - doesn't document it, currently Schannel will not negotiate TLS 1.3 when + does not document it, currently Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */ ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; @@ -1015,7 +1015,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, #endif sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, + Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, &backend->cred->cred_handle, @@ -1083,7 +1083,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef HAS_ALPN /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. - Also it doesn't seem to be supported for Wine, see curl bug #983. */ + Also it does not seem to be supported for WINE, see curl bug #983. */ backend->use_alpn = connssl->alpn && !GetProcAddress(GetModuleHandle(TEXT("ntdll")), "wine_get_version") && @@ -1095,11 +1095,11 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef _WIN32_WCE #ifdef HAS_MANUAL_VERIFY_API - /* certificate validation on CE doesn't seem to work right; we'll + /* certificate validation on CE does not seem to work right; we will * do it following a more manual process. */ backend->use_manual_cred_validation = true; #else -#error "compiler too old to support requisite manual cert verify for Win CE" +#error "compiler too old to support Windows CE requisite manual cert verify" #endif #else #ifdef HAS_MANUAL_VERIFY_API @@ -1127,7 +1127,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) backend->cred = NULL; /* check for an existing reusable credential handle */ - if(ssl_config->primary.sessionid) { + if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, (void **)&old_cred, NULL)) { @@ -1241,11 +1241,11 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Schannel InitializeSecurityContext: https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - At the moment we don't pass inbuf unless we're using ALPN since we only - use it for that, and Wine (for which we currently disable ALPN) is giving + At the moment we do not pass inbuf unless we are using ALPN since we only + use it for that, and WINE (for which we currently disable ALPN) is giving us problems with inbuf regardless. https://github.com/curl/curl/issues/983 */ - sspi_status = s_pSecFn->InitializeSecurityContext( + sspi_status = Curl_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, NULL, backend->cred->sni_hostname, backend->req_flags, 0, 0, (backend->use_alpn ? &inbuf_desc : NULL), @@ -1287,9 +1287,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* send initial handshake data which is now stored in output buffer */ written = Curl_conn_cf_send(cf->next, data, - outbuf.pvBuffer, outbuf.cbBuffer, + outbuf.pvBuffer, outbuf.cbBuffer, FALSE, &result); - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); + Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send initial handshake data: " "sent %zd of %lu bytes", written, outbuf.cbBuffer); @@ -1332,7 +1332,8 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); - doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; + doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? FALSE : TRUE; + connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", @@ -1393,8 +1394,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) backend->encdata_offset, &result); if(result == CURLE_AGAIN) { - if(connssl->connecting_state != ssl_connect_2_writing) - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: failed to receive handshake, " "need more data")); return CURLE_OK; @@ -1436,7 +1436,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, backend->encdata_offset); - sspi_status = s_pSecFn->InitializeSecurityContext( + sspi_status = Curl_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, backend->cred->sni_hostname, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL, @@ -1448,7 +1448,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* check if the handshake was incomplete */ if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { backend->encdata_is_incomplete = true; - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: received incomplete message, need more data")); return CURLE_OK; @@ -1460,7 +1460,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - connssl->connecting_state = ssl_connect_2_writing; + connssl->io_need = CURL_SSL_IO_NEED_SEND; DEBUGF(infof(data, "schannel: a client certificate has been requested")); return CURLE_OK; @@ -1477,7 +1477,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* send handshake token to server */ written = Curl_conn_cf_send(cf->next, data, outbuf[i].pvBuffer, outbuf[i].cbBuffer, - &result); + FALSE, &result); if((result != CURLE_OK) || (outbuf[i].cbBuffer != (size_t) written)) { failf(data, "schannel: failed to send next handshake data: " @@ -1488,7 +1488,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* free obsolete buffer */ if(outbuf[i].pvBuffer) { - s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); + Curl_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); } } } @@ -1531,7 +1531,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: - 1) If we're renegotiating a connection and the handshake is already + 1) If we are renegotiating a connection and the handshake is already complete (from the server perspective), it can encrypted app data (not handshake data) in an extra buffer at this point. 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a @@ -1560,7 +1560,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* check if the handshake needs to be continued */ if(sspi_status == SEC_I_CONTINUE_NEEDED) { - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } @@ -1593,7 +1593,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* Verify the hostname manually when certificate verification is disabled, - because in that case Schannel won't verify it. */ + because in that case Schannel will not verify it. */ if(!conn_config->verifypeer && conn_config->verifyhost) return Curl_verify_host(cf, data); @@ -1684,7 +1684,7 @@ static void schannel_session_free(void *sessionid, size_t idsize) if(cred) { cred->refcount--; if(cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + Curl_pSecFn->FreeCredentialsHandle(&cred->cred_handle); curlx_unicodefree(cred->sni_hostname); #ifdef HAS_CLIENT_CERT_PATH if(cred->client_cert_store) { @@ -1739,7 +1739,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef HAS_ALPN if(backend->use_alpn) { sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result); @@ -1772,40 +1772,22 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* save the current session data for possible reuse */ - if(ssl_config->primary.sessionid) { - bool incache; - struct Curl_schannel_cred *old_cred = NULL; - + if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&old_cred, NULL)); - if(incache) { - if(old_cred != backend->cred) { - DEBUGF(infof(data, - "schannel: old credential handle is stale, removing")); - /* we're not taking old_cred ownership here, no refcount++ is needed */ - Curl_ssl_delsessionid(data, (void *)old_cred); - incache = FALSE; - } - } - if(!incache) { - /* Up ref count since call takes ownership */ - backend->cred->refcount++; - result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred, - sizeof(struct Curl_schannel_cred), - schannel_session_free); - if(result) { - Curl_ssl_sessionid_unlock(data); - return result; - } - } + /* Up ref count since call takes ownership */ + backend->cred->refcount++; + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, backend->cred, + sizeof(struct Curl_schannel_cred), + schannel_session_free); Curl_ssl_sessionid_unlock(data); + if(result) + return result; } if(data->set.ssl.certinfo) { int certs_count = 0; sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context); @@ -1853,7 +1835,7 @@ schannel_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_1 == connssl->connecting_state) { - /* check out how much more time we're allowed */ + /* check out how much more time we are allowed */ timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -1867,11 +1849,9 @@ schannel_connect_common(struct Curl_cfilter *cf, return result; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { - /* check out how much more time we're allowed */ + /* check out how much more time we are allowed */ timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -1880,14 +1860,13 @@ schannel_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd : CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd : CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking ? 0 : timeout_ms); @@ -1918,10 +1897,7 @@ schannel_connect_common(struct Curl_cfilter *cf, * have a valid fdset to wait on. */ result = schannel_connect_step2(cf, data); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return result; } /* repeat step2 until all transactions are done. */ @@ -1979,7 +1955,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* check if the maximum stream sizes were queried */ if(backend->stream_sizes.cbMaximumMessage == 0) { - sspi_status = s_pSecFn->QueryContextAttributes( + sspi_status = Curl_pSecFn->QueryContextAttributes( &backend->ctxt->ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &backend->stream_sizes); @@ -2018,7 +1994,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, memcpy(outbuf[1].pvBuffer, buf, len); /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ - sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, + sspi_status = Curl_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, &outbuf_desc, 0); /* check if the message was encrypted */ @@ -2029,10 +2005,10 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; /* - It's important to send the full message which includes the header, - encrypted payload, and trailer. Until the client receives all the + it is important to send the full message which includes the header, + encrypted payload, and trailer. Until the client receives all the data a coherent message has not been delivered and the client - can't read any of it. + cannot read any of it. If we wanted to buffer the unwritten encrypted bytes, we would tell the client that all data it has requested to be sent has been @@ -2078,7 +2054,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, this_write = Curl_conn_cf_send(cf->next, data, ptr + written, len - written, - &result); + FALSE, &result); if(result == CURLE_AGAIN) continue; else if(result != CURLE_OK) { @@ -2129,8 +2105,9 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); /**************************************************************************** - * Don't return or set backend->recv_unrecoverable_err unless in the cleanup. - * The pattern for return error is set *err, optional infof, goto cleanup. + * Do not return or set backend->recv_unrecoverable_err unless in the + * cleanup. The pattern for return error is set *err, optional infof, goto + * cleanup. * * Our priority is to always return as much decrypted data to the caller as * possible, even if an error occurs. The state of the decrypted buffer must @@ -2155,7 +2132,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, infof(data, "schannel: server indicated shutdown in a prior call"); goto cleanup; } - /* It's debatable what to return when !len. Regardless we can't return + /* it is debatable what to return when !len. Regardless we cannot return immediately because there may be data to decrypt (in the case we want to decrypt all encrypted cached data) so handle !len later in cleanup. */ @@ -2234,7 +2211,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */ - sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, + sspi_status = Curl_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, &inbuf_desc, 0, NULL); /* check if everything went fine (server may want to renegotiate @@ -2313,14 +2290,15 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(sspi_status == SEC_I_RENEGOTIATE) { infof(data, "schannel: remote party requests renegotiation"); if(*err && *err != CURLE_AGAIN) { - infof(data, "schannel: can't renegotiate, an error is pending"); + infof(data, "schannel: cannot renegotiate, an error is pending"); goto cleanup; } /* begin renegotiation */ infof(data, "schannel: renegotiating SSL/TLS connection"); connssl->state = ssl_connection_negotiating; - connssl->connecting_state = ssl_connect_2_writing; + connssl->connecting_state = ssl_connect_2; + connssl->io_need = CURL_SSL_IO_NEED_SEND; backend->recv_renegotiating = true; *err = schannel_connect_common(cf, data, FALSE, &done); backend->recv_renegotiating = false; @@ -2377,13 +2355,13 @@ cleanup: /* Error if the connection has closed without a close_notify. - The behavior here is a matter of debate. We don't want to be vulnerable - to a truncation attack however there's some browser precedent for + The behavior here is a matter of debate. We do not want to be vulnerable + to a truncation attack however there is some browser precedent for ignoring the close_notify for compatibility reasons. Additionally, Windows 2000 (v5.0) is a special case since it seems it - doesn't return close_notify. In that case if the connection was closed we - assume it was graceful (close_notify) since there doesn't seem to be a + does not return close_notify. In that case if the connection was closed we + assume it was graceful (close_notify) since there does not seem to be a way to tell. */ if(len && !backend->decdata_offset && backend->recv_connection_closed && @@ -2420,7 +2398,7 @@ cleanup: if(!*err && !backend->recv_connection_closed) *err = CURLE_AGAIN; - /* It's debatable what to return when !len. We could return whatever error + /* it is debatable what to return when !len. We could return whatever error we got from decryption but instead we override here so the return is consistent. */ @@ -2475,8 +2453,9 @@ static bool schannel_data_pending(struct Curl_cfilter *cf, /* shut down the SSL connection and clean up related memory. this function can be called multiple times on the same connection including if the SSL connection failed (eg connection made but failed handshake). */ -static int schannel_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode schannel_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) { /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx * Shutting Down an Schannel Connection @@ -2484,41 +2463,57 @@ static int schannel_shutdown(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + + if(cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } DEBUGASSERT(data); DEBUGASSERT(backend); + /* Not supported in schannel */ + (void)send_shutdown; + + *done = FALSE; if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", connssl->peer.hostname, connssl->peer.port); } - if(backend->cred && backend->ctxt) { + if(!backend->ctxt || cf->shutdown) { + *done = TRUE; + goto out; + } + + if(backend->cred && backend->ctxt && !backend->sent_shutdown) { SecBufferDesc BuffDesc; SecBuffer Buffer; SECURITY_STATUS sspi_status; SecBuffer outbuf; SecBufferDesc outbuf_desc; - CURLcode result; DWORD dwshut = SCHANNEL_SHUTDOWN; InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); InitSecBufferDesc(&BuffDesc, &Buffer, 1); - sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, + sspi_status = Curl_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, &BuffDesc); if(sspi_status != SEC_E_OK) { char buffer[STRERROR_LEN]; failf(data, "schannel: ApplyControlToken failure: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_SEND_ERROR; + goto out; } /* setup output buffer */ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&outbuf_desc, &outbuf, 1); - sspi_status = s_pSecFn->InitializeSecurityContext( + sspi_status = Curl_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, backend->cred->sni_hostname, @@ -2536,19 +2531,81 @@ static int schannel_shutdown(struct Curl_cfilter *cf, /* send close message which is in output buffer */ ssize_t written = Curl_conn_cf_send(cf->next, data, outbuf.pvBuffer, outbuf.cbBuffer, - &result); - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { - infof(data, "schannel: failed to send close msg: %s" - " (bytes written: %zd)", curl_easy_strerror(result), written); + FALSE, &result); + Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); + if(!result) { + if(written < (ssize_t)outbuf.cbBuffer) { + /* TODO: handle partial sends */ + infof(data, "schannel: failed to send close msg: %s" + " (bytes written: %zd)", curl_easy_strerror(result), written); + result = CURLE_SEND_ERROR; + goto out; + } + backend->sent_shutdown = TRUE; + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; } + else { + if(!backend->recv_connection_closed) { + infof(data, "schannel: error sending close msg: %d", result); + result = CURLE_SEND_ERROR; + goto out; + } + /* Looks like server already closed the connection. + * An error to send our close notify is not a failure. */ + *done = TRUE; + result = CURLE_OK; + } + } + } + + /* If the connection seems open and we have not seen the close notify + * from the server yet, try to receive it. */ + if(backend->cred && backend->ctxt && + !backend->recv_sspi_close_notify && !backend->recv_connection_closed) { + char buffer[1024]; + ssize_t nread; + + nread = schannel_recv(cf, data, buffer, sizeof(buffer), &result); + if(nread > 0) { + /* still data coming in? */ + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + backend->recv_connection_closed = TRUE; + *done = TRUE; + } + else if(nread < 0 && result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else { + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + result = CURLE_RECV_ERROR; } } +out: + cf->shutdown = (result || *done); + return result; +} + +static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct schannel_ssl_backend_data *backend = + (struct schannel_ssl_backend_data *)connssl->backend; + + DEBUGASSERT(data); + DEBUGASSERT(backend); + /* free SSPI Schannel API security context handle */ if(backend->ctxt) { DEBUGF(infof(data, "schannel: clear security context handle")); - s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); + Curl_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); Curl_safefree(backend->ctxt); } @@ -2574,13 +2631,6 @@ static int schannel_shutdown(struct Curl_cfilter *cf, backend->decdata_length = 0; backend->decdata_offset = 0; } - - return CURLE_OK; -} - -static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - schannel_shutdown(cf, data); } static int schannel_init(void) @@ -2595,9 +2645,7 @@ static void schannel_cleanup(void) static size_t schannel_version(char *buffer, size_t size) { - size = msnprintf(buffer, size, "Schannel"); - - return size; + return msnprintf(buffer, size, "Schannel"); } static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM, @@ -2622,7 +2670,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, DEBUGASSERT(backend); - /* if a path wasn't specified, don't pin */ + /* if a path was not specified, do not pin */ if(!pinnedpubkey) return CURLE_OK; @@ -2634,7 +2682,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf, struct Curl_asn1Element *pubkey; sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, + Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); @@ -2684,6 +2732,13 @@ static void schannel_checksum(const unsigned char *input, DWORD provType, const unsigned int algId) { +#ifdef CURL_WINDOWS_APP + (void)input; + (void)inputlen; + (void)provType; + (void)algId; + memset(checksum, 0, checksumlen); +#else HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; DWORD cbHashSize = 0; @@ -2724,6 +2779,7 @@ static void schannel_checksum(const unsigned char *input, if(hProv) CryptReleaseContext(hProv, 0); +#endif } static CURLcode schannel_sha256sum(const unsigned char *input, @@ -2752,7 +2808,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - struct schannel_multi_ssl_backend_data *mbackend; + struct schannel_cert_share *share; const struct ssl_general_config *cfg = &data->set.general_ssl; timediff_t timeout_ms; timediff_t elapsed_ms; @@ -2761,12 +2817,14 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, DEBUGASSERT(multi); - if(!multi || !multi->ssl_backend_data) { + if(!multi) { return NULL; } - mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; - if(!mbackend->cert_store) { + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + if(!share || !share->cert_store) { return NULL; } @@ -2781,37 +2839,47 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms >= 0) { now = Curl_now(); - elapsed_ms = Curl_timediff(now, mbackend->time); + elapsed_ms = Curl_timediff(now, share->time); if(elapsed_ms >= timeout_ms) { return NULL; } } if(ca_info_blob) { - if(!mbackend->CAinfo_blob_digest) { - return NULL; - } - if(mbackend->CAinfo_blob_size != ca_info_blob->len) { + if(share->CAinfo_blob_size != ca_info_blob->len) { return NULL; } schannel_sha256sum((const unsigned char *)ca_info_blob->data, ca_info_blob->len, info_blob_digest, CURL_SHA256_DIGEST_LENGTH); - if(memcmp(mbackend->CAinfo_blob_digest, - info_blob_digest, + if(memcmp(share->CAinfo_blob_digest, info_blob_digest, CURL_SHA256_DIGEST_LENGTH)) { - return NULL; + return NULL; } } else { - if(!conn_config->CAfile || !mbackend->CAfile || - strcmp(mbackend->CAfile, conn_config->CAfile)) { + if(!conn_config->CAfile || !share->CAfile || + strcmp(share->CAfile, conn_config->CAfile)) { return NULL; } } - return mbackend->cert_store; + return share->cert_store; +} + +static void schannel_cert_share_free(void *key, size_t key_len, void *p) +{ + struct schannel_cert_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_SCHANNEL_CERT_SHARE_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->cert_store) { + CertCloseStore(share->cert_store, 0); + } + free(share->CAfile); + free(share); } bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, @@ -2821,8 +2889,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - struct schannel_multi_ssl_backend_data *mbackend; - unsigned char *CAinfo_blob_digest = NULL; + struct schannel_cert_share *share; size_t CAinfo_blob_size = 0; char *CAfile = NULL; @@ -2832,25 +2899,27 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, return false; } - if(!multi->ssl_backend_data) { - multi->ssl_backend_data = - calloc(1, sizeof(struct schannel_multi_ssl_backend_data)); - if(!multi->ssl_backend_data) { + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) { + return false; + } + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1, + share, schannel_cert_share_free)) { + free(share); return false; } } - mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data; - - if(ca_info_blob) { - CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH); - if(!CAinfo_blob_digest) { - return false; - } schannel_sha256sum((const unsigned char *)ca_info_blob->data, ca_info_blob->len, - CAinfo_blob_digest, + share->CAinfo_blob_digest, CURL_SHA256_DIGEST_LENGTH); CAinfo_blob_size = ca_info_blob->len; } @@ -2864,33 +2933,18 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, } /* free old cache data */ - if(mbackend->cert_store) { - CertCloseStore(mbackend->cert_store, 0); + if(share->cert_store) { + CertCloseStore(share->cert_store, 0); } - free(mbackend->CAinfo_blob_digest); - free(mbackend->CAfile); + free(share->CAfile); - mbackend->time = Curl_now(); - mbackend->cert_store = cert_store; - mbackend->CAinfo_blob_digest = CAinfo_blob_digest; - mbackend->CAinfo_blob_size = CAinfo_blob_size; - mbackend->CAfile = CAfile; + share->time = Curl_now(); + share->cert_store = cert_store; + share->CAinfo_blob_size = CAinfo_blob_size; + share->CAfile = CAfile; return true; } -static void schannel_free_multi_ssl_backend_data( - struct multi_ssl_backend_data *msbd) -{ - struct schannel_multi_ssl_backend_data *mbackend = - (struct schannel_multi_ssl_backend_data*)msbd; - if(mbackend->cert_store) { - CertCloseStore(mbackend->cert_store, 0); - } - free(mbackend->CAinfo_blob_digest); - free(mbackend->CAfile); - free(mbackend); -} - const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ @@ -2898,9 +2952,13 @@ const struct Curl_ssl Curl_ssl_schannel = { #ifdef HAS_MANUAL_VERIFY_API SSLSUPP_CAINFO_BLOB | #endif +#ifndef CURL_WINDOWS_APP SSLSUPP_PINNEDPUBKEY | +#endif SSLSUPP_TLS13_CIPHERSUITES | - SSLSUPP_HTTPS_PROXY, + SSLSUPP_CA_CACHE | + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST, sizeof(struct schannel_ssl_backend_data), @@ -2925,9 +2983,9 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif /* USE_SCHANNEL */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index 5e233a9d0..800fdf88e 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h @@ -28,7 +28,10 @@ #ifdef USE_SCHANNEL -#if defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN) +#include "vtls.h" + +#if (defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN)) \ + && !defined(CURL_WINDOWS_APP) #define HAS_MANUAL_VERIFY_API #endif @@ -144,7 +147,7 @@ struct schannel_ssl_backend_data { size_t encdata_offset, decdata_offset; unsigned char *encdata_buffer, *decdata_buffer; /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another recv() (that is, status is + cannot be decrypted without another recv() (that is, status is SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds more bytes into encdata then set this back to false. */ bool encdata_is_incomplete; @@ -157,10 +160,14 @@ struct schannel_ssl_backend_data { #ifdef HAS_MANUAL_VERIFY_API bool use_manual_cred_validation; /* true if manual cred validation is used */ #endif + BIT(sent_shutdown); }; -struct schannel_multi_ssl_backend_data { - unsigned char *CAinfo_blob_digest; /* CA info blob digest */ +/* key to use at `multi->proto_hash` */ +#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share" + +struct schannel_cert_share { + unsigned char CAinfo_blob_digest[CURL_SHA256_DIGEST_LENGTH]; size_t CAinfo_blob_size; /* CA info blob size */ char *CAfile; /* CAfile path used to generate certificate store */ diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 24146d0bd..11e61b689 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -33,7 +33,7 @@ #ifdef USE_SCHANNEL #ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." +# error "cannot compile SCHANNEL support without SSPI." #endif #include "schannel.h" @@ -82,8 +82,8 @@ static int is_cr_or_lf(char c) } /* Search the substring needle,needlelen into string haystack,haystacklen - * Strings don't need to be terminated by a '\0'. - * Similar of OSX/Linux memmem (not available on Visual Studio). + * Strings do not need to be terminated by a '\0'. + * Similar of macOS/Linux memmem (not available on Visual Studio). * Return position of beginning of first occurrence or NULL if not found */ static const char *c_memmem(const void *haystack, size_t haystacklen, @@ -335,7 +335,7 @@ cleanup: /* * Returns the number of characters necessary to populate all the host_names. - * If host_names is not NULL, populate it with all the host names. Each string + * If host_names is not NULL, populate it with all the hostnames. Each string * in the host_names is null-terminated and the last string is double * null-terminated. If no DNS names are found, a single null-terminated empty * string is returned. @@ -346,6 +346,12 @@ static DWORD cert_get_name_string(struct Curl_easy *data, DWORD length) { DWORD actual_length = 0; +#if defined(CURL_WINDOWS_APP) + (void)data; + (void)cert_context; + (void)host_names; + (void)length; +#else BOOL compute_content = FALSE; CERT_INFO *cert_info = NULL; CERT_EXTENSION *extension = NULL; @@ -441,14 +447,14 @@ static DWORD cert_get_name_string(struct Curl_easy *data, } /* Sanity check to prevent buffer overrun. */ if((actual_length + current_length) > length) { - failf(data, "schannel: Not enough memory to list all host names."); + failf(data, "schannel: Not enough memory to list all hostnames."); break; } dns_w = entry->pwszDNSName; - /* pwszDNSName is in ia5 string format and hence doesn't contain any - * non-ascii characters. */ + /* pwszDNSName is in ia5 string format and hence does not contain any + * non-ASCII characters. */ while(*dns_w != '\0') { - *current_pos++ = (char)(*dns_w++); + *current_pos++ = (TCHAR)(*dns_w++); } *current_pos++ = '\0'; actual_length += (DWORD)current_length; @@ -457,6 +463,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, /* Last string has double null-terminator. */ *current_pos = '\0'; } +#endif return actual_length; } @@ -476,7 +483,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, DWORD actual_len = 0; sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); @@ -605,7 +612,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, DEBUGASSERT(BACKEND); sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index f49db6481..0eb079bbb 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -30,6 +30,8 @@ #include "curl_setup.h" +#ifdef USE_SECTRANSP + #include "urldata.h" /* for the Curl_easy definition */ #include "curl_base64.h" #include "strtok.h" @@ -37,19 +39,16 @@ #include "strcase.h" #include "x509asn1.h" #include "strerror.h" - -#ifdef USE_SECTRANSP +#include "cipher_suite.h" #ifdef __clang__ #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-pointer-compare" +#pragma clang diagnostic ignored "-Wunreachable-code" #endif /* __clang__ */ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" -#pragma GCC diagnostic ignored "-Wundef" -#pragma GCC diagnostic ignored "-Wunreachable-code" #endif #include @@ -72,7 +71,7 @@ #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 -#error "The Secure Transport back-end requires Leopard or later." +#error "The Secure Transport backend requires Leopard or later." #endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ #define CURL_BUILD_IOS 0 @@ -122,7 +121,7 @@ #define CURL_SUPPORT_MAC_10_9 0 #else -#error "The Secure Transport back-end requires iOS or macOS." +#error "The Secure Transport backend requires iOS or macOS." #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ #if CURL_BUILD_MAC @@ -144,7 +143,8 @@ #include "memdebug.h" -/* From MacTypes.h (which we can't include because it isn't present in iOS: */ +/* From MacTypes.h (which we cannot include because it is not present in + iOS: */ #define ioErr -36 #define paramErr -50 @@ -152,636 +152,60 @@ struct st_ssl_backend_data { SSLContextRef ssl_ctx; bool ssl_direction; /* true if writing, false if reading */ size_t ssl_write_buffered_length; + BIT(sent_shutdown); }; -struct st_cipher { - const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ - const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ - SSLCipherSuite num; /* Cipher suite code/number defined in IANA registry */ - bool weak; /* Flag to mark cipher as weak based on previous implementation - of Secure Transport back-end by CURL */ -}; - -/* Macro to initialize st_cipher data structure: stringify id to name, cipher - number/id, 'weak' suite flag +/* Create the list of default ciphers to use by making an intersection of the + * ciphers supported by Secure Transport and the list below, using the order + * of the former. + * This list is based on TLS recommendations by Mozilla, balancing between + * security and wide compatibility: "Most ciphers that are not clearly broken + * and dangerous to use are supported" */ -#define CIPHER_DEF(num, alias, weak) \ - { #num, alias, num, weak } - -/* - Macro to initialize st_cipher data structure with name, code (IANA cipher - number/id value), and 'weak' suite flag. The first 28 cipher suite numbers - have the same IANA code for both SSL and TLS standards: numbers 0x0000 to - 0x001B. They have different names though. The first 4 letters of the cipher - suite name are the protocol name: "SSL_" or "TLS_", rest of the IANA name is - the same for both SSL and TLS cipher suite name. - The second part of the problem is that macOS/iOS SDKs don't define all TLS - codes but only 12 of them. The SDK defines all SSL codes though, i.e. SSL_NUM - constant is always defined for those 28 ciphers while TLS_NUM is defined only - for 12 of the first 28 ciphers. Those 12 TLS cipher codes match to - corresponding SSL enum value and represent the same cipher suite. Therefore - we'll use the SSL enum value for those cipher suites because it is defined - for all 28 of them. - We make internal data consistent and based on TLS names, i.e. all st_cipher - item names start with the "TLS_" prefix. - Summarizing all the above, those 28 first ciphers are presented in our table - with both TLS and SSL names. Their cipher numbers are assigned based on the - SDK enum value for the SSL cipher, which matches to IANA TLS number. - */ -#define CIPHER_DEF_SSLTLS(num_wo_prefix, alias, weak) \ - { "TLS_" #num_wo_prefix, alias, SSL_##num_wo_prefix, weak } - -/* - Cipher suites were marked as weak based on the following: - RC4 encryption - rfc7465, the document contains a list of deprecated ciphers. - Marked in the code below as weak. - RC2 encryption - many mentions, was found vulnerable to a relatively easy - attack https://link.springer.com/chapter/10.1007%2F3-540-69710-1_14 - Marked in the code below as weak. - DES and IDEA encryption - rfc5469, has a list of deprecated ciphers. - Marked in the code below as weak. - Anonymous Diffie-Hellman authentication and anonymous elliptic curve - Diffie-Hellman - vulnerable to a man-in-the-middle attack. Deprecated by - RFC 4346 aka TLS 1.1 (section A.5, page 60) - Null bulk encryption suites - not encrypted communication - Export ciphers, i.e. ciphers with restrictions to be used outside the US for - software exported to some countries, they were excluded from TLS 1.1 - version. More precisely, they were noted as ciphers which MUST NOT be - negotiated in RFC 4346 aka TLS 1.1 (section A.5, pages 60 and 61). - All of those filters were considered weak because they contain a weak - algorithm like DES, RC2 or RC4, and already considered weak by other - criteria. - 3DES - NIST deprecated it and is going to retire it by 2023 - https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA - OpenSSL https://www.openssl.org/blog/blog/2016/08/24/sweet32/ also - deprecated those ciphers. Some other libraries also consider it - vulnerable or at least not strong enough. - - CBC ciphers are vulnerable with SSL3.0 and TLS1.0: - https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance - /118518-technote-esa-00.html - We don't take care of this issue because it is resolved by later TLS - versions and for us, it requires more complicated checks, we need to - check a protocol version also. Vulnerability doesn't look very critical - and we do not filter out those cipher suites. - */ - -#define CIPHER_WEAK_NOT_ENCRYPTED TRUE -#define CIPHER_WEAK_RC_ENCRYPTION TRUE -#define CIPHER_WEAK_DES_ENCRYPTION TRUE -#define CIPHER_WEAK_IDEA_ENCRYPTION TRUE -#define CIPHER_WEAK_ANON_AUTH TRUE -#define CIPHER_WEAK_3DES_ENCRYPTION TRUE -#define CIPHER_STRONG_ENOUGH FALSE - -/* Please do not change the order of the first ciphers available for SSL. - Do not insert and do not delete any of them. Code below - depends on their order and continuity. - If you add a new cipher, please maintain order by number, i.e. - insert in between existing items to appropriate place based on - cipher suite IANA number -*/ -static const struct st_cipher ciphertable[] = { - /* SSL version 3.0 and initial TLS 1.0 cipher suites. - Defined since SDK 10.2.8 */ - CIPHER_DEF_SSLTLS(NULL_WITH_NULL_NULL, /* 0x0000 */ - NULL, - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_WITH_NULL_MD5, /* 0x0001 */ - "NULL-MD5", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_WITH_NULL_SHA, /* 0x0002 */ - "NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC4_40_MD5, /* 0x0003 */ - "EXP-RC4-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_MD5, /* 0x0004 */ - "RC4-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_SHA, /* 0x0005 */ - "RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* 0x0006 */ - "EXP-RC2-CBC-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_IDEA_CBC_SHA, /* 0x0007 */ - "IDEA-CBC-SHA", - CIPHER_WEAK_IDEA_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0008 */ - "EXP-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_DES_CBC_SHA, /* 0x0009 */ - "DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - "DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x000B */ - "EXP-DH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_WITH_DES_CBC_SHA, /* 0x000C */ - "DH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x000D */ - "DH-DSS-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x000E */ - "EXP-DH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_WITH_DES_CBC_SHA, /* 0x000F */ - "DH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0010 */ - "DH-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x0011 */ - "EXP-EDH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_WITH_DES_CBC_SHA, /* 0x0012 */ - "EDH-DSS-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x0013 */ - "DHE-DSS-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0014 */ - "EXP-EDH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_WITH_DES_CBC_SHA, /* 0x0015 */ - "EDH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0016 */ - "DHE-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_RC4_40_MD5, /* 0x0017 */ - "EXP-ADH-RC4-MD5", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_RC4_128_MD5, /* 0x0018 */ - "ADH-RC4-MD5", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_DES40_CBC_SHA, /* 0x0019 */ - "EXP-ADH-DES-CBC-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_DES_CBC_SHA, /* 0x001A */ - "ADH-DES-CBC-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_3DES_EDE_CBC_SHA, /* 0x001B */ - "ADH-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* 0x001C */ - NULL, - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* 0x001D */ - NULL, - CIPHER_STRONG_ENOUGH), - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */ - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA, /* 0x002C */ - "PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA, /* 0x002D */ - "DHE-PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA, /* 0x002E */ - "RSA-PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* TLS addenda using AES, per RFC 3268. Defined since SDK 10.4u */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - "AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA, /* 0x0030 */ - "DH-DSS-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA, /* 0x0031 */ - "DH-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* 0x0032 */ - "DHE-DSS-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* 0x0033 */ - "DHE-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA, /* 0x0034 */ - "ADH-AES128-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - "AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA, /* 0x0036 */ - "DH-DSS-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA, /* 0x0037 */ - "DH-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* 0x0038 */ - "DHE-DSS-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* 0x0039 */ - "DHE-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA, /* 0x003A */ - "ADH-AES256-SHA", - CIPHER_WEAK_ANON_AUTH), - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* TLS 1.2 addenda, RFC 5246 */ - /* Server provided RSA certificate for key exchange. */ - CIPHER_DEF(TLS_RSA_WITH_NULL_SHA256, /* 0x003B */ - "NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - "AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - "AES256-SHA256", - CIPHER_STRONG_ENOUGH), - /* Server-authenticated (and optionally client-authenticated) - Diffie-Hellman. */ - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, /* 0x003E */ - "DH-DSS-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, /* 0x003F */ - "DH-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, /* 0x0040 */ - "DHE-DSS-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - - /* TLS 1.2 addenda, RFC 5246 */ - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ - "DHE-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, /* 0x0068 */ - "DH-DSS-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, /* 0x0069 */ - "DH-RSA-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, /* 0x006A */ - "DHE-DSS-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ - "DHE-RSA-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA256, /* 0x006C */ - "ADH-AES128-SHA256", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA256, /* 0x006D */ - "ADH-AES256-SHA256", - CIPHER_WEAK_ANON_AUTH), -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* Addendum from RFC 4279, TLS PSK */ - CIPHER_DEF(TLS_PSK_WITH_RC4_128_SHA, /* 0x008A */ - "PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008B */ - "PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA, /* 0x008C */ - "PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA, /* 0x008D */ - "PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_RC4_128_SHA, /* 0x008E */ - "DHE-PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008F */ - "DHE-PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, /* 0x0090 */ - "DHE-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, /* 0x0091 */ - "DHE-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_RC4_128_SHA, /* 0x0092 */ - "RSA-PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x0093 */ - "RSA-PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, /* 0x0094 */ - "RSA-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, /* 0x0095 */ - "RSA-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites - for TLS. */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - "AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - "AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ - "DHE-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ - "DHE-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, /* 0x00A0 */ - "DH-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, /* 0x00A1 */ - "DH-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A2 */ - "DHE-DSS-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A3 */ - "DHE-DSS-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A4 */ - "DH-DSS-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A5 */ - "DH-DSS-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_GCM_SHA256, /* 0x00A6 */ - "ADH-AES128-GCM-SHA256", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_GCM_SHA384, /* 0x00A7 */ - "ADH-AES256-GCM-SHA384", - CIPHER_WEAK_ANON_AUTH), -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* RFC 5487 - PSK with SHA-256/384 and AES GCM */ - CIPHER_DEF(TLS_PSK_WITH_AES_128_GCM_SHA256, /* 0x00A8 */ - "PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_GCM_SHA384, /* 0x00A9 */ - "PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AA */ - "DHE-PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AB */ - "DHE-PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AC */ - "RSA-PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AD */ - "RSA-PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA256, /* 0x00AE */ - "PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA384, /* 0x00AF */ - "PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA256, /* 0x00B0 */ - "PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA384, /* 0x00B1 */ - "PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B2 */ - "DHE-PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B3 */ - "DHE-PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA256, /* 0x00B4 */ - "DHE-PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA384, /* 0x00B5 */ - "DHE-PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B6 */ - "RSA-PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B7 */ - "RSA-PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA256, /* 0x00B8 */ - "RSA-PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA384, /* 0x00B9 */ - "RSA-PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* RFC 5746 - Secure Renegotiation. This is not a real suite, - it is a response to initiate negotiation again */ - CIPHER_DEF(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* 0x00FF */ - NULL, - CIPHER_STRONG_ENOUGH), - -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - /* TLS 1.3 standard cipher suites for ChaCha20+Poly1305. - Note: TLS 1.3 ciphersuites do not specify the key exchange - algorithm -- they only specify the symmetric ciphers. - Cipher alias name matches to OpenSSL cipher name, and for - TLS 1.3 ciphers */ - CIPHER_DEF(TLS_AES_128_GCM_SHA256, /* 0x1301 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_256_GCM_SHA384, /* 0x1302 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_128_CCM_SHA256, /* 0x1304 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_128_CCM_8_SHA256, /* 0x1305 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ +static const uint16_t default_ciphers[] = { + TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ + TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ + TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ #if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS - /* ECDSA addenda, RFC 4492 */ - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_NULL_SHA, /* 0xC001 */ - "ECDH-ECDSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, /* 0xC002 */ - "ECDH-ECDSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ - "ECDH-ECDSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ - "ECDH-ECDSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ - "ECDH-ECDSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_NULL_SHA, /* 0xC006 */ - "ECDHE-ECDSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, /* 0xC007 */ - "ECDHE-ECDSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ - "ECDHE-ECDSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - "ECDHE-ECDSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - "ECDHE-ECDSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_NULL_SHA, /* 0xC00B */ - "ECDH-RSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDH_RSA_WITH_RC4_128_SHA, /* 0xC00C */ - "ECDH-RSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ - "ECDH-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ - "ECDH-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ - "ECDH-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_NULL_SHA, /* 0xC010 */ - "ECDHE-RSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_RC4_128_SHA, /* 0xC011 */ - "ECDHE-RSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ - "ECDHE-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - "ECDHE-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ - "ECDHE-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_anon_WITH_NULL_SHA, /* 0xC015 */ - "AECDH-NULL-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_RC4_128_SHA, /* 0xC016 */ - "AECDH-RC4-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, /* 0xC017 */ - "AECDH-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, /* 0xC018 */ - "AECDH-AES128-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, /* 0xC019 */ - "AECDH-AES256-SHA", - CIPHER_WEAK_ANON_AUTH), + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ #endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - HMAC SHA-256/384. */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - "ECDHE-ECDSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - "ECDHE-ECDSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ - "ECDH-ECDSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ - "ECDH-ECDSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - "ECDHE-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - "ECDHE-RSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ - "ECDH-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ - "ECDH-RSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - SHA-256/384 and AES Galois Counter Mode (GCM) */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - "ECDHE-ECDSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - "ECDHE-ECDSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ - "ECDH-ECDSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ - "ECDH-ECDSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - "ECDHE-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ - "ECDHE-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ - "ECDH-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ - "ECDH-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), + TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ + TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ + TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ + TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ -#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 - /* ECDHE_PSK Cipher Suites for Transport Layer Security (TLS), RFC 5489 */ - CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, /* 0xC035 */ - "ECDHE-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, /* 0xC036 */ - "ECDHE-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ - #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - /* Addenda from rfc 7905 ChaCha20-Poly1305 Cipher Suites for - Transport Layer Security (TLS). */ - CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - "ECDHE-RSA-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - "ECDHE-ECDSA-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ + + /* TLSv1.3 is not supported by sectransp, but there is also other + * code referencing TLSv1.3, like: kTLSProtocol13 ? */ + TLS_AES_128_GCM_SHA256, /* 0x1301 */ + TLS_AES_256_GCM_SHA384, /* 0x1302 */ + TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - -#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 - /* ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS), - RFC 7905 */ - CIPHER_DEF(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCAB */ - "PSK-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ - - /* Tags for SSL 2 cipher kinds which are not specified for SSL 3. - Defined since SDK 10.2.8 */ - CIPHER_DEF(SSL_RSA_WITH_RC2_CBC_MD5, /* 0xFF80 */ - NULL, - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_IDEA_CBC_MD5, /* 0xFF81 */ - NULL, - CIPHER_WEAK_IDEA_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_DES_CBC_MD5, /* 0xFF82 */ - NULL, - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_3DES_EDE_CBC_MD5, /* 0xFF83 */ - NULL, - CIPHER_WEAK_3DES_ENCRYPTION), }; -#define NUM_OF_CIPHERS sizeof(ciphertable)/sizeof(ciphertable[0]) +#define DEFAULT_CIPHERS_LEN sizeof(default_ciphers)/sizeof(default_ciphers[0]) /* pinned public key support tests */ @@ -792,7 +216,7 @@ static const struct st_cipher ciphertable[] = { #define SECTRANSP_PINNEDPUBKEY_V1 1 #endif -/* version 2 supports MacOSX 10.7+ */ +/* version 2 supports macOS 10.7+ */ #if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) #define SECTRANSP_PINNEDPUBKEY_V2 1 #endif @@ -816,7 +240,7 @@ static const unsigned char rsa2048SpkiHeader[] = { 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00}; #ifdef SECTRANSP_PINNEDPUBKEY_V1 -/* the *new* version doesn't return DER encoded ecdsa certs like the old... */ +/* the *new* version does not return DER encoded ecdsa certs like the old... */ static const unsigned char ecDsaSecp256r1SpkiHeader[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, @@ -886,7 +310,8 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection, OSStatus rtn = noErr; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); + nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, FALSE, + &result); CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", *dataLength, nwritten, result); if(nwritten <= 0) { @@ -906,25 +331,6 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection, return rtn; } -CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) -{ - /* The first ciphers in the ciphertable are continuous. Here we do small - optimization and instead of loop directly get SSL name by cipher number. - */ - size_t i; - if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) { - return ciphertable[cipher].name; - } - /* Iterate through the rest of the ciphers */ - for(i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1; i < NUM_OF_CIPHERS; - ++i) { - if(ciphertable[i].num == cipher) { - return ciphertable[i].name; - } - } - return ciphertable[SSL_NULL_WITH_NULL_NULL].name; -} - #if CURL_BUILD_MAC CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) { @@ -957,27 +363,27 @@ CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) #endif /* CURL_BUILD_MAC */ /* Apple provides a myriad of ways of getting information about a certificate - into a string. Some aren't available under iOS or newer cats. So here's - a unified function for getting a string describing the certificate that - ought to work in all cats starting with Leopard. */ + into a string. Some are not available under iOS or newer cats. Here's a + unified function for getting a string describing the certificate that ought + to work in all cats starting with Leopard. */ CF_INLINE CFStringRef getsubject(SecCertificateRef cert) { CFStringRef server_cert_summary = CFSTR("(null)"); #if CURL_BUILD_IOS - /* iOS: There's only one way to do this. */ + /* iOS: There is only one way to do this. */ server_cert_summary = SecCertificateCopySubjectSummary(cert); #else #if CURL_BUILD_MAC_10_7 /* Lion & later: Get the long description if we can. */ - if(SecCertificateCopyLongDescription) + if(&SecCertificateCopyLongDescription) server_cert_summary = SecCertificateCopyLongDescription(NULL, cert, NULL); else #endif /* CURL_BUILD_MAC_10_7 */ #if CURL_BUILD_MAC_10_6 /* Snow Leopard: Get the certificate summary. */ - if(SecCertificateCopySubjectSummary) + if(&SecCertificateCopySubjectSummary) server_cert_summary = SecCertificateCopySubjectSummary(cert); else #endif /* CURL_BUILD_MAC_10_6 */ @@ -1015,7 +421,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; cbuf = calloc(1, cbuf_size); if(cbuf) { - if(!CFStringGetCString(c, cbuf, cbuf_size, + if(!CFStringGetCString(c, cbuf, (CFIndex)cbuf_size, kCFStringEncodingUTF8)) { failf(data, "SSL: invalid CA certificate subject"); result = CURLE_PEER_FAILED_VERIFICATION; @@ -1025,7 +431,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, *certp = cbuf; } else { - failf(data, "SSL: couldn't allocate %zu bytes of memory", cbuf_size); + failf(data, "SSL: could not allocate %zu bytes of memory", cbuf_size); result = CURLE_OUT_OF_MEMORY; } } @@ -1037,7 +443,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data, #if CURL_SUPPORT_MAC_10_6 /* The SecKeychainSearch API was deprecated in Lion, and using it will raise - deprecation warnings, so let's not compile this unless it's necessary: */ + deprecation warnings, so let's not compile this unless it is necessary: */ static OSStatus CopyIdentityWithLabelOldSchool(char *label, SecIdentityRef *out_c_a_k) { @@ -1090,7 +496,7 @@ static OSStatus CopyIdentityWithLabel(char *label, /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. kSecClassIdentity was introduced in Lion. If both exist, let's use them to find the certificate. */ - if(SecItemCopyMatching && kSecClassIdentity) { + if(&SecItemCopyMatching && kSecClassIdentity) { CFTypeRef keys[5]; CFTypeRef values[5]; CFDictionaryRef query_dict; @@ -1108,7 +514,7 @@ static OSStatus CopyIdentityWithLabel(char *label, /* identity searches need a SecPolicyRef in order to work */ values[3] = SecPolicyCreateSSL(false, NULL); keys[3] = kSecMatchPolicy; - /* match the name of the certificate (doesn't work in macOS 10.12.1) */ + /* match the name of the certificate (does not work in macOS 10.12.1) */ values[4] = label_cf; keys[4] = kSecAttrLabel; query_dict = CFDictionaryCreate(NULL, (const void **)keys, @@ -1120,7 +526,7 @@ static OSStatus CopyIdentityWithLabel(char *label, /* Do we have a match? */ status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list); - /* Because kSecAttrLabel matching doesn't work with kSecClassIdentity, + /* Because kSecAttrLabel matching does not work with kSecClassIdentity, * we need to find the correct identity ourselves */ if(status == noErr) { keys_list_count = CFArrayGetCount(keys_list); @@ -1186,7 +592,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, cPassword, kCFStringEncodingUTF8) : NULL; CFDataRef pkcs_data = NULL; - /* We can import P12 files on iOS or OS X 10.7 or later: */ + /* We can import P12 files on iOS or macOS 10.7 or later: */ /* These constants are documented as having first appeared in 10.6 but they raise linker errors when used on that cat for some reason. */ #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS @@ -1194,7 +600,8 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, if(blob) { pkcs_data = CFDataCreate(kCFAllocatorDefault, - (const unsigned char *)blob->data, blob->len); + (const unsigned char *)blob->data, + (CFIndex)blob->len); status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; resource_imported = (pkcs_data != NULL); } @@ -1202,7 +609,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)cPath, - strlen(cPath), false); + (CFIndex)strlen(cPath), false); resource_imported = CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data, @@ -1231,7 +638,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, /* On macOS SecPKCS12Import will always add the client certificate to * the Keychain. * - * As this doesn't match iOS, and apps may not want to see their client + * As this does not match iOS, and apps may not want to see their client * certificate saved in the user's keychain, we use SecItemImport * with a NULL keychain to avoid importing it. * @@ -1311,336 +718,308 @@ CF_INLINE bool is_file(const char *filename) return false; } -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS -static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver, - long ssl_version) +static CURLcode +sectransp_set_ssl_version_min_max(struct Curl_easy *data, + struct st_ssl_backend_data *backend, + struct ssl_primary_config *conn_config) { - switch(ssl_version) { +#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS + OSStatus err; + SSLProtocol ver_min; + SSLProtocol ver_max; + +#if CURL_SUPPORT_MAC_10_7 + if(!&SSLSetProtocolVersionMax) + goto legacy; +#endif + + switch(conn_config->version) { + case CURL_SSLVERSION_DEFAULT: + case CURL_SSLVERSION_TLSv1: case CURL_SSLVERSION_TLSv1_0: - *darwinver = kTLSProtocol1; - return CURLE_OK; + ver_min = kTLSProtocol1; + break; case CURL_SSLVERSION_TLSv1_1: - *darwinver = kTLSProtocol11; - return CURLE_OK; + ver_min = kTLSProtocol11; + break; case CURL_SSLVERSION_TLSv1_2: - *darwinver = kTLSProtocol12; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */ -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - *darwinver = kTLSProtocol13; - return CURLE_OK; - } -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ + ver_min = kTLSProtocol12; break; + case CURL_SSLVERSION_TLSv1_3: + default: + failf(data, "SSL: unsupported minimum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; } - return CURLE_SSL_CONNECT_ERROR; -} -#endif -static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; - long max_supported_version_by_os; - - DEBUGASSERT(backend); + switch(conn_config->version_max) { + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + case CURL_SSLVERSION_MAX_TLSv1_3: + case CURL_SSLVERSION_MAX_TLSv1_2: + ver_max = kTLSProtocol12; + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + ver_max = kTLSProtocol11; + break; + case CURL_SSLVERSION_MAX_TLSv1_0: + ver_max = kTLSProtocol1; + break; + default: + failf(data, "SSL: unsupported maximum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; + } - /* macOS 10.5-10.7 supported TLS 1.0 only. - macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. - macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3; + err = SSLSetProtocolVersionMin(backend->ssl_ctx, ver_min); + if(err != noErr) { + failf(data, "SSL: failed to set minimum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } - else { - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; + err = SSLSetProtocolVersionMax(backend->ssl_ctx, ver_max); + if(err != noErr) { + failf(data, "SSL: failed to set maximum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } -#else - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ - switch(ssl_version) { + return CURLE_OK; +#endif +#if CURL_SUPPORT_MAC_10_7 + goto legacy; +legacy: + switch(conn_config->version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = max_supported_version_by_os; + case CURL_SSLVERSION_TLSv1_0: break; + default: + failf(data, "SSL: unsupported minimum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; } -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax) { - SSLProtocol darwin_ver_min = kTLSProtocol1; - SSLProtocol darwin_ver_max = kTLSProtocol1; - CURLcode result = sectransp_version_from_curl(&darwin_ver_min, - ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = sectransp_version_from_curl(&darwin_ver_max, - ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } + /* only TLS 1.0 is supported, disable SSL 3.0 and SSL 2.0 */ + SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); + SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true); - (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min); - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max); - return result; - } - else { -#if CURL_SUPPORT_MAC_10_8 - long i = ssl_version; - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kSSLProtocolAll, - false); - for(; i <= (ssl_version_max >> 16); i++) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - break; - case CURL_SSLVERSION_TLSv1_1: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol11, - true); - break; - case CURL_SSLVERSION_TLSv1_2: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol12, - true); - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "Your version of the OS does not support TLSv1.3"); - return CURLE_SSL_CONNECT_ERROR; - } - } - return CURLE_OK; -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - failf(data, "Secure Transport: cannot set SSL protocol"); - return CURLE_SSL_CONNECT_ERROR; + return CURLE_OK; +#endif } -static bool is_cipher_suite_strong(SSLCipherSuite suite_num) +static int sectransp_cipher_suite_get_str(uint16_t id, char *buf, + size_t buf_size, bool prefer_rfc) { - size_t i; - for(i = 0; i < NUM_OF_CIPHERS; ++i) { - if(ciphertable[i].num == suite_num) { - return !ciphertable[i].weak; - } - } - /* If the cipher is not in our list, assume it is a new one - and therefore strong. Previous implementation was the same, - if cipher suite is not in the list, it was considered strong enough */ - return true; + /* are these fortezza suites even supported ? */ + if(id == SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) + msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"); + else if(id == SSL_FORTEZZA_DMS_WITH_NULL_SHA) + msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_NULL_SHA"); + /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ + else if(id == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + msnprintf(buf, buf_size, "%s", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); + /* do we still need to support these SSL2-only ciphers ? */ + else if(id == SSL_RSA_WITH_RC2_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_RC2_CBC_MD5"); + else if(id == SSL_RSA_WITH_IDEA_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_IDEA_CBC_MD5"); + else if(id == SSL_RSA_WITH_DES_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_DES_CBC_MD5"); + else if(id == SSL_RSA_WITH_3DES_EDE_CBC_MD5) + msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_3DES_EDE_CBC_MD5"); + else + return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); + return 0; } -static bool sectransp_is_separator(char c) +static uint16_t sectransp_cipher_suite_walk_str(const char **str, + const char **end) { - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; + uint16_t id = Curl_cipher_suite_walk_str(str, end); + size_t len = *end - *str; + + if(!id) { + /* are these fortezza suites even supported ? */ + if(strncasecompare("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", *str, len)) + id = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA; + else if(strncasecompare("SSL_FORTEZZA_DMS_WITH_NULL_SHA", *str, len)) + id = SSL_FORTEZZA_DMS_WITH_NULL_SHA; + /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ + else if(strncasecompare("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", *str, len)) + id = TLS_EMPTY_RENEGOTIATION_INFO_SCSV; + /* do we still need to support these SSL2-only ciphers ? */ + else if(strncasecompare("SSL_RSA_WITH_RC2_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_RC2_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_IDEA_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_IDEA_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_DES_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_DES_CBC_MD5; + else if(strncasecompare("SSL_RSA_WITH_3DES_EDE_CBC_MD5", *str, len)) + id = SSL_RSA_WITH_3DES_EDE_CBC_MD5; + } + return id; } -static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, - SSLContextRef ssl_ctx) +/* allocated memory must be freed */ +static SSLCipherSuite * sectransp_get_supported_ciphers(SSLContextRef ssl_ctx, + size_t *len) { - size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i; - SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL; + SSLCipherSuite *ciphers = NULL; OSStatus err = noErr; + *len = 0; -#if CURL_BUILD_MAC - int darwinver_maj = 0, darwinver_min = 0; + err = SSLGetNumberSupportedCiphers(ssl_ctx, len); + if(err != noErr) + goto failed; - GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); -#endif /* CURL_BUILD_MAC */ + ciphers = malloc(*len * sizeof(SSLCipherSuite)); + if(!ciphers) + goto failed; + + err = SSLGetSupportedCiphers(ssl_ctx, ciphers, len); + if(err != noErr) + goto failed; - /* Disable cipher suites that ST supports but are not safe. These ciphers - are unlikely to be used in any case since ST gives other ciphers a much - higher priority, but it's probably better that we not connect at all than - to give the user a false sense of security if the server only supports - insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */ - err = SSLGetNumberSupportedCiphers(ssl_ctx, &all_ciphers_count); - if(err != noErr) { - failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d", - err); - return CURLE_SSL_CIPHER; - } - all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - if(!all_ciphers) { - failf(data, "SSL: Failed to allocate memory for all ciphers"); - return CURLE_OUT_OF_MEMORY; - } - allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - if(!allowed_ciphers) { - Curl_safefree(all_ciphers); - failf(data, "SSL: Failed to allocate memory for allowed ciphers"); - return CURLE_OUT_OF_MEMORY; - } - err = SSLGetSupportedCiphers(ssl_ctx, all_ciphers, - &all_ciphers_count); - if(err != noErr) { - Curl_safefree(all_ciphers); - Curl_safefree(allowed_ciphers); - return CURLE_SSL_CIPHER; - } - for(i = 0UL ; i < all_ciphers_count ; i++) { #if CURL_BUILD_MAC - /* There's a known bug in early versions of Mountain Lion where ST's ECC - ciphers (cipher suite 0xC001 through 0xC032) simply do not work. - Work around the problem here by disabling those ciphers if we are - running in an affected version of OS X. */ - if(darwinver_maj == 12 && darwinver_min <= 3 && - all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { - continue; + { + int maj = 0, min = 0; + GetDarwinVersionNumber(&maj, &min); + /* There is a known bug in early versions of Mountain Lion where ST's ECC + ciphers (cipher suite 0xC001 through 0xC032) simply do not work. + Work around the problem here by disabling those ciphers if we are + running in an affected version of macOS. */ + if(maj == 12 && min <= 3) { + size_t i = 0, j = 0; + for(; i < *len; i++) { + if(ciphers[i] >= 0xC001 && ciphers[i] <= 0xC032) + continue; + ciphers[j++] = ciphers[i]; + } + *len = j; } -#endif /* CURL_BUILD_MAC */ - if(is_cipher_suite_strong(all_ciphers[i])) { - allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; + } +#endif + + return ciphers; +failed: + *len = 0; + Curl_safefree(ciphers); + return NULL; +} + +static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, + SSLContextRef ssl_ctx) +{ + CURLcode ret = CURLE_SSL_CIPHER; + size_t count = 0, i, j; + OSStatus err; + size_t supported_len; + SSLCipherSuite *ciphers = NULL; + + ciphers = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); + if(!ciphers) { + failf(data, "SSL: Failed to get supported ciphers"); + goto failed; + } + + /* Intersect the ciphers supported by Secure Transport with the default + * ciphers, using the order of the former. */ + for(i = 0; i < supported_len; i++) { + for(j = 0; j < DEFAULT_CIPHERS_LEN; j++) { + if(default_ciphers[j] == ciphers[i]) { + ciphers[count++] = ciphers[i]; + break; + } } } - err = SSLSetEnabledCiphers(ssl_ctx, allowed_ciphers, - allowed_ciphers_count); - Curl_safefree(all_ciphers); - Curl_safefree(allowed_ciphers); + + if(count == 0) { + failf(data, "SSL: no supported default ciphers"); + goto failed; + } + + err = SSLSetEnabledCiphers(ssl_ctx, ciphers, count); if(err != noErr) { failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - return CURLE_SSL_CIPHER; + goto failed; } - return CURLE_OK; + + ret = CURLE_OK; +failed: + Curl_safefree(ciphers); + return ret; } static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, SSLContextRef ssl_ctx, const char *ciphers) { - size_t ciphers_count = 0; - const char *cipher_start = ciphers; - OSStatus err = noErr; - SSLCipherSuite selected_ciphers[NUM_OF_CIPHERS]; + CURLcode ret = CURLE_SSL_CIPHER; + size_t count = 0, i; + const char *ptr, *end; + OSStatus err; + size_t supported_len; + SSLCipherSuite *supported = NULL; + SSLCipherSuite *selected = NULL; - if(!ciphers) - return CURLE_OK; + supported = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); + if(!supported) { + failf(data, "SSL: Failed to get supported ciphers"); + goto failed; + } - while(sectransp_is_separator(*ciphers)) /* Skip initial separators. */ - ciphers++; - if(!*ciphers) - return CURLE_OK; + selected = malloc(supported_len * sizeof(SSLCipherSuite)); + if(!selected) { + failf(data, "SSL: Failed to allocate memory"); + goto failed; + } - cipher_start = ciphers; - while(*cipher_start && ciphers_count < NUM_OF_CIPHERS) { - bool cipher_found = FALSE; - size_t cipher_len = 0; - const char *cipher_end = NULL; - bool tls_name = FALSE; - size_t i; - - /* Skip separators */ - while(sectransp_is_separator(*cipher_start)) - cipher_start++; - if(*cipher_start == '\0') { - break; - } - /* Find last position of a cipher in the ciphers string */ - cipher_end = cipher_start; - while(*cipher_end != '\0' && !sectransp_is_separator(*cipher_end)) { - ++cipher_end; - } + for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { + uint16_t id = sectransp_cipher_suite_walk_str(&ptr, &end); - /* IANA cipher names start with the TLS_ or SSL_ prefix. - If the 4th symbol of the cipher is '_' we look for a cipher in the - table by its (TLS) name. - Otherwise, we try to match cipher by an alias. */ - if(cipher_start[3] == '_') { - tls_name = TRUE; + /* Check if cipher is supported */ + if(id) { + for(i = 0; i < supported_len && supported[i] != id; i++); + if(i == supported_len) + id = 0; } - /* Iterate through the cipher table and look for the cipher, starting - the cipher number 0x01 because the 0x00 is not the real cipher */ - cipher_len = cipher_end - cipher_start; - for(i = 1; i < NUM_OF_CIPHERS; ++i) { - const char *table_cipher_name = NULL; - if(tls_name) { - table_cipher_name = ciphertable[i].name; - } - else if(ciphertable[i].alias_name) { - table_cipher_name = ciphertable[i].alias_name; - } - else { - continue; - } - /* Compare a part of the string between separators with a cipher name - in the table and make sure we matched the whole cipher name */ - if(strncmp(cipher_start, table_cipher_name, cipher_len) == 0 - && table_cipher_name[cipher_len] == '\0') { - selected_ciphers[ciphers_count] = ciphertable[i].num; - ++ciphers_count; - cipher_found = TRUE; - break; - } - } - if(!cipher_found) { - /* It would be more human-readable if we print the wrong cipher name - but we don't want to allocate any additional memory and copy the name - into it, then add it into logs. - Also, we do not modify an original cipher list string. We just point - to positions where cipher starts and ends in the cipher list string. - The message is a bit cryptic and longer than necessary but can be - understood by humans. */ - failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name" - " starting position %zd and ending position %zd", - ciphers, - cipher_start - ciphers, - cipher_end - ciphers); - return CURLE_SSL_CIPHER; - } - if(*cipher_end) { - cipher_start = cipher_end + 1; + if(!id) { + if(ptr[0] != '\0') + infof(data, "SSL: unknown cipher in list: \"%.*s\"", (int) (end - ptr), + ptr); + continue; } - else { - break; + + /* No duplicates allowed (so selected cannot overflow) */ + for(i = 0; i < count && selected[i] != id; i++); + if(i < count) { + infof(data, "SSL: duplicate cipher in list: \"%.*s\"", (int) (end - ptr), + ptr); + continue; } + + selected[count++] = id; + } + + if(count == 0) { + failf(data, "SSL: no supported cipher in list"); + goto failed; } - /* All cipher suites in the list are found. Report to logs as-is */ - infof(data, "SSL: Setting cipher suites list \"%s\"", ciphers); - err = SSLSetEnabledCiphers(ssl_ctx, selected_ciphers, ciphers_count); + err = SSLSetEnabledCiphers(ssl_ctx, selected, count); if(err != noErr) { failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - return CURLE_SSL_CIPHER; + goto failed; } - return CURLE_OK; + + ret = CURLE_OK; +failed: + Curl_safefree(supported); + Curl_safefree(selected); + return ret; } static void sectransp_session_free(void *sessionid, size_t idsize) { /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a cached session ID inside the Security framework. There is a private - function that does this, but I don't want to have to explain to you why I + function that does this, but I do not want to have to explain to you why I got your application rejected from the App Store due to the use of a private API, so the best we can do is free up our own char array that we created way back in sectransp_connect_step1... */ @@ -1665,6 +1044,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; char *ciphers; OSStatus err = noErr; + CURLcode result; #if CURL_BUILD_MAC int darwinver_maj = 0, darwinver_min = 0; @@ -1675,23 +1055,23 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #endif /* CURL_BUILD_MAC */ #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext) { /* use the newer API if available */ + if(&SSLCreateContext) { /* use the newer API if available */ if(backend->ssl_ctx) CFRelease(backend->ssl_ctx); backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); if(!backend->ssl_ctx) { - failf(data, "SSL: couldn't create a context"); + failf(data, "SSL: could not create a context"); return CURLE_OUT_OF_MEMORY; } } else { - /* The old ST API does not exist under iOS, so don't compile it: */ + /* The old ST API does not exist under iOS, so do not compile it: */ #if CURL_SUPPORT_MAC_10_8 if(backend->ssl_ctx) (void)SSLDisposeContext(backend->ssl_ctx); err = SSLNewContext(false, &(backend->ssl_ctx)); if(err != noErr) { - failf(data, "SSL: couldn't create a context: OSStatus %d", err); + failf(data, "SSL: could not create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } #endif /* CURL_SUPPORT_MAC_10_8 */ @@ -1701,123 +1081,18 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, (void)SSLDisposeContext(backend->ssl_ctx); err = SSLNewContext(false, &(backend->ssl_ctx)); if(err != noErr) { - failf(data, "SSL: couldn't create a context: OSStatus %d", err); + failf(data, "SSL: could not create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */ - /* check to see if we've been told to use an explicit SSL/TLS version */ -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax) { - switch(conn_config->version) { - case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13); - } - else { - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); - } -#else - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(cf, data); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kSSLProtocolAll, - false); - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol11, - true); - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol12, - true); - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(cf, data); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { - failf(data, "Your version of the OS does not support to set maximum" - " SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - break; - case CURL_SSLVERSION_TLSv1_1: - failf(data, "Your version of the OS does not support TLSv1.1"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_2: - failf(data, "Your version of the OS does not support TLSv1.2"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "Your version of the OS does not support TLSv1.3"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_SSLv2: - case CURL_SSLVERSION_SSLv3: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ + result = sectransp_set_ssl_version_min_max(data, backend, conn_config); + if(result != CURLE_OK) + return result; -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) if(connssl->alpn) { if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { struct alpn_proto_buf proto; @@ -1886,7 +1161,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, err = SecIdentityCopyCertificate(cert_and_key, &cert); if(err == noErr) { char *certp; - CURLcode result = CopyCertSubject(data, cert, &certp); + result = CopyCertSubject(data, cert, &certp); if(!result) { infof(data, "Client certificate: %s", certp); free(certp); @@ -1929,11 +1204,11 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, cert_showfilename_error); break; case errSecItemNotFound: - failf(data, "SSL: Can't find the certificate \"%s\" and its private " + failf(data, "SSL: cannot find the certificate \"%s\" and its private " "key in the Keychain.", cert_showfilename_error); break; default: - failf(data, "SSL: Can't load the certificate \"%s\" and its private " + failf(data, "SSL: cannot load the certificate \"%s\" and its private " "key: OSStatus %d", cert_showfilename_error, err); break; } @@ -1948,7 +1223,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, #if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS /* Snow Leopard introduced the SSLSetSessionOption() function, but due to a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag - works, it doesn't work as expected under Snow Leopard, Lion or + works, it does not work as expected under Snow Leopard, Lion or Mountain Lion. So we need to call SSLSetEnableCertVerify() on those older cats in order to disable certificate validation if the user turned that off. @@ -1962,9 +1237,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, Darwin 15.x.x is El Capitan (10.11) */ #if CURL_BUILD_MAC - if(SSLSetSessionOption && darwinver_maj >= 13) { + if(&SSLSetSessionOption && darwinver_maj >= 13) { #else - if(SSLSetSessionOption) { + if(&SSLSetSessionOption) { #endif /* CURL_BUILD_MAC */ bool break_on_auth = !conn_config->verifypeer || ssl_cafile || ssl_cablob; @@ -2000,7 +1275,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); if(!(is_cert_file || is_cert_data)) { - failf(data, "SSL: can't load CA certificate file %s", + failf(data, "SSL: cannot load CA certificate file %s", ssl_cafile ? ssl_cafile : "(blob memory)"); return CURLE_SSL_CACERT_BADFILE; } @@ -2031,21 +1306,21 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, ciphers = conn_config->cipher_list; if(ciphers) { - err = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); + result = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); } else { - err = sectransp_set_default_ciphers(data, backend->ssl_ctx); + result = sectransp_set_default_ciphers(data, backend->ssl_ctx); } - if(err != noErr) { + if(result != CURLE_OK) { failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. " - "Error code: %d", err); + "Error code: %d", (int)result); return CURLE_SSL_CIPHER; } #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 /* We want to enable 1/n-1 when using a CBC cipher unless the user - specifically doesn't want us doing that: */ - if(SSLSetSessionOption) { + specifically does not want us doing that: */ + if(&SSLSetSessionOption) { SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, !ssl_config->enable_beast); SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, @@ -2053,8 +1328,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, } #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { char *ssl_sessionid; size_t ssl_sessionid_len; @@ -2071,10 +1346,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, /* Informational message */ infof(data, "SSL reusing session ID"); } - /* If there isn't one, then let's make one up! This has to be done prior + /* If there is not one, then let's make one up! This has to be done prior to starting the handshake. */ else { - CURLcode result; ssl_sessionid = aprintf("%s:%d:%d:%s:%d", ssl_cafile ? ssl_cafile : "(blob memory)", @@ -2089,9 +1363,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid, - ssl_sessionid_len, - sectransp_session_free); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, ssl_sessionid, + ssl_sessionid_len, + sectransp_session_free); Curl_ssl_sessionid_unlock(data); if(result) return result; @@ -2121,7 +1395,7 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) char *sep_start, *sep_end, *cert_start, *cert_end; size_t i, j, err; size_t len; - unsigned char *b64; + char *b64; /* Jump through the separators at the beginning of the certificate. */ sep_start = strstr(in, "-----"); @@ -2202,16 +1476,16 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen) return 0; } -static int append_cert_to_array(struct Curl_easy *data, - const unsigned char *buf, size_t buflen, - CFMutableArrayRef array) +static CURLcode append_cert_to_array(struct Curl_easy *data, + const unsigned char *buf, size_t buflen, + CFMutableArrayRef array) { char *certp; CURLcode result; SecCertificateRef cacert; CFDataRef certdata; - certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); + certdata = CFDataCreate(kCFAllocatorDefault, buf, (CFIndex)buflen); if(!certdata) { failf(data, "SSL: failed to allocate array for CA certificate"); return CURLE_OUT_OF_MEMORY; @@ -2248,7 +1522,8 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, const unsigned char *certbuf, size_t buflen, SSLContextRef ctx) { - int n = 0, rc; + int n = 0; + CURLcode rc; long res; unsigned char *der; size_t derlen, offset = 0; @@ -2419,7 +1694,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - /* if a path wasn't specified, don't pin */ + /* if a path was not specified, do not pin */ if(!pinnedpubkey) return CURLE_OK; @@ -2451,17 +1726,17 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, #elif SECTRANSP_PINNEDPUBKEY_V2 { - OSStatus success; - success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); - CFRelease(keyRef); - if(success != errSecSuccess || !publicKeyBits) - break; + OSStatus success; + success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, + &publicKeyBits); + CFRelease(keyRef); + if(success != errSecSuccess || !publicKeyBits) + break; } #endif /* SECTRANSP_PINNEDPUBKEY_V2 */ - pubkeylen = CFDataGetLength(publicKeyBits); + pubkeylen = (size_t)CFDataGetLength(publicKeyBits); pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits); switch(pubkeylen) { @@ -2530,24 +1805,23 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, SSLCipherSuite cipher; SSLProtocol protocol = 0; - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state - || ssl_connect_2_reading == connssl->connecting_state - || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "connect_step2"); /* Here goes nothing: */ check_handshake: + connssl->io_need = CURL_SSL_IO_NEED_NONE; err = SSLHandshake(backend->ssl_ctx); if(err != noErr) { switch(err) { - case errSSLWouldBlock: /* they're not done with us yet */ - connssl->connecting_state = backend->ssl_direction ? - ssl_connect_2_writing : ssl_connect_2_reading; + case errSSLWouldBlock: /* they are not done with us yet */ + connssl->io_need = backend->ssl_direction ? + CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; return CURLE_OK; - /* The below is errSSLServerAuthCompleted; it's not defined in + /* The below is errSSLServerAuthCompleted; it is not defined in Leopard's headers */ case -9841: if((conn_config->CAfile || conn_config->ca_info_blob) && @@ -2657,8 +1931,8 @@ check_handshake: "authority"); break; - /* This error is raised if the server's cert didn't match the server's - host name: */ + /* This error is raised if the server's cert did not match the server's + hostname: */ case errSSLHostNameMismatch: failf(data, "SSL certificate peer verification failed, the " "certificate did not match \"%s\"\n", connssl->peer.dispname); @@ -2759,7 +2033,8 @@ check_handshake: return CURLE_SSL_CONNECT_ERROR; } else { - /* we have been connected fine, we're not waiting for anything else. */ + char cipher_str[64]; + /* we have been connected fine, we are not waiting for anything else. */ connssl->connecting_state = ssl_connect_3; #ifdef SECTRANSP_PINNEDPUBKEY @@ -2777,33 +2052,30 @@ check_handshake: /* Informational message */ (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); + + sectransp_cipher_suite_get_str((uint16_t) cipher, cipher_str, + sizeof(cipher_str), true); switch(protocol) { case kSSLProtocol2: - infof(data, "SSL 2.0 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "SSL 2.0 connection using %s", cipher_str); break; case kSSLProtocol3: - infof(data, "SSL 3.0 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "SSL 3.0 connection using %s", cipher_str); break; case kTLSProtocol1: - infof(data, "TLS 1.0 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "TLS 1.0 connection using %s", cipher_str); break; #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS case kTLSProtocol11: - infof(data, "TLS 1.1 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "TLS 1.1 connection using %s", cipher_str); break; case kTLSProtocol12: - infof(data, "TLS 1.2 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "TLS 1.2 connection using %s", cipher_str); break; #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 case kTLSProtocol13: - infof(data, "TLS 1.3 connection using %s", - TLSCipherNameForNumber(cipher)); + infof(data, "TLS 1.3 connection using %s", cipher_str); break; #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ default: @@ -2811,7 +2083,8 @@ check_handshake: break; } -#if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 +#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \ + defined(HAVE_BUILTIN_AVAILABLE) if(connssl->alpn) { if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { CFArrayRef alpnArr = NULL; @@ -2835,11 +2108,8 @@ check_handshake: else infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - /* chosenProtocol is a reference to the string within alpnArr - and doesn't need to be freed separately */ + and does not need to be freed separately */ if(alpnArr) CFRelease(alpnArr); } @@ -2941,10 +2211,10 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf, /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion. The function SecTrustGetCertificateAtIndex() is officially present in Lion, but it is unfortunately also present in Snow Leopard as - private API and doesn't work as expected. So we have to look for + private API and does not 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(SecTrustCopyPublicKey) { + if(&SecTrustCopyPublicKey) { #pragma unused(server_certs) err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); /* For some reason, SSLCopyPeerTrust() can return noErr and yet return @@ -3030,7 +2300,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ + /* Find out how much more time we are allowed */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -3044,9 +2314,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -3057,14 +2325,13 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking ? 0 : timeout_ms); @@ -3094,10 +2361,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, * or epoll() will always have a valid fdset to wait on. */ result = sectransp_connect_step2(cf, data); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return result; } /* repeat step2 until all transactions are done. */ @@ -3146,6 +2410,92 @@ static CURLcode sectransp_connect(struct Curl_cfilter *cf, return CURLE_OK; } +static ssize_t sectransp_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + size_t buffersize, + CURLcode *curlcode); + +static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct st_ssl_backend_data *backend = + (struct st_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + ssize_t nread; + char buf[1024]; + size_t i; + + DEBUGASSERT(backend); + if(!backend->ssl_ctx || cf->shutdown) { + *done = TRUE; + goto out; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + + if(send_shutdown && !backend->sent_shutdown) { + OSStatus err; + + CURL_TRC_CF(data, cf, "shutdown, send close notify"); + err = SSLClose(backend->ssl_ctx); + switch(err) { + case noErr: + backend->sent_shutdown = TRUE; + break; + case errSSLWouldBlock: + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_OK; + goto out; + default: + CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)err); + result = CURLE_SEND_ERROR; + goto out; + } + } + + for(i = 0; i < 10; ++i) { + if(!backend->sent_shutdown) { + nread = sectransp_recv(cf, data, buf, (int)sizeof(buf), &result); + } + else { + /* We would like to read the close notify from the server using + * secure transport, however SSLRead() no longer works after we + * sent the notify from our side. So, we just read from the + * underlying filter and hope it will end. */ + nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); + } + CURL_TRC_CF(data, cf, "shutdown read -> %zd, %d", nread, result); + if(nread <= 0) + break; + } + + if(nread > 0) { + /* still data coming in? */ + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else if(nread == 0) { + /* We got the close notify alert and are done. */ + CURL_TRC_CF(data, cf, "shutdown done"); + *done = TRUE; + } + else if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + } + else { + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + } + +out: + cf->shutdown = (result || *done); + return result; +} + static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; @@ -3158,9 +2508,8 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->ssl_ctx) { CURL_TRC_CF(data, cf, "close"); - (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext) + if(&SSLCreateContext) CFRelease(backend->ssl_ctx); #if CURL_SUPPORT_MAC_10_8 else @@ -3173,69 +2522,6 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static int sectransp_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - ssize_t nread; - int what; - int rc; - char buf[120]; - int loop = 10; /* avoid getting stuck */ - CURLcode result; - - DEBUGASSERT(backend); - - if(!backend->ssl_ctx) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - sectransp_close(cf, data); - - rc = 0; - - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); - - CURL_TRC_CF(data, cf, "shutdown"); - while(loop--) { - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to SSL_Read now, so use read(). */ - - nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); - - if(nread < 0) { - failf(data, "read: %s", curl_easy_strerror(result)); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); - } - - return rc; -} - static size_t sectransp_version(char *buffer, size_t size) { return msnprintf(buffer, size, "SecureTransport"); @@ -3267,7 +2553,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, unsigned char *entropy, size_t length) { - /* arc4random_buf() isn't available on cats older than Lion, so let's + /* arc4random_buf() is not available on cats older than Lion, so let's do this manually for the benefit of the older cats. */ size_t i; u_int32_t random_number = 0; @@ -3298,7 +2584,7 @@ static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ static bool sectransp_false_start(void) { #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - if(SSLSetSessionOption) + if(&SSLSetSessionOption) return TRUE; #endif return FALSE; @@ -3325,7 +2611,7 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf, Now, one could interpret that as "written to the socket," but actually, it returns the amount of data that was written to a buffer internal to - the SSLContextRef instead. So it's possible for SSLWrite() to return + the SSLContextRef instead. So it is possible for SSLWrite() to return errSSLWouldBlock and a number of bytes "written" because those bytes were encrypted and written to a buffer, not to the socket. @@ -3338,7 +2624,7 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf, err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed); switch(err) { case noErr: - /* processed is always going to be 0 because we didn't write to + /* processed is always going to be 0 because we did not write to the buffer, so return how much was written to the socket */ processed = backend->ssl_write_buffered_length; backend->ssl_write_buffered_length = 0UL; @@ -3353,7 +2639,7 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf, } } else { - /* We've got new data to write: */ + /* We have got new data to write: */ err = SSLWrite(backend->ssl_ctx, mem, len, &processed); if(err != noErr) { switch(err) { @@ -3410,7 +2696,7 @@ again: *curlcode = CURLE_OK; return 0; - /* The below is errSSLPeerAuthCompleted; it's not defined in + /* The below is errSSLPeerAuthCompleted; it is not defined in Leopard's headers */ case -9841: if((conn_config->CAfile || conn_config->ca_info_blob) && @@ -3451,7 +2737,8 @@ const struct Curl_ssl Curl_ssl_sectransp = { #ifdef SECTRANSP_PINNEDPUBKEY SSLSUPP_PINNEDPUBKEY | #endif /* SECTRANSP_PINNEDPUBKEY */ - SSLSUPP_HTTPS_PROXY, + SSLSUPP_HTTPS_PROXY | + SSLSUPP_CIPHER_LIST, sizeof(struct st_ssl_backend_data), @@ -3476,9 +2763,9 @@ const struct Curl_ssl Curl_ssl_sectransp = { sectransp_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ sectransp_recv, /* recv decrypted data */ sectransp_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #ifdef __GNUC__ diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 570a10d5a..36a422678 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -68,7 +68,10 @@ #include "curl_base64.h" #include "curl_printf.h" #include "inet_pton.h" +#include "connect.h" +#include "select.h" #include "strdup.h" +#include "rand.h" /* The last #include files should be: */ #include "curl_memory.h" @@ -103,7 +106,7 @@ static CURLcode blobdup(struct curl_blob **dest, DEBUGASSERT(dest); DEBUGASSERT(!*dest); if(src) { - /* only if there's data to dupe! */ + /* only if there is data to dupe! */ struct curl_blob *d; d = malloc(sizeof(struct curl_blob) + src->len); if(!d) @@ -136,6 +139,9 @@ static const struct alpn_spec ALPN_SPEC_H11 = { { ALPN_HTTP_1_1 }, 1 }; #ifdef USE_HTTP2 +static const struct alpn_spec ALPN_SPEC_H2 = { + { ALPN_H2 }, 1 +}; static const struct alpn_spec ALPN_SPEC_H2_H11 = { { ALPN_H2, ALPN_HTTP_1_1 }, 2 }; @@ -146,13 +152,15 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn) if(!use_alpn) return NULL; #ifdef USE_HTTP2 + if(httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) + return &ALPN_SPEC_H2; if(httpwant >= CURL_HTTP_VERSION_2) return &ALPN_SPEC_H2_H11; #else (void)httpwant; #endif /* Use the ALPN protocol "http/1.1" for HTTP/1.x. - Avoid "http/1.0" because some servers don't support it. */ + Avoid "http/1.0" because some servers do not support it. */ return &ALPN_SPEC_H11; } #endif /* USE_SSL */ @@ -166,7 +174,7 @@ void Curl_ssl_easy_config_init(struct Curl_easy *data) */ data->set.ssl.primary.verifypeer = TRUE; data->set.ssl.primary.verifyhost = TRUE; - data->set.ssl.primary.sessionid = TRUE; /* session ID caching by default */ + data->set.ssl.primary.cache_session = TRUE; /* caching by default */ #ifndef CURL_DISABLE_PROXY data->set.proxy_ssl = data->set.ssl; #endif @@ -228,7 +236,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, dest->verifypeer = source->verifypeer; dest->verifyhost = source->verifyhost; dest->verifystatus = source->verifystatus; - dest->sessionid = source->sessionid; + dest->cache_session = source->cache_session; dest->ssl_options = source->ssl_options; CLONE_BLOB(cert_blob); @@ -411,23 +419,6 @@ int Curl_ssl_init(void) return Curl_ssl->init(); } -#if defined(CURL_WITH_MULTI_SSL) -static const struct Curl_ssl Curl_ssl_multi; -#endif - -/* Global cleanup */ -void Curl_ssl_cleanup(void) -{ - if(init_ssl) { - /* only cleanup if we did a previous init */ - Curl_ssl->cleanup(); -#if defined(CURL_WITH_MULTI_SSL) - Curl_ssl = &Curl_ssl_multi; -#endif - init_ssl = FALSE; - } -} - static bool ssl_prefs_check(struct Curl_easy *data) { /* check for CURLOPT_SSLVERSION invalid parameter value */ @@ -453,7 +444,7 @@ static bool ssl_prefs_check(struct Curl_easy *data) } static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, - const struct alpn_spec *alpn) + const struct alpn_spec *alpn) { struct ssl_connect_data *ctx; @@ -529,8 +520,8 @@ void Curl_ssl_sessionid_unlock(struct Curl_easy *data) } /* - * Check if there's a session ID for the given connection in the cache, and if - * there's one suitable, it is provided. Returns TRUE when no entry matched. + * Check if there is a session ID for the given connection in the cache, and if + * there is one suitable, it is provided. Returns TRUE when no entry matched. */ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -549,9 +540,9 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, if(!ssl_config) return TRUE; - DEBUGASSERT(ssl_config->primary.sessionid); + DEBUGASSERT(ssl_config->primary.cache_session); - if(!ssl_config->primary.sessionid || !data->state.session) + if(!ssl_config->primary.cache_session || !data->state.session) /* session ID reuse is disabled or the session cache has not been setup */ return TRUE; @@ -589,10 +580,9 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, } } - DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", - no_match? "Didn't find": "Found", - Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host", - cf->conn->handler->scheme, peer->hostname, peer->port)); + CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d", + no_match? "No": "Found", + cf->conn->handler->scheme, peer->hostname, peer->port); return no_match; } @@ -635,18 +625,12 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) } } -/* - * Store session id in the session cache. The ID passed on to this function - * must already have been extracted and allocated the proper way for the SSL - * layer. Curl_XXXX_session_free() will be called to free/kill the session ID - * later on. - */ -CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - void *ssl_sessionid, - size_t idsize, - Curl_ssl_sessionid_dtor *sessionid_free_cb) +CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void *ssl_sessionid, + size_t idsize, + Curl_ssl_sessionid_dtor *sessionid_free_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -657,6 +641,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, char *clone_conn_to_host = NULL; int conn_to_port; long *general_age; + void *old_sessionid; + size_t old_size; CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(ssl_sessionid); @@ -667,9 +653,20 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, return CURLE_OK; } + if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size)) { + if((old_size == idsize) && + ((old_sessionid == ssl_sessionid) || + (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) { + /* the very same */ + sessionid_free_cb(ssl_sessionid, idsize); + return CURLE_OK; + } + Curl_ssl_delsessionid(data, old_sessionid); + } + store = &data->state.session[0]; oldest_age = data->state.session[0].age; /* zero if unused */ - DEBUGASSERT(ssl_config->primary.sessionid); + DEBUGASSERT(ssl_config->primary.cache_session); (void)ssl_config; clone_host = strdup(peer->hostname); @@ -687,7 +684,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, else conn_to_port = -1; - /* Now we should add the session ID and the host name to the cache, (remove + /* Now we should add the session ID and the hostname to the cache, (remove the oldest if necessary) */ /* If using shared SSL session, lock! */ @@ -722,12 +719,12 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, store->idsize = idsize; store->sessionid_free = sessionid_free_cb; store->age = *general_age; /* set current age */ - /* free it if there's one already present */ + /* free it if there is one already present */ free(store->name); free(store->conn_to_host); - store->name = clone_host; /* clone host name */ + store->name = clone_host; /* clone hostname */ clone_host = NULL; - store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ + store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */ clone_conn_to_host = NULL; store->conn_to_port = conn_to_port; /* connect to port number */ /* port number */ @@ -753,10 +750,12 @@ out: return CURLE_OK; } -void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend) +CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, + struct dynbuf *binding) { - if(Curl_ssl->free_multi_ssl_backend_data && mbackend) - Curl_ssl->free_multi_ssl_backend_data(mbackend); + if(Curl_ssl->get_channel_binding) + return Curl_ssl->get_channel_binding(data, sockindex, binding); + return CURLE_OK; } void Curl_ssl_close_all(struct Curl_easy *data) @@ -778,19 +777,20 @@ void Curl_ssl_close_all(struct Curl_easy *data) void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { - if(!cf->connected) { - struct ssl_connect_data *connssl = cf->ctx; + struct ssl_connect_data *connssl = cf->ctx; + + if(connssl->io_need) { curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); if(sock != CURL_SOCKET_BAD) { - if(connssl->connecting_state == ssl_connect_2_writing) { + if(connssl->io_need & CURL_SSL_IO_NEED_SEND) { Curl_pollset_set_out_only(data, ps, sock); - CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" - CURL_FORMAT_SOCKET_T, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" FMT_SOCKET_T, + sock); } else { Curl_pollset_set_in_only(data, ps, sock); - CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" - CURL_FORMAT_SOCKET_T, sock); + CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" FMT_SOCKET_T, + sock); } } } @@ -901,7 +901,9 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, CURLcode result = CURLE_OK; struct dynbuf build; - Curl_dyn_init(&build, 10000); + DEBUGASSERT(certnum < ci->num_of_certs); + + Curl_dyn_init(&build, CURL_X509_STR_MAX); if(Curl_dyn_add(&build, label) || Curl_dyn_addn(&build, ":", 1) || @@ -920,11 +922,16 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, return result; } +/* get 32 bits of random */ CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *entropy, size_t length) { - return Curl_ssl->random(data, entropy, length); + DEBUGASSERT(length == sizeof(int)); + if(Curl_ssl->random) + return Curl_ssl->random(data, entropy, length); + else + return CURLE_NOT_BUILT_IN; } /* @@ -1000,7 +1007,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, (void)data; #endif - /* if a path wasn't specified, don't pin */ + /* if a path was not specified, do not pin */ if(!pinnedpubkey) return CURLE_OK; if(!pubkey || !pubkeylen) @@ -1048,7 +1055,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, end_pos = strstr(begin_pos, ";sha256//"); /* * if there is an end_pos, null terminate, - * otherwise it'll go to the end of the original string + * otherwise it will go to the end of the original string */ if(end_pos) end_pos[0] = '\0'; @@ -1094,7 +1101,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, /* * if the size of our certificate is bigger than the file - * size then it can't match + * size then it cannot match */ size = curlx_sotouz((curl_off_t) filesize); if(pubkeylen > size) @@ -1112,7 +1119,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, if((int) fread(buf, size, 1, fp) != 1) break; - /* If the sizes are the same, it can't be base64 encoded, must be der */ + /* If the sizes are the same, it cannot be base64 encoded, must be der */ if(pubkeylen == size) { if(!memcmp(pubkey, buf, pubkeylen)) result = CURLE_OK; @@ -1120,18 +1127,18 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, } /* - * Otherwise we will assume it's PEM and try to decode it + * Otherwise we will assume it is PEM and try to decode it * after placing null terminator */ buf[size] = '\0'; pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); - /* if it wasn't read successfully, exit */ + /* if it was not read successfully, exit */ if(pem_read) break; /* - * if the size of our certificate doesn't match the size of - * the decoded file, they can't be the same, otherwise compare + * if the size of our certificate does not match the size of + * the decoded file, they cannot be the same, otherwise compare */ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) result = CURLE_OK; @@ -1173,12 +1180,18 @@ int Curl_none_init(void) void Curl_none_cleanup(void) { } -int Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM, - struct Curl_easy *data UNUSED_PARAM) +CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM, + struct Curl_easy *data UNUSED_PARAM, + bool send_shutdown UNUSED_PARAM, + bool *done) { (void)data; (void)cf; - return 0; + (void)send_shutdown; + /* Every SSL backend should have a shutdown implementation. Until we + * have implemented that, we put this fake in place. */ + *done = TRUE; + return CURLE_OK; } int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1188,16 +1201,6 @@ int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) return -1; } -CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy UNUSED_PARAM, - size_t length UNUSED_PARAM) -{ - (void)data; - (void)entropy; - (void)length; - return CURLE_NOT_BUILT_IN; -} - void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM) { (void)data; @@ -1324,7 +1327,7 @@ static const struct Curl_ssl Curl_ssl_multi = { Curl_none_check_cxn, /* check_cxn */ Curl_none_shutdown, /* shutdown */ Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ + NULL, /* random */ Curl_none_cert_status_request, /* cert_status_request */ multissl_connect, /* connect */ multissl_connect_nonblocking, /* connect_nonblocking */ @@ -1339,9 +1342,9 @@ static const struct Curl_ssl Curl_ssl_multi = { NULL, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ multissl_recv_plain, /* recv decrypted data */ multissl_send_plain, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; const struct Curl_ssl *Curl_ssl = @@ -1349,8 +1352,6 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_multi; #elif defined(USE_WOLFSSL) &Curl_ssl_wolfssl; -#elif defined(USE_SECTRANSP) - &Curl_ssl_sectransp; #elif defined(USE_GNUTLS) &Curl_ssl_gnutls; #elif defined(USE_MBEDTLS) @@ -1359,6 +1360,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_rustls; #elif defined(USE_OPENSSL) &Curl_ssl_openssl; +#elif defined(USE_SECTRANSP) + &Curl_ssl_sectransp; #elif defined(USE_SCHANNEL) &Curl_ssl_schannel; #elif defined(USE_BEARSSL) @@ -1371,9 +1374,6 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_WOLFSSL) &Curl_ssl_wolfssl, #endif -#if defined(USE_SECTRANSP) - &Curl_ssl_sectransp, -#endif #if defined(USE_GNUTLS) &Curl_ssl_gnutls, #endif @@ -1383,6 +1383,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_OPENSSL) &Curl_ssl_openssl, #endif +#if defined(USE_SECTRANSP) + &Curl_ssl_sectransp, +#endif #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif @@ -1395,6 +1398,19 @@ static const struct Curl_ssl *available_backends[] = { NULL }; +/* Global cleanup */ +void Curl_ssl_cleanup(void) +{ + if(init_ssl) { + /* only cleanup if we did a previous init */ + Curl_ssl->cleanup(); +#if defined(CURL_WITH_MULTI_SSL) + Curl_ssl = &Curl_ssl_multi; +#endif + init_ssl = FALSE; + } +} + static size_t multissl_version(char *buffer, size_t size) { static const struct Curl_ssl *selected; @@ -1562,69 +1578,70 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, int transport) { const char *ehostname, *edispname; - int eport; + CURLcode result = CURLE_OUT_OF_MEMORY; - /* We need the hostname for SNI negotiation. Once handshaked, this - * remains the SNI hostname for the TLS connection. But when the - * connection is reused, the settings in cf->conn might change. - * So we keep a copy of the hostname we use for SNI. + /* We expect a clean struct, e.g. called only ONCE */ + DEBUGASSERT(peer); + DEBUGASSERT(!peer->hostname); + DEBUGASSERT(!peer->dispname); + DEBUGASSERT(!peer->sni); + /* We need the hostname for SNI negotiation. Once handshaked, this remains + * the SNI hostname for the TLS connection. When the connection is reused, + * the settings in cf->conn might change. We keep a copy of the hostname we + * use for SNI. */ + peer->transport = transport; #ifndef CURL_DISABLE_PROXY if(Curl_ssl_cf_is_proxy(cf)) { ehostname = cf->conn->http_proxy.host.name; edispname = cf->conn->http_proxy.host.dispname; - eport = cf->conn->http_proxy.port; + peer->port = cf->conn->http_proxy.port; } else #endif { ehostname = cf->conn->host.name; edispname = cf->conn->host.dispname; - eport = cf->conn->remote_port; + peer->port = cf->conn->remote_port; } - /* change if ehostname changed */ - DEBUGASSERT(!ehostname || ehostname[0]); - if(ehostname && (!peer->hostname - || strcmp(ehostname, peer->hostname))) { - Curl_ssl_peer_cleanup(peer); - peer->hostname = strdup(ehostname); - if(!peer->hostname) { - Curl_ssl_peer_cleanup(peer); - return CURLE_OUT_OF_MEMORY; - } - if(!edispname || !strcmp(ehostname, edispname)) - peer->dispname = peer->hostname; - else { - peer->dispname = strdup(edispname); - if(!peer->dispname) { - Curl_ssl_peer_cleanup(peer); - return CURLE_OUT_OF_MEMORY; - } - } + /* hostname MUST exist and not be empty */ + if(!ehostname || !ehostname[0]) { + result = CURLE_FAILED_INIT; + goto out; + } - peer->type = get_peer_type(peer->hostname); - if(peer->type == CURL_SSL_PEER_DNS && peer->hostname[0]) { - /* not an IP address, normalize according to RCC 6066 ch. 3, - * max len of SNI is 2^16-1, no trailing dot */ - size_t len = strlen(peer->hostname); - if(len && (peer->hostname[len-1] == '.')) - len--; - if(len < USHRT_MAX) { - peer->sni = calloc(1, len + 1); - if(!peer->sni) { - Curl_ssl_peer_cleanup(peer); - return CURLE_OUT_OF_MEMORY; - } - Curl_strntolower(peer->sni, peer->hostname, len); - peer->sni[len] = 0; - } + peer->hostname = strdup(ehostname); + if(!peer->hostname) + goto out; + if(!edispname || !strcmp(ehostname, edispname)) + peer->dispname = peer->hostname; + else { + peer->dispname = strdup(edispname); + if(!peer->dispname) + goto out; + } + peer->type = get_peer_type(peer->hostname); + if(peer->type == CURL_SSL_PEER_DNS) { + /* not an IP address, normalize according to RCC 6066 ch. 3, + * max len of SNI is 2^16-1, no trailing dot */ + size_t len = strlen(peer->hostname); + if(len && (peer->hostname[len-1] == '.')) + len--; + if(len < USHRT_MAX) { + peer->sni = calloc(1, len + 1); + if(!peer->sni) + goto out; + Curl_strntolower(peer->sni, peer->hostname, len); + peer->sni[len] = 0; } - } - peer->port = eport; - peer->transport = transport; - return CURLE_OK; + result = CURLE_OK; + +out: + if(result) + Curl_ssl_peer_cleanup(peer); + return result; } static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1663,22 +1680,29 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } + if(!cf->next) { + *done = FALSE; + return CURLE_FAILED_INIT; + } + + if(!cf->next->connected) { + result = cf->next->cft->do_connect(cf->next, data, blocking, done); + if(result || !*done) + return result; + } + CF_DATA_SAVE(save, cf, data); CURL_TRC_CF(data, cf, "cf_connect()"); - (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); DEBUGASSERT(connssl); - DEBUGASSERT(cf->conn->host.name); - - result = cf->next->cft->do_connect(cf->next, data, blocking, done); - if(result || !*done) - goto out; *done = FALSE; - result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); - if(result) - goto out; + if(!connssl->peer.hostname) { + result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP); + if(result) + goto out; + } if(blocking) { result = ssl_connect(cf, data); @@ -1716,11 +1740,12 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf, static ssize_t ssl_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, - CURLcode *err) + bool eos, CURLcode *err) { struct cf_call_data save; ssize_t nwritten; + (void)eos; /* unused */ CF_DATA_SAVE(save, cf, data); *err = CURLE_OK; nwritten = Curl_ssl->send_plain(cf, data, buf, len, err); @@ -1751,17 +1776,34 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, return nread; } -static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { - struct cf_call_data save; + CURLcode result = CURLE_OK; + + *done = TRUE; + if(!cf->shutdown) { + struct cf_call_data save; - if(!cf->connected) { CF_DATA_SAVE(save, cf, data); - Curl_ssl->adjust_pollset(cf, data, ps); + result = Curl_ssl->shut_down(cf, data, TRUE, done); + CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); } + return result; +} + +static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_call_data save; + + CF_DATA_SAVE(save, cf, data); + Curl_ssl->adjust_pollset(cf, data, ps); + CF_DATA_RESTORE(cf, save); } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, @@ -1851,6 +1893,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, + ssl_cf_shutdown, Curl_cf_def_get_host, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1871,6 +1914,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, + ssl_cf_shutdown, Curl_cf_def_get_host, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1982,10 +2026,10 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, #endif /* !CURL_DISABLE_PROXY */ -bool Curl_ssl_supports(struct Curl_easy *data, int option) +bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option) { (void)data; - return (Curl_ssl->supports & option)? TRUE : FALSE; + return (Curl_ssl->supports & ssl_option)? TRUE : FALSE; } static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) @@ -2021,19 +2065,77 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, return result; } +static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct cf_call_data save; + CURLcode result = CURLE_OK; + timediff_t timeout_ms; + int what, loop = 10; + + if(cf->shutdown) { + *done = TRUE; + return CURLE_OK; + } + CF_DATA_SAVE(save, cf, data); + + *done = FALSE; + while(!result && !*done && loop--) { + timeout_ms = Curl_shutdown_timeleft(cf->conn, cf->sockindex, NULL); + + if(timeout_ms < 0) { + /* no need to continue if time is already up */ + failf(data, "SSL shutdown timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + result = Curl_ssl->shut_down(cf, data, send_shutdown, done); + if(result ||*done) + goto out; + + if(connssl->io_need) { + what = Curl_conn_cf_poll(cf, data, timeout_ms); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + result = CURLE_RECV_ERROR; + goto out; + } + else if(0 == what) { + /* timeout */ + failf(data, "SSL shutdown timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + /* socket is readable or writable */ + } + } +out: + CF_DATA_RESTORE(cf, save); + cf->shutdown = (result || *done); + return result; +} + CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex) + int sockindex, bool send_shutdown) { struct Curl_cfilter *cf, *head; CURLcode result = CURLE_OK; - (void)data; head = data->conn? data->conn->cfilter[sockindex] : NULL; for(cf = head; cf; cf = cf->next) { if(cf->cft == &Curl_cft_ssl) { - if(Curl_ssl->shut_down(cf, data)) + bool done; + CURL_TRC_CF(data, cf, "shutdown and remove SSL, start"); + Curl_shutdown_start(data, sockindex, NULL); + result = vtls_shutdown_blocking(cf, data, send_shutdown, &done); + Curl_shutdown_clear(data, sockindex); + if(!result && !done) /* blocking failed? */ result = CURLE_SSL_SHUTDOWN_FAILED; Curl_conn_cf_discard_sub(head, cf, data, FALSE); + CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); break; } } @@ -2118,7 +2220,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, const unsigned char *proto, size_t proto_len) { - int can_multi = 0; unsigned char *palpn = #ifndef CURL_DISABLE_PROXY (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf))? @@ -2137,14 +2238,12 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, else if(proto_len == ALPN_H2_LENGTH && !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) { *palpn = CURL_HTTP_VERSION_2; - can_multi = 1; } #endif #ifdef USE_HTTP3 else if(proto_len == ALPN_H3_LENGTH && !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) { *palpn = CURL_HTTP_VERSION_3; - can_multi = 1; } #endif else { @@ -2163,9 +2262,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, } out: - if(!Curl_ssl_cf_is_proxy(cf)) - Curl_multiuse_state(data, can_multi? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index c40ff2620..fce1e0018 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -38,6 +38,8 @@ struct Curl_ssl_session; #define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ #define SSLSUPP_CAINFO_BLOB (1<<6) #define SSLSUPP_ECH (1<<7) +#define SSLSUPP_CA_CACHE (1<<8) +#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -52,7 +54,6 @@ struct Curl_ssl_session; /* Curl_multi SSL backend-specific data; declared differently by each SSL backend */ -struct multi_ssl_backend_data; struct Curl_cfilter; CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, @@ -92,7 +93,7 @@ CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, void Curl_ssl_conn_config_cleanup(struct connectdata *conn); /** - * Return TRUE iff SSL configuration from `conn` is functionally the + * Return TRUE iff SSL configuration from `data` is functionally the * same as the one on `candidate`. * @param proxy match the proxy SSL config or the main one */ @@ -131,6 +132,7 @@ CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t); void Curl_ssl_version(char *buffer, size_t size); /* Certificate information list handling. */ +#define CURL_X509_STR_MAX 100000 void Curl_ssl_free_certinfo(struct Curl_easy *data); CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num); @@ -181,7 +183,24 @@ bool Curl_ssl_cert_status_request(void); bool Curl_ssl_false_start(struct Curl_easy *data); -void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend); +/* The maximum size of the SSL channel binding is 85 bytes, as defined in + * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long, + * and SHA-512 is the longest supported hash algorithm, with a digest length of + * 64 bytes. + * The maximum size of the channel binding is therefore 21 + 64 = 85 bytes. + */ +#define SSL_CB_MAX_SIZE 85 + +/* Return the tls-server-end-point channel binding, including the + * 'tls-server-end-point:' prefix. + * If successful, the data is written to the dynbuf, and CURLE_OK is returned. + * The dynbuf MUST HAVE a minimum toobig size of SSL_CB_MAX_SIZE. + * If the dynbuf is too small, CURLE_OUT_OF_MEMORY is returned. + * If channel binding is not supported, binding stays empty and CURLE_OK is + * returned. + */ +CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, + struct dynbuf *binding); #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ @@ -193,7 +212,7 @@ CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data); CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, - int sockindex); + int sockindex, bool send_shutdown); #ifndef CURL_DISABLE_PROXY CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, @@ -205,7 +224,7 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, * Option is one of the defined SSLSUPP_* values. * `data` maybe NULL for the features of the default implementation. */ -bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option); +bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option); /** * Get the internal ssl instance (like OpenSSL's SSL*) from the filter @@ -252,7 +271,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_ssl_cfilter_remove(a,b) CURLE_OK +#define Curl_ssl_cfilter_remove(a,b,c) CURLE_OK #define Curl_ssl_cf_get_config(a,b) NULL #define Curl_ssl_cf_get_primary_config(a) NULL #endif diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index 5259babb2..836bfad70 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h @@ -64,15 +64,34 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, const unsigned char *proto, size_t proto_len); +/* enum for the nonblocking SSL connection state machine */ +typedef enum { + ssl_connect_1, + ssl_connect_2, + ssl_connect_3, + ssl_connect_done +} ssl_connect_state; + +typedef enum { + ssl_connection_none, + ssl_connection_negotiating, + ssl_connection_complete +} ssl_connection_state; + +#define CURL_SSL_IO_NEED_NONE (0) +#define CURL_SSL_IO_NEED_RECV (1<<0) +#define CURL_SSL_IO_NEED_SEND (1<<1) + /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { - ssl_connection_state state; - ssl_connect_state connecting_state; struct ssl_peer peer; const struct alpn_spec *alpn; /* ALPN to use or NULL for none */ void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ + ssl_connection_state state; + ssl_connect_state connecting_state; + int io_need; /* TLS signals special SEND/RECV needs */ BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ }; @@ -99,8 +118,8 @@ struct Curl_ssl { size_t (*version)(char *buffer, size_t size); int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data); - int (*shut_down)(struct Curl_cfilter *cf, - struct Curl_easy *data); + CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data, + bool send_shutdown, bool *done); bool (*data_pending)(struct Curl_cfilter *cf, const struct Curl_easy *data); @@ -115,9 +134,8 @@ struct Curl_ssl { struct Curl_easy *data, bool *done); - /* During handshake, adjust the pollset to include the socket - * for POLLOUT or POLLIN as needed. - * Mandatory. */ + /* During handshake/shutdown, adjust the pollset to include the socket + * for POLLOUT or POLLIN as needed. Mandatory. */ void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps); void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); @@ -135,13 +153,14 @@ struct Curl_ssl { bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data); - void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend); - ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *code); ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, const void *mem, size_t len, CURLcode *code); + CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex, + struct dynbuf *binding); + }; extern const struct Curl_ssl *Curl_ssl; @@ -149,10 +168,9 @@ extern const struct Curl_ssl *Curl_ssl; int Curl_none_init(void); void Curl_none_cleanup(void); -int Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data); +CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, + bool send_shutdown, bool *done); int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data); -CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, - size_t length); void Curl_none_close_all(struct Curl_easy *data); void Curl_none_session_free(void *ptr); bool Curl_none_data_pending(struct Curl_cfilter *cf, @@ -181,19 +199,22 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, const struct ssl_peer *peer, void **ssl_sessionid, size_t *idsize); /* set 0 if unknown */ -/* add a new session ID + +/* Set a TLS session ID for `peer`. Replaces an existing session ID if + * not already the very same. * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). + * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` + * to deallocate it. Is called in all outcomes, either right away or + * later when the session cache is cleaned up. * Caller must ensure that it has properly shared ownership of this sessionid * object with cache (e.g. incrementing refcount on success) - * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` - * to destroy it in case of failure or later removal. */ -CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, - struct Curl_easy *data, - const struct ssl_peer *peer, - void *ssl_sessionid, - size_t idsize, - Curl_ssl_sessionid_dtor *sessionid_free_cb); +CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct ssl_peer *peer, + void *sessionid, + size_t sessionid_size, + Curl_ssl_sessionid_dtor *sessionid_free_cb); #include "openssl.h" /* OpenSSL versions */ #include "gtls.h" /* GnuTLS versions */ @@ -202,7 +223,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, #include "sectransp.h" /* SecureTransport (Darwin) version */ #include "mbedtls.h" /* mbedTLS versions */ #include "bearssl.h" /* BearSSL versions */ -#include "rustls.h" /* rustls versions */ +#include "rustls.h" /* Rustls versions */ #endif /* USE_SSL */ diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 2c92f56ea..bd7963ec3 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -36,6 +36,10 @@ #include #include +#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */ +#error "wolfSSL version should be at least 3.4.6" +#endif + /* To determine what functions are available we rely on one or both of: - the user's options.h generated by wolfSSL - the symbols detected by curl's configure @@ -99,17 +103,11 @@ #undef USE_BIO_CHAIN #endif -struct wolfssl_ssl_backend_data { - WOLFSSL_CTX *ctx; - WOLFSSL *handle; - CURLcode io_result; /* result of last BIO cfilter operation */ -}; - #ifdef OPENSSL_EXTRA /* * Availability note: * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in - * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that + * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that * option is not set, then TLS 1.3 will not be logged. * For TLS 1.2 and before, we use wolfSSL_get_keys(). * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA @@ -207,7 +205,7 @@ wolfssl_log_tls12_secret(SSL *ssl) } #endif /* OPENSSL_EXTRA */ -static int do_file_type(const char *type) +static int wolfssl_do_file_type(const char *type) { if(!type || !type[0]) return SSL_FILETYPE_PEM; @@ -218,7 +216,7 @@ static int do_file_type(const char *type) return -1; } -#ifdef HAVE_LIBOQS +#ifdef WOLFSSL_HAVE_KYBER struct group_name_map { const word16 group; const char *name; @@ -290,20 +288,35 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio, { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + ssize_t nwritten, skiplen = 0; CURLcode result = CURLE_OK; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); + if(backend->shutting_down && backend->io_send_blocked_len && + (backend->io_send_blocked_len < blen)) { + /* bug in wolfSSL: + * It adds the close notify message again every time we retry + * sending during shutdown. */ + CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d" + " to %d bytes", blen, backend->io_send_blocked_len); + skiplen = (ssize_t)(blen - backend->io_send_blocked_len); + blen = backend->io_send_blocked_len; + } + nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result); backend->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", blen, nwritten, result); wolfSSL_BIO_clear_retry_flags(bio); - if(nwritten < 0 && CURLE_AGAIN == result) + if(nwritten < 0 && CURLE_AGAIN == result) { BIO_set_retry_write(bio); + if(backend->shutting_down && !backend->io_send_blocked_len) + backend->io_send_blocked_len = blen; + } + else if(!result && skiplen) + nwritten += skiplen; return (int)nwritten; } @@ -311,8 +324,8 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_OK; @@ -357,6 +370,335 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */ +static CURLcode populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store, + struct wolfssl_ctx *wssl) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + bool imported_native_ca = false; + +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) + /* load native CA certificates */ + if(ssl_config->native_ca_store) { + if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) { + infof(data, "error importing native CA store, continuing anyway"); + } + else { + imported_native_ca = true; + infof(data, "successfully imported native CA store"); + wssl->x509_store_setup = TRUE; + } + } +#endif /* !NO_FILESYSTEM */ + + /* load certificate blob */ + if(ca_info_blob) { + if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data, + (long)ca_info_blob->len, + SSL_FILETYPE_PEM) != SSL_SUCCESS) { + if(imported_native_ca) { + infof(data, "error importing CA certificate blob, continuing anyway"); + } + else { + failf(data, "error importing CA certificate blob"); + return CURLE_SSL_CACERT_BADFILE; + } + } + else { + infof(data, "successfully imported CA certificate blob"); + wssl->x509_store_setup = TRUE; + } + } + +#ifndef NO_FILESYSTEM + /* load trusted cacert from file if not blob */ + + CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d", + ssl_cafile? ssl_cafile : "none", !!ca_info_blob); + if(!store) + return CURLE_OUT_OF_MEMORY; + + if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) { + int rc = + wolfSSL_CTX_load_verify_locations_ex(wssl->ctx, + ssl_cafile, + ssl_capath, + WOLFSSL_LOAD_FLAG_IGNORE_ERR); + if(SSL_SUCCESS != rc) { + if(conn_config->verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + /* Just continue with a warning if no strict certificate + verification is required. */ + infof(data, "error setting certificate verify locations," + " continuing anyway:"); + } + } + else { + /* Everything is fine. */ + infof(data, "successfully set certificate verify locations:"); + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#endif + (void)store; + wssl->x509_store_setup = TRUE; + return CURLE_OK; +} + +/* key to use at `multi->proto_hash` */ +#define MPROTO_WSSL_X509_KEY "tls:wssl:x509:share" + +struct wssl_x509_share { + char *CAfile; /* CAfile path used to generate X509 store */ + WOLFSSL_X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; + +static void wssl_x509_share_free(void *key, size_t key_len, void *p) +{ + struct wssl_x509_share *share = p; + DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY)-1)); + DEBUGASSERT(!memcmp(MPROTO_WSSL_X509_KEY, key, key_len)); + (void)key; + (void)key_len; + if(share->store) { + wolfSSL_X509_STORE_free(share->store); + } + free(share->CAfile); + free(share); +} + +static bool +cached_x509_store_expired(const struct Curl_easy *data, + const struct wssl_x509_share *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool +cached_x509_store_different(struct Curl_cfilter *cf, + const struct wssl_x509_share *mb) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!mb->CAfile || !conn_config->CAfile) + return mb->CAfile != conn_config->CAfile; + + return strcmp(mb->CAfile, conn_config->CAfile); +} + +static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + WOLFSSL_X509_STORE *store = NULL; + + DEBUGASSERT(multi); + share = multi? Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL; + if(share && share->store && + !cached_x509_store_expired(data, share) && + !cached_x509_store_different(cf, share)) { + store = share->store; + } + + return store; +} + +static void set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi; + struct wssl_x509_share *share; + + DEBUGASSERT(multi); + if(!multi) + return; + share = Curl_hash_pick(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1); + + if(!share) { + share = calloc(1, sizeof(*share)); + if(!share) + return; + if(!Curl_hash_add2(&multi->proto_hash, + (void *)MPROTO_WSSL_X509_KEY, + sizeof(MPROTO_WSSL_X509_KEY)-1, + share, wssl_x509_share_free)) { + free(share); + return; + } + } + + if(wolfSSL_X509_STORE_up_ref(store)) { + char *CAfile = NULL; + + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + X509_STORE_free(store); + return; + } + } + + if(share->store) { + X509_STORE_free(share->store); + free(share->CAfile); + } + + share->time = Curl_now(); + share->store = store; + share->CAfile = CAfile; + } +} + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + WOLFSSL_X509_STORE *cached_store; + bool cache_criteria_met; + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to wolfSSL's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store; + + cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL; + if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) { + /* The cached store is already in use, do nothing. */ + } + else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) { + wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store); + } + else if(cache_criteria_met) { + /* wolfSSL's initial store in CTX is not shareable by default. + * Make a new one, suitable for adding to the cache. See #14278 */ + X509_STORE *store = wolfSSL_X509_STORE_new(); + if(!store) { + failf(data, "SSL: could not create a X509 store"); + return CURLE_OUT_OF_MEMORY; + } + wolfSSL_CTX_set_cert_store(wssl->ctx, store); + + result = populate_x509_store(cf, data, store, wssl); + if(!result) { + set_cached_x509_store(cf, data, store); + } + } + else { + /* We never share the CTX's store, use it. */ + X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + result = populate_x509_store(cf, data, store, wssl); + } + + return result; +} + +#ifdef WOLFSSL_TLS13 +static size_t +wssl_get_default_ciphers(bool tls13, char *buf, size_t size) +{ + size_t len = 0; + char *term = buf; + int i; + char *str; + size_t n; + + for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) { + if((strncmp(str, "TLS13", 5) == 0) != tls13) + continue; + + n = strlen(str); + if(buf && len + n + 1 <= size) { + memcpy(buf + len, str, n); + term = buf + len + n; + *term = ':'; + } + len += n + 1; + } + + if(buf) + *term = '\0'; + + return len > 0 ? len - 1 : 0; +} +#endif + +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ +static int +wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) +{ + int res; + switch(version) { + default: + case TLS1_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1); + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_1_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_1); + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_2_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_2); +#ifdef WOLFSSL_TLS13 + if(res == WOLFSSL_SUCCESS) + return res; + FALLTHROUGH(); + case TLS1_3_VERSION: + res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_3); +#endif + } + return res; +} +static int +wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version) +{ + (void) ctx, (void) version; + return WOLFSSL_NOT_IMPLEMENTED; +} +#define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version +#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version +#endif + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -364,136 +706,98 @@ static void wolfssl_bio_cf_free_methods(void) static CURLcode wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { + int res; char *ciphers, *curves; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char * const ssl_capath = conn_config->CApath; WOLFSSL_METHOD* req_method = NULL; -#ifdef HAVE_LIBOQS - word16 oqsAlg = 0; +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem = 0; size_t idx = 0; #endif -#ifdef HAVE_SNI - bool sni = FALSE; -#define use_sni(x) sni = (x) -#else -#define use_sni(x) Curl_nop_stmt -#endif - bool imported_native_ca = false; - bool imported_ca_info_blob = false; DEBUGASSERT(backend); if(connssl->state == ssl_connection_complete) return CURLE_OK; - if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) { - failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ + req_method = wolfSSLv23_client_method(); +#else + req_method = wolfTLS_client_method(); +#endif + if(!req_method) { + failf(data, "wolfSSL: could not create a client method"); + return CURLE_OUT_OF_MEMORY; + } + + if(backend->ctx) + wolfSSL_CTX_free(backend->ctx); + + backend->ctx = wolfSSL_CTX_new(req_method); + if(!backend->ctx) { + failf(data, "wolfSSL: could not create a context"); + return CURLE_OUT_OF_MEMORY; } - /* check to see if we've been told to use an explicit SSL/TLS version */ switch(conn_config->version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ - /* minimum protocol version is set later after the CTX object is created */ - req_method = SSLv23_client_method(); -#else - infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " - "TLS 1.0 is used exclusively"); - req_method = TLSv1_client_method(); -#endif - use_sni(TRUE); - break; case CURL_SSLVERSION_TLSv1_0: -#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) - req_method = TLSv1_client_method(); - use_sni(TRUE); + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION); break; -#else - failf(data, "wolfSSL does not support TLS 1.0"); - return CURLE_NOT_BUILT_IN; -#endif case CURL_SSLVERSION_TLSv1_1: -#ifndef NO_OLD_TLS - req_method = TLSv1_1_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.1"); - return CURLE_NOT_BUILT_IN; -#endif + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION); break; case CURL_SSLVERSION_TLSv1_2: -#ifndef WOLFSSL_NO_TLS12 - req_method = TLSv1_2_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.2"); - return CURLE_NOT_BUILT_IN; -#endif + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION); break; - case CURL_SSLVERSION_TLSv1_3: #ifdef WOLFSSL_TLS13 - req_method = wolfTLSv1_3_client_method(); - use_sni(TRUE); + case CURL_SSLVERSION_TLSv1_3: + res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION); break; -#else - failf(data, "wolfSSL: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; #endif default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); + failf(data, "wolfSSL: unsupported minimum TLS version value"); return CURLE_SSL_CONNECT_ERROR; } - - if(!req_method) { - failf(data, "SSL: couldn't create a method"); - return CURLE_OUT_OF_MEMORY; - } - - if(backend->ctx) - wolfSSL_CTX_free(backend->ctx); - backend->ctx = wolfSSL_CTX_new(req_method); - - if(!backend->ctx) { - failf(data, "SSL: couldn't create a context"); - return CURLE_OUT_OF_MEMORY; + if(res != WOLFSSL_SUCCESS) { + failf(data, "wolfSSL: failed set the minimum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ - /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is - * whatever minimum version of TLS was built in and at least TLS 1.0. For - * later library versions that could change (eg TLS 1.0 built in but - * defaults to TLS 1.1) so we have this short circuit evaluation to find - * the minimum supported TLS version. - */ - if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1) + switch(conn_config->version_max) { #ifdef WOLFSSL_TLS13 - && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1) -#endif - ) { - failf(data, "SSL: couldn't set the minimum protocol version"); - return CURLE_SSL_CONNECT_ERROR; - } + case CURL_SSLVERSION_MAX_TLSv1_3: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + break; #endif - FALLTHROUGH(); - default: + case CURL_SSLVERSION_MAX_TLSv1_2: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION); + break; + case CURL_SSLVERSION_MAX_TLSv1_1: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION); + break; + case CURL_SSLVERSION_MAX_TLSv1_0: + res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION); + break; + case CURL_SSLVERSION_MAX_DEFAULT: + case CURL_SSLVERSION_MAX_NONE: + res = WOLFSSL_SUCCESS; break; + default: + failf(data, "wolfSSL: unsupported maximum TLS version value"); + return CURLE_SSL_CONNECT_ERROR; + } + if(res != WOLFSSL_SUCCESS) { + failf(data, "wolfSSL: failed set the maximum TLS version"); + return CURLE_SSL_CONNECT_ERROR; } +#ifndef WOLFSSL_TLS13 ciphers = conn_config->cipher_list; if(ciphers) { if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { @@ -502,19 +806,57 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } infof(data, "Cipher selection: %s", ciphers); } +#else + if(conn_config->cipher_list || conn_config->cipher_list13) { + const char *ciphers12 = conn_config->cipher_list; + const char *ciphers13 = conn_config->cipher_list13; + + /* Set ciphers to a combination of ciphers_list and ciphers_list13. + * If cipher_list is not set use the default TLSv1.2 (1.1, 1.0) ciphers. + * If cipher_list13 is not set use the default TLSv1.3 ciphers. */ + size_t len13 = ciphers13 ? strlen(ciphers13) + : wssl_get_default_ciphers(true, NULL, 0); + size_t len12 = ciphers12 ? strlen(ciphers12) + : wssl_get_default_ciphers(false, NULL, 0); + + ciphers = malloc(len13 + 1 + len12 + 1); + if(!ciphers) + return CURLE_OUT_OF_MEMORY; + + if(ciphers13) + memcpy(ciphers, ciphers13, len13); + else + wssl_get_default_ciphers(true, ciphers, len13 + 1); + ciphers[len13] = ':'; + + if(ciphers12) + memcpy(ciphers + len13 + 1, ciphers12, len12); + else + wssl_get_default_ciphers(false, ciphers + len13 + 1, len12 + 1); + ciphers[len13 + 1 + len12] = '\0'; + + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + free(ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + free(ciphers); + } +#endif curves = conn_config->curves; if(curves) { -#ifdef HAVE_LIBOQS +#ifdef WOLFSSL_HAVE_KYBER for(idx = 0; gnm[idx].name != NULL; idx++) { if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - oqsAlg = gnm[idx].group; + pqkem = gnm[idx].group; break; } } - if(oqsAlg == 0) + if(pqkem == 0) #endif { if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { @@ -524,99 +866,89 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } -#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS) - /* load native CA certificates */ - if(ssl_config->native_ca_store) { - if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) { - infof(data, "error importing native CA store, continuing anyway"); - } - else { - imported_native_ca = true; - infof(data, "successfully imported native CA store"); - } - } -#endif /* !NO_FILESYSTEM */ + /* Load the client certificate, and private key */ +#ifndef NO_FILESYSTEM + if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) { + const char *cert_file = ssl_config->primary.clientcert; + const char *key_file = ssl_config->key; + const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; + const struct curl_blob *key_blob = ssl_config->key_blob; + int file_type = wolfssl_do_file_type(ssl_config->cert_type); + int rc; - /* load certificate blob */ - if(ca_info_blob) { - if(wolfSSL_CTX_load_verify_buffer(backend->ctx, ca_info_blob->data, - ca_info_blob->len, - SSL_FILETYPE_PEM) != SSL_SUCCESS) { - if(imported_native_ca) { - infof(data, "error importing CA certificate blob, continuing anyway"); - } - else { - failf(data, "error importing CA certificate blob"); - return CURLE_SSL_CACERT_BADFILE; - } + switch(file_type) { + case WOLFSSL_FILETYPE_PEM: + rc = cert_blob ? + wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx, + cert_blob->data, + (long)cert_blob->len) : + wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file); + break; + case WOLFSSL_FILETYPE_ASN1: + rc = cert_blob ? + wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data, + (long)cert_blob->len, file_type) : + wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type); + break; + default: + failf(data, "unknown cert type"); + return CURLE_BAD_FUNCTION_ARGUMENT; } - else { - imported_ca_info_blob = true; - infof(data, "successfully imported CA certificate blob"); + if(rc != 1) { + failf(data, "unable to use client certificate"); + return CURLE_SSL_CONNECT_ERROR; } - } -#ifndef NO_FILESYSTEM - /* load trusted cacert from file if not blob */ - if(ssl_cafile || ssl_capath) { - int rc = - wolfSSL_CTX_load_verify_locations_ex(backend->ctx, - ssl_cafile, - ssl_capath, - WOLFSSL_LOAD_FLAG_IGNORE_ERR); - if(SSL_SUCCESS != rc) { - if(conn_config->verifypeer && !imported_ca_info_blob && - !imported_native_ca) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - /* Just continue with a warning if no strict certificate - verification is required. */ - infof(data, "error setting certificate verify locations," - " continuing anyway:"); - } + if(!key_blob && !key_file) { + key_blob = cert_blob; + key_file = cert_file; } - else { - /* Everything is fine. */ - infof(data, "successfully set certificate verify locations:"); + else + file_type = wolfssl_do_file_type(ssl_config->key_type); + + rc = key_blob ? + wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data, + (long)key_blob->len, file_type) : + wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type); + if(rc != 1) { + failf(data, "unable to set private key"); + return CURLE_SSL_CONNECT_ERROR; } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); } +#else /* NO_FILESYSTEM */ + if(ssl_config->primary.cert_blob) { + const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; + const struct curl_blob *key_blob = ssl_config->key_blob; + int file_type = wolfssl_do_file_type(ssl_config->cert_type); + int rc; - /* Load the client certificate, and private key */ - if(ssl_config->primary.clientcert && ssl_config->key) { - int file_type = do_file_type(ssl_config->cert_type); - - if(file_type == WOLFSSL_FILETYPE_PEM) { - if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx, - ssl_config->primary.clientcert) - != 1) { - failf(data, "unable to use client certificate"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else if(file_type == WOLFSSL_FILETYPE_ASN1) { - if(wolfSSL_CTX_use_certificate_file(backend->ctx, - ssl_config->primary.clientcert, - file_type) != 1) { - failf(data, "unable to use client certificate"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { + switch(file_type) { + case WOLFSSL_FILETYPE_PEM: + rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx, + cert_blob->data, + (long)cert_blob->len); + break; + case WOLFSSL_FILETYPE_ASN1: + rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data, + (long)cert_blob->len, file_type); + break; + default: failf(data, "unknown cert type"); return CURLE_BAD_FUNCTION_ARGUMENT; } + if(rc != 1) { + failf(data, "unable to use client certificate"); + return CURLE_SSL_CONNECT_ERROR; + } + + if(!key_blob) + key_blob = cert_blob; + else + file_type = wolfssl_do_file_type(ssl_config->key_type); - file_type = do_file_type(ssl_config->key_type); - if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, - file_type) != 1) { + if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data, + (long)key_blob->len, + file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } @@ -632,7 +964,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI - if(sni && connssl->peer.sni) { + if(connssl->peer.sni) { size_t sni_len = strlen(connssl->peer.sni); if((sni_len < USHRT_MAX)) { if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, @@ -647,8 +979,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { - CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx, - data->set.ssl.fsslctxp); + CURLcode result; + if(!backend->x509_store_setup) { + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + result = (*data->set.ssl.fsslctx)(data, backend->ctx, + data->set.ssl.fsslctxp); if(result) { failf(data, "error signaled by ssl ctx callback"); return result; @@ -656,7 +994,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #ifdef NO_FILESYSTEM else if(conn_config->verifypeer) { - failf(data, "SSL: Certificates can't be loaded because wolfSSL was built" + failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" " with \"no filesystem\". Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); @@ -669,14 +1007,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) wolfSSL_free(backend->handle); backend->handle = wolfSSL_new(backend->ctx); if(!backend->handle) { - failf(data, "SSL: couldn't create a handle"); + failf(data, "SSL: could not create a handle"); return CURLE_OUT_OF_MEMORY; } -#ifdef HAVE_LIBOQS - if(oqsAlg) { - if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) { - failf(data, "unable to use oqs KEM"); +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); } } #endif @@ -688,7 +1026,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) result = Curl_alpn_to_proto_str(&proto, connssl->alpn); if(result || - wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len, + wolfSSL_UseALPN(backend->handle, + (char *)proto.data, (unsigned int)proto.len, WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; @@ -715,8 +1054,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif /* HAVE_SECURE_RENEGOTIATION */ - /* Check if there's a cached ID we can/should use here! */ - if(ssl_config->primary.sessionid) { + /* Check if there is a cached ID we can/should use here! */ + if(ssl_config->primary.cache_session) { void *ssl_sessionid = NULL; Curl_ssl_sessionid_lock(data); @@ -725,7 +1064,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_delsessionid(data, ssl_sessionid); - infof(data, "Can't use session ID, going on without"); + infof(data, "cannot use session ID, going on without"); } else infof(data, "SSL reusing session ID"); @@ -738,7 +1077,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) int trying_ech_now = 0; if(data->set.str[STRING_ECH_PUBLIC]) { - infof(data, "ECH: outername not (yet) supported with WolfSSL"); + infof(data, "ECH: outername not (yet) supported with wolfSSL"); return CURLE_SSL_CONNECT_ERROR; } if(data->set.tls_ech == CURLECH_GREASE) { @@ -796,13 +1135,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } - Curl_resolv_unlock(data, dns); + Curl_resolv_unlink(data, &dns); } } if(trying_ech_now && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) { - infof(data, "ECH: Can't force TLSv1.3 [ERROR]"); + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); return CURLE_SSL_CONNECT_ERROR; } @@ -834,13 +1173,31 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } +static char *wolfssl_strerror(unsigned long error, char *buf, + unsigned long size) +{ + DEBUGASSERT(size > 40); + *buf = '\0'; + + wolfSSL_ERR_error_string_n(error, buf, size); + + if(!*buf) { + const char *msg = error ? "Unknown error" : "No error"; + /* the string fits because the assert above assures this */ + strcpy(buf, msg); + } + + return buf; +} + + static CURLcode wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { int ret = -1; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)? @@ -862,6 +1219,16 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } + if(!backend->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result; + result = Curl_wssl_setup_x509_store(cf, data, backend); + if(result) + return result; + } + + connssl->io_need = CURL_SSL_IO_NEED_NONE; ret = wolfSSL_connect(backend->handle); #ifdef OPENSSL_EXTRA @@ -889,15 +1256,14 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* OPENSSL_EXTRA */ if(ret != 1) { - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int detail = wolfSSL_get_error(backend->handle, ret); + int detail = wolfSSL_get_error(backend->handle, ret); if(SSL_ERROR_WANT_READ == detail) { - connssl->connecting_state = ssl_connect_2_reading; + connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } else if(SSL_ERROR_WANT_WRITE == detail) { - connssl->connecting_state = ssl_connect_2_writing; + connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } /* There is no easy way to override only the CN matching. @@ -929,7 +1295,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif } -#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ else if(ASN_NO_SIGNER_E == detail) { if(conn_config->verifypeer) { failf(data, " CA signer not available for verification"); @@ -942,7 +1307,14 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) "continuing anyway"); } } -#endif + else if(ASN_AFTER_DATE_E == detail) { + failf(data, "server verification failed: certificate has expired."); + return CURLE_PEER_FAILED_VERIFICATION; + } + else if(ASN_BEFORE_DATE_E == detail) { + failf(data, "server verification failed: certificate not valid yet."); + return CURLE_PEER_FAILED_VERIFICATION; + } #ifdef USE_ECH else if(-1 == detail) { /* try access a retry_config ECHConfigList for tracing */ @@ -950,7 +1322,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) word32 echConfigsLen = 1000; int rv = 0; - /* this currently doesn't produce the retry_configs */ + /* this currently does not produce the retry_configs */ rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs, &echConfigsLen); if(rv != WOLFSSL_SUCCESS) { @@ -972,8 +1344,10 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } else { + char error_buffer[256]; failf(data, "SSL_connect failed with error %d: %s", detail, - wolfSSL_ERR_error_string(detail, error_buffer)); + wolfssl_strerror((unsigned long)detail, error_buffer, + sizeof(error_buffer))); return CURLE_SSL_CONNECT_ERROR; } } @@ -1070,31 +1444,23 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.sessionid) { + if(ssl_config->primary.cache_session) { /* wolfSSL_get1_session allocates memory that has to be freed. */ WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); if(our_ssl_sessionid) { - void *old_ssl_sessionid = NULL; - bool incache; Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer, - &old_ssl_sessionid, NULL)); - if(incache) { - Curl_ssl_delsessionid(data, old_ssl_sessionid); - } - /* call takes ownership of `our_ssl_sessionid` */ - result = Curl_ssl_addsessionid(cf, data, &connssl->peer, - our_ssl_sessionid, 0, - wolfssl_session_free); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, + our_ssl_sessionid, 0, + wolfssl_session_free); Curl_ssl_sessionid_unlock(data); if(result) { failf(data, "failed to store ssl session"); @@ -1116,9 +1482,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; @@ -1133,7 +1498,7 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_write() */ + /* there is data pending, re-invoke SSL_write() */ CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; @@ -1144,9 +1509,13 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return -1; } CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err); - failf(data, "SSL write: %s, errno %d", - wolfSSL_ERR_error_string(err, error_buffer), - SOCKERRNO); + { + char error_buffer[256]; + failf(data, "SSL write: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } *curlcode = CURLE_SEND_ERROR; return -1; } @@ -1155,23 +1524,122 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, return rc; } +static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool send_shutdown, bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + char buf[1024]; + char error_buffer[256]; + int nread = -1, err; + size_t i; + int detail; + + DEBUGASSERT(wctx); + if(!wctx->handle || cf->shutdown) { + *done = TRUE; + goto out; + } + + wctx->shutting_down = TRUE; + connssl->io_need = CURL_SSL_IO_NEED_NONE; + *done = FALSE; + if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) { + /* We have not started the shutdown from our side yet. Check + * if the server already sent us one. */ + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + err = wolfSSL_get_error(wctx->handle, nread); + CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err); + if(!nread && err == SSL_ERROR_ZERO_RETURN) { + bool input_pending; + /* Yes, it did. */ + if(!send_shutdown) { + CURL_TRC_CF(data, cf, "SSL shutdown received, not sending"); + *done = TRUE; + goto out; + } + else if(!cf->next->cft->is_alive(cf->next, data, &input_pending)) { + /* Server closed the connection after its closy notify. It + * seems not interested to see our close notify, so do not + * send it. We are done. */ + CURL_TRC_CF(data, cf, "peer closed connection"); + connssl->peer_closed = TRUE; + *done = TRUE; + goto out; + } + } + } + + /* SSL should now have started the shutdown from our side. Since it + * was not complete, we are lacking the close notify from the server. */ + if(send_shutdown) { + ERR_clear_error(); + if(wolfSSL_shutdown(wctx->handle) == 1) { + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + *done = TRUE; + goto out; + } + if(SSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) { + CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + goto out; + } + /* Having sent the close notify, we use wolfSSL_read() to get the + * missing close notify from the server. */ + } + + for(i = 0; i < 10; ++i) { + ERR_clear_error(); + nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); + if(nread <= 0) + break; + } + err = wolfSSL_get_error(wctx->handle, nread); + switch(err) { + case SSL_ERROR_ZERO_RETURN: /* no more data */ + CURL_TRC_CF(data, cf, "SSL shutdown received"); + *done = TRUE; + break; + case SSL_ERROR_NONE: /* just did not get anything */ + case SSL_ERROR_WANT_READ: + /* SSL has send its notify and now wants to read the reply + * from the server. We are not really interested in that. */ + CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); + connssl->io_need = CURL_SSL_IO_NEED_RECV; + break; + case SSL_ERROR_WANT_WRITE: + CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); + connssl->io_need = CURL_SSL_IO_NEED_SEND; + break; + default: + detail = wolfSSL_get_error(wctx->handle, err); + CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + detail); + result = CURLE_RECV_ERROR; + break; + } + +out: + cf->shutdown = (result || *done); + return result; +} + static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; (void) data; DEBUGASSERT(backend); if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf)); - if(!connssl->peer_closed) - (void)wolfSSL_shutdown(backend->handle); wolfSSL_free(backend->handle); backend->handle = NULL; } @@ -1187,9 +1655,8 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, CURLcode *curlcode) { struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; int nread; @@ -1211,7 +1678,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke wolfSSL_read() */ + /* there is data pending, re-invoke wolfSSL_read() */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; @@ -1221,8 +1688,13 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, *curlcode = CURLE_AGAIN; return -1; } - failf(data, "SSL read: %s, errno %d", - wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); + { + char error_buffer[256]; + failf(data, "SSL read: %s, errno %d", + wolfssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), + SOCKERRNO); + } *curlcode = CURLE_RECV_ERROR; return -1; } @@ -1269,43 +1741,18 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; + struct wolfssl_ctx *backend; (void)data; DEBUGASSERT(ctx && ctx->backend); - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; + backend = (struct wolfssl_ctx *)ctx->backend; if(backend->handle) /* SSL is in use */ return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; else return FALSE; } - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int wolfssl_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *ctx = cf->ctx; - struct wolfssl_ssl_backend_data *backend; - int retval = 0; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - - backend = (struct wolfssl_ssl_backend_data *)ctx->backend; - if(backend->handle) { - wolfSSL_ERR_clear_error(); - wolfSSL_free(backend->handle); - backend->handle = NULL; - } - return retval; -} - - static CURLcode wolfssl_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1324,7 +1771,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ + /* Find out how much more time we are allowed */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { @@ -1338,9 +1785,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, return result; } - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { + while(ssl_connect_2 == connssl->connecting_state) { /* check allowed time left */ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); @@ -1351,14 +1796,13 @@ wolfssl_connect_common(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { + /* if ssl is expecting something, check if it is available. */ + if(connssl->io_need) { - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)? + sockfd:CURL_SOCKET_BAD; + curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)? + sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0:timeout_ms); @@ -1389,10 +1833,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf, * have a valid fdset to wait on. */ result = wolfssl_connect_step2(cf, data); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) + if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state))) return result; } /* repeat step2 until all transactions are done. */ @@ -1472,15 +1913,15 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { - struct wolfssl_ssl_backend_data *backend = - (struct wolfssl_ssl_backend_data *)connssl->backend; + struct wolfssl_ctx *backend = + (struct wolfssl_ctx *)connssl->backend; (void)info; DEBUGASSERT(backend); return backend->handle; } const struct Curl_ssl Curl_ssl_wolfssl = { - { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */ + { CURLSSLBACKEND_WOLFSSL, "wolfssl" }, /* info */ #ifdef KEEP_PEER_CERT SSLSUPP_PINNEDPUBKEY | @@ -1493,9 +1934,14 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #ifdef USE_ECH SSLSUPP_ECH | #endif - SSLSUPP_SSL_CTX, + SSLSUPP_SSL_CTX | +#ifdef WOLFSSL_TLS13 + SSLSUPP_TLS13_CIPHERSUITES | +#endif + SSLSUPP_CA_CACHE | + SSLSUPP_CIPHER_LIST, - sizeof(struct wolfssl_ssl_backend_data), + sizeof(struct wolfssl_ctx), wolfssl_init, /* init */ wolfssl_cleanup, /* cleanup */ @@ -1518,9 +1964,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = { wolfssl_sha256sum, /* sha256sum */ NULL, /* associate_connection */ NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ wolfssl_recv, /* recv decrypted data */ wolfssl_send, /* send data to encrypt */ + NULL, /* get_channel_binding */ }; #endif diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h index a5ed84809..318d8b4ab 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.h +++ b/Utilities/cmcurl/lib/vtls/wolfssl.h @@ -26,8 +26,27 @@ #include "curl_setup.h" #ifdef USE_WOLFSSL +#include +#include +#include +#include + +#include "urldata.h" extern const struct Curl_ssl Curl_ssl_wolfssl; +struct wolfssl_ctx { + WOLFSSL_CTX *ctx; + WOLFSSL *handle; + CURLcode io_result; /* result of last BIO cfilter operation */ + int io_send_blocked_len; /* length of last BIO write that EAGAINed */ + BIT(x509_store_setup); /* x509 store has been set up */ + BIT(shutting_down); /* TLS is being shut down */ +}; + +CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wssl); + #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index 4564ea958..4c93ce78d 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -25,13 +25,15 @@ #include "curl_setup.h" #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) #if defined(USE_WOLFSSL) || defined(USE_SCHANNEL) #define WANT_PARSEX509 /* uses Curl_parseX509() */ #endif -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #define WANT_PARSEX509 /* ... uses Curl_parseX509() */ #endif @@ -97,10 +99,6 @@ #define CURL_ASN1_CHARACTER_STRING 29 #define CURL_ASN1_BMP_STRING 30 -/* Max sixes */ - -#define MAX_X509_STR 10000 -#define MAX_X509_CERT 100000 #ifdef WANT_EXTRACT_CERTINFO /* ASN.1 OID table entry. */ @@ -110,15 +108,16 @@ struct Curl_OID { }; /* ASN.1 OIDs. */ -static const char cnOID[] = "2.5.4.3"; /* Common name. */ -static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ - static const struct Curl_OID OIDtable[] = { { "1.2.840.10040.4.1", "dsa" }, { "1.2.840.10040.4.3", "dsa-with-sha1" }, { "1.2.840.10045.2.1", "ecPublicKey" }, { "1.2.840.10045.3.0.1", "c2pnb163v1" }, { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, + { "1.2.840.10045.4.3.1", "ecdsa-with-SHA224" }, + { "1.2.840.10045.4.3.2", "ecdsa-with-SHA256" }, + { "1.2.840.10045.4.3.3", "ecdsa-with-SHA384" }, + { "1.2.840.10045.4.3.4", "ecdsa-with-SHA512" }, { "1.2.840.10046.2.1", "dhpublicnumber" }, { "1.2.840.113549.1.1.1", "rsaEncryption" }, { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, @@ -132,7 +131,7 @@ static const struct Curl_OID OIDtable[] = { { "1.2.840.113549.2.2", "md2" }, { "1.2.840.113549.2.5", "md5" }, { "1.3.14.3.2.26", "sha1" }, - { cnOID, "CN" }, + { "2.5.4.3", "CN" }, { "2.5.4.4", "SN" }, { "2.5.4.5", "serialNumber" }, { "2.5.4.6", "C" }, @@ -153,7 +152,7 @@ static const struct Curl_OID OIDtable[] = { { "2.5.4.65", "pseudonym" }, { "1.2.840.113549.1.9.1", "emailAddress" }, { "2.5.4.72", "role" }, - { sanOID, "subjectAltName" }, + { "2.5.29.17", "subjectAltName" }, { "2.5.29.18", "issuerAltName" }, { "2.5.29.19", "basicConstraints" }, { "2.16.840.1.101.3.4.2.4", "sha224" }, @@ -372,7 +371,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) else { while(!result && (from < end)) { char buf[4]; /* decode buffer */ - int charsize = 1; + size_t charsize = 1; unsigned int wc = 0; switch(size) { @@ -390,7 +389,6 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) if(wc >= 0x00000800) { if(wc >= 0x00010000) { if(wc >= 0x00200000) { - free(buf); /* Invalid char. size for target encoding. */ return CURLE_WEIRD_SERVER_REPLY; } @@ -461,7 +459,7 @@ static CURLcode OID2str(struct dynbuf *store, if(beg < end) { if(symbolic) { struct dynbuf buf; - Curl_dyn_init(&buf, MAX_X509_STR); + Curl_dyn_init(&buf, CURL_X509_STR_MAX); result = encodeOID(&buf, beg, end); if(!result) { @@ -469,7 +467,7 @@ static CURLcode OID2str(struct dynbuf *store, if(op) result = Curl_dyn_add(store, op->textoid); else - result = CURLE_BAD_FUNCTION_ARGUMENT; + result = Curl_dyn_add(store, Curl_dyn_ptr(&buf)); Curl_dyn_free(&buf); } } @@ -492,7 +490,7 @@ static CURLcode GTime2str(struct dynbuf *store, /* Convert an ASN.1 Generalized time to a printable string. Return the dynamically allocated string, or NULL if an error occurs. */ - for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) + for(fracp = beg; fracp < end && ISDIGIT(*fracp); fracp++) ; /* Get seconds digits. */ @@ -511,32 +509,44 @@ static CURLcode GTime2str(struct dynbuf *store, return CURLE_BAD_FUNCTION_ARGUMENT; } - /* Scan for timezone, measure fractional seconds. */ + /* timezone follows optional fractional seconds. */ tzp = fracp; - fracl = 0; + fracl = 0; /* no fractional seconds detected so far */ if(fracp < end && (*fracp == '.' || *fracp == ',')) { - fracp++; - do + /* Have fractional seconds, e.g. "[.,]\d+". How many? */ + fracp++; /* should be a digit char or BAD ARGUMENT */ + tzp = fracp; + while(tzp < end && ISDIGIT(*tzp)) tzp++; - while(tzp < end && *tzp >= '0' && *tzp <= '9'); - /* Strip leading zeroes in fractional seconds. */ - for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) - ; + if(tzp == fracp) /* never looped, no digit after [.,] */ + return CURLE_BAD_FUNCTION_ARGUMENT; + fracl = tzp - fracp; /* number of fractional sec digits */ + DEBUGASSERT(fracl > 0); + /* Strip trailing zeroes in fractional seconds. + * May reduce fracl to 0 if only '0's are present. */ + while(fracl && fracp[fracl - 1] == '0') + fracl--; } /* Process timezone. */ - if(tzp >= end) - ; /* Nothing to do. */ + if(tzp >= end) { + tzp = ""; + tzl = 0; + } else if(*tzp == 'Z') { - tzp = " GMT"; - end = tzp + 4; + sep = " "; + tzp = "GMT"; + tzl = 3; + } + else if((*tzp == '+') || (*tzp == '-')) { + sep = " UTC"; + tzl = end - tzp; } else { sep = " "; - tzp++; + tzl = end - tzp; } - tzl = end - tzp; return Curl_dyn_addf(store, "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", beg, beg + 4, beg + 6, @@ -545,6 +555,15 @@ static CURLcode GTime2str(struct dynbuf *store, sep, (int)tzl, tzp); } +#ifdef UNITTESTS +/* used by unit1656.c */ +CURLcode Curl_x509_GTime2str(struct dynbuf *store, + const char *beg, const char *end) +{ + return GTime2str(store, beg, end); +} +#endif + /* * Convert an ASN.1 UTC time to a printable string. * @@ -598,7 +617,7 @@ static CURLcode ASN1tostr(struct dynbuf *store, { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; if(elem->constructed) - return CURLE_OK; /* No conversion of structured elements. */ + return result; /* No conversion of structured elements. */ if(!type) type = elem->tag; /* Type not forced: use element tag as type. */ @@ -662,7 +681,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) CURLcode result = CURLE_OK; bool added = FALSE; struct dynbuf temp; - Curl_dyn_init(&temp, MAX_X509_STR); + Curl_dyn_init(&temp, CURL_X509_STR_MAX); for(p1 = dn->beg; p1 < dn->end;) { p1 = getASN1Element(&rdn, p1, dn->end); @@ -692,6 +711,11 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) str = Curl_dyn_ptr(&temp); + if(!str) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto error; + } + /* Encode delimiter. If attribute has a short uppercase name, delimiter is ", ". */ for(p3 = str; ISUPPER(*p3); p3++) @@ -921,7 +945,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, CURLcode result; struct dynbuf out; - Curl_dyn_init(&out, MAX_X509_STR); + Curl_dyn_init(&out, CURL_X509_STR_MAX); /* Generate a certificate information record for the public key. */ @@ -959,7 +983,8 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; } - return do_pubkey_field(data, certnum, "ecPublicKey", pubkey); + return do_pubkey_field(data, certnum, "ecPublicKey", pubkey) == CURLE_OK + ? 0 : 1; } /* Get the public key (single element). */ @@ -1064,7 +1089,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(certnum) return CURLE_OK; - Curl_dyn_init(&out, MAX_X509_STR); + Curl_dyn_init(&out, CURL_X509_STR_MAX); /* Prepare the certificate information for curl_easy_getinfo(). */ /* Extract the certificate ASN.1 elements. */ @@ -1223,6 +1248,8 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out); done: + if(result) + failf(data, "Failed extracting certificate chain"); Curl_dyn_free(&out); return result; } diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h index 23a67b828..5b48596c7 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.h +++ b/Utilities/cmcurl/lib/vtls/x509asn1.h @@ -28,7 +28,8 @@ #include "curl_setup.h" #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) #include "cfilters.h" #include "urldata.h" @@ -76,5 +77,16 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, const char *beg, const char *end); CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); + +#ifdef UNITTESTS +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_MBEDTLS) + +/* used by unit1656.c */ +CURLcode Curl_x509_GTime2str(struct dynbuf *store, + const char *beg, const char *end); +#endif +#endif + #endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ #endif /* HEADER_CURL_X509ASN1_H */ diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 6ccf9e65f..670694470 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c @@ -37,6 +37,7 @@ #include "ws.h" #include "easyif.h" #include "transfer.h" +#include "select.h" #include "nonblock.h" /* The last 3 #include files should be in this order */ @@ -102,7 +103,7 @@ static unsigned char ws_frame_flags2op(int flags) size_t i; for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) { if(WS_FRAMES[i].flags & flags) - return WS_FRAMES[i].proto_opcode; + return (unsigned char)WS_FRAMES[i].proto_opcode; } return 0; } @@ -127,7 +128,7 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, } else { CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%" - CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "]", + FMT_OFF_T "/%" FMT_OFF_T "]", msg, ws_frame_name_of_op(dec->head[0]), (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL", dec->payload_offset, dec->payload_len); @@ -136,6 +137,9 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data, } } +static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws, + const char *buffer, size_t buflen); + typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen, int frame_age, int frame_flags, curl_off_t payload_offset, @@ -171,7 +175,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, dec->head[0] = *inbuf; Curl_bufq_skip(inraw, 1); - dec->frame_flags = ws_frame_op2flags(dec->head[0]); + dec->frame_flags = ws_frame_op2flags(dec->head[0]); if(!dec->frame_flags) { failf(data, "WS: unknown opcode: %x", dec->head[0]); ws_dec_reset(dec); @@ -278,7 +282,7 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, dec->payload_offset += (curl_off_t)nwritten; remain = dec->payload_len - dec->payload_offset; CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %" - CURL_FORMAT_CURL_OFF_T " remain", nwritten, remain); + FMT_OFF_T " remain", nwritten, remain); } return remain? CURLE_AGAIN : CURLE_OK; @@ -488,8 +492,7 @@ static const struct Curl_cwtype ws_cw_decode = { static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data, const char *msg) { - infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "]", + infof(data, "WS-ENC: %s [%s%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]", msg, ws_frame_name_of_op(enc->firstbyte), (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ? " CONT" : "", @@ -547,20 +550,20 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, if(payload_len < 0) { failf(data, "WS: starting new frame with negative payload length %" - CURL_FORMAT_CURL_OFF_T, payload_len); + FMT_OFF_T, payload_len); *err = CURLE_SEND_ERROR; return -1; } if(enc->payload_remain > 0) { /* trying to write a new frame before the previous one is finished */ - failf(data, "WS: starting new frame with %zd bytes from last one" + failf(data, "WS: starting new frame with %zd bytes from last one " "remaining to be sent", (ssize_t)enc->payload_remain); *err = CURLE_SEND_ERROR; return -1; } - opcode = ws_frame_flags2op(flags); + opcode = ws_frame_flags2op((int)flags & ~CURLWS_CONT); if(!opcode) { failf(data, "WS: provided flags not recognized '%x'", flags); *err = CURLE_SEND_ERROR; @@ -579,7 +582,7 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, enc->contfragment = FALSE; } else if(enc->contfragment) { - /* the previous fragment was not a final one and this isn't either, keep a + /* the previous fragment was not a final one and this is not either, keep a CONT opcode and no FIN bit */ firstbyte |= WSBIT_OPCODE_CONT; } @@ -773,7 +776,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, } } #endif - DEBUGF(infof(data, "WS, using chunk size %zu", chunk_size)); + CURL_TRC_WS(data, "WS, using chunk size %zu", chunk_size); Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT, @@ -970,8 +973,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, infof(data, "connection expectedly closed?"); return CURLE_GOT_NOTHING; } - DEBUGF(infof(data, "curl_ws_recv, added %zu bytes from network", - Curl_bufq_len(&ws->recvbuf))); + CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", + Curl_bufq_len(&ws->recvbuf)); } result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, @@ -1001,14 +1004,14 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, ctx.payload_len, ctx.bufidx); *metap = &ws->frame; *nread = ws->frame.len; - /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)", - buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */ + CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" + FMT_OFF_T ", %" FMT_OFF_T " left)", + buflen, *nread, ws->frame.offset, ws->frame.bytesleft); return CURLE_OK; } static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, - bool complete) + bool blocking) { if(!Curl_bufq_is_empty(&ws->sendbuf)) { CURLcode result; @@ -1016,30 +1019,26 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, size_t outlen, n; while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) { - if(data->set.connect_only) + if(blocking) { + result = ws_send_raw_blocking(data, ws, (char *)out, outlen); + n = result? 0 : outlen; + } + else if(data->set.connect_only || Curl_is_in_callback(data)) result = Curl_senddata(data, out, outlen, &n); else { - result = Curl_xfer_send(data, out, outlen, &n); + result = Curl_xfer_send(data, out, outlen, FALSE, &n); if(!result && !n && outlen) result = CURLE_AGAIN; } - if(result) { - if(result == CURLE_AGAIN) { - if(!complete) { - infof(data, "WS: flush EAGAIN, %zu bytes remain in buffer", - Curl_bufq_len(&ws->sendbuf)); - return result; - } - /* TODO: the current design does not allow for buffered writes. - * We need to flush the buffer now. There is no ws_flush() later */ - n = 0; - continue; - } - else if(result) { - failf(data, "WS: flush, write error %d", result); - return result; - } + if(result == CURLE_AGAIN) { + CURL_TRC_WS(data, "flush EAGAIN, %zu bytes remain in buffer", + Curl_bufq_len(&ws->sendbuf)); + return result; + } + else if(result) { + failf(data, "WS: flush, write error %d", result); + return result; } else { infof(data, "WS: flushed %zu bytes", n); @@ -1050,6 +1049,83 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return CURLE_OK; } +static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws, + const char *buffer, size_t buflen) +{ + CURLcode result = CURLE_OK; + size_t nwritten; + + (void)ws; + while(buflen) { + result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten); + if(result) + return result; + DEBUGASSERT(nwritten <= buflen); + buffer += nwritten; + buflen -= nwritten; + if(buflen) { + curl_socket_t sock = data->conn->sock[FIRSTSOCKET]; + timediff_t left_ms; + int ev; + + CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send", + buflen); + left_ms = Curl_timeleft(data, NULL, FALSE); + if(left_ms < 0) { + failf(data, "Timeout waiting for socket becoming writable"); + return CURLE_SEND_ERROR; + } + + /* POLLOUT socket */ + if(sock == CURL_SOCKET_BAD) + return CURLE_SEND_ERROR; + ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock, + left_ms? left_ms : 500); + if(ev < 0) { + failf(data, "Error while waiting for socket becoming writable"); + return CURLE_SEND_ERROR; + } + } + } + return result; +} + +static CURLcode ws_send_raw(CURL *data, const void *buffer, + size_t buflen, size_t *pnwritten) +{ + struct websocket *ws = data->conn->proto.ws; + CURLcode result; + + if(!ws) { + failf(data, "Not a websocket transfer"); + return CURLE_SEND_ERROR; + } + if(!buflen) + return CURLE_OK; + + if(Curl_is_in_callback(data)) { + /* When invoked from inside callbacks, we do a blocking send as the + * callback will probably not implement partial writes that may then + * mess up the ws framing subsequently. + * We need any pending data to be flushed before sending. */ + result = ws_flush(data, ws, TRUE); + if(result) + return result; + result = ws_send_raw_blocking(data, ws, buffer, buflen); + } + else { + /* We need any pending data to be sent or EAGAIN this call. */ + result = ws_flush(data, ws, FALSE); + if(result) + return result; + result = Curl_senddata(data, buffer, buflen, pnwritten); + } + + CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu", + buflen, result, *pnwritten); + return result; +} + CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, size_t buflen, size_t *sent, curl_off_t fragsize, @@ -1057,60 +1133,53 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, { struct websocket *ws; ssize_t n; - size_t nwritten, space; + size_t space, payload_added; CURLcode result; + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x), raw=%d", + buflen, fragsize, flags, data->set.ws_raw_mode); *sent = 0; if(!data->conn && data->set.connect_only) { result = Curl_connect_only_attach(data); if(result) - return result; + goto out; } if(!data->conn) { failf(data, "No associated connection"); - return CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; + goto out; } if(!data->conn->proto.ws) { failf(data, "Not a websocket transfer"); - return CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; + goto out; } ws = data->conn->proto.ws; + /* try flushing any content still waiting to be sent. */ + result = ws_flush(data, ws, FALSE); + if(result) + goto out; + if(data->set.ws_raw_mode) { + /* In raw mode, we write directly to the connection */ if(fragsize || flags) { - DEBUGF(infof(data, "ws_send: " - "fragsize and flags cannot be non-zero in raw mode")); + failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero"); return CURLE_BAD_FUNCTION_ARGUMENT; } - 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_xfer_send(data, buffer, buflen, &nwritten); - } - else - result = Curl_senddata(data, buffer, buflen, &nwritten); - - infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", - buflen, nwritten); - *sent = nwritten; - return result; + result = ws_send_raw(data, buffer, buflen, sent); + goto out; } /* Not RAW mode, buf we do the frame encoding */ - result = ws_flush(data, ws, FALSE); - if(result) - return result; - - /* TODO: the current design does not allow partial writes, afaict. - * It is not clear how the application is supposed to react. */ space = Curl_bufq_space(&ws->sendbuf); - DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu", - buflen, Curl_bufq_len(&ws->sendbuf), space)); - if(space < 14) - return CURLE_AGAIN; + CURL_TRC_WS(data, "curl_ws_send(len=%zu), sendbuf=%zu space_left=%zu", + buflen, Curl_bufq_len(&ws->sendbuf), space); + if(space < 14) { + result = CURLE_AGAIN; + goto out; + } if(flags & CURLWS_OFFSET) { if(fragsize) { @@ -1118,12 +1187,12 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, n = ws_enc_write_head(data, &ws->enc, flags, fragsize, &ws->sendbuf, &result); if(n < 0) - return result; + goto out; } else { if((curl_off_t)buflen > ws->enc.payload_remain) { infof(data, "WS: unaligned frame size (sending %zu instead of %" - CURL_FORMAT_CURL_OFF_T ")", + FMT_OFF_T ")", buflen, ws->enc.payload_remain); } } @@ -1132,16 +1201,66 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen, &ws->sendbuf, &result); if(n < 0) - return result; + goto out; } n = ws_enc_write_payload(&ws->enc, data, buffer, buflen, &ws->sendbuf, &result); if(n < 0) - return result; + goto out; + payload_added = (size_t)n; + + while(!result && (buflen || !Curl_bufq_is_empty(&ws->sendbuf))) { + /* flush, blocking when in callback */ + result = ws_flush(data, ws, Curl_is_in_callback(data)); + if(!result) { + DEBUGASSERT(payload_added <= buflen); + /* all buffered data sent. Try sending the rest if there is any. */ + *sent += payload_added; + buffer = (const char *)buffer + payload_added; + buflen -= payload_added; + payload_added = 0; + if(buflen) { + n = ws_enc_write_payload(&ws->enc, data, + buffer, buflen, &ws->sendbuf, &result); + if(n < 0) + goto out; + payload_added = Curl_bufq_len(&ws->sendbuf); + } + } + else if(result == CURLE_AGAIN) { + /* partially sent. how much of the call data has been part of it? what + * should we report to out caller so it can retry/send the rest? */ + if(payload_added < buflen) { + /* We did not add everything the caller wanted. Return just + * the partial write to our buffer. */ + *sent = payload_added; + result = CURLE_OK; + goto out; + } + else if(!buflen) { + /* We have no payload to report a partial write. EAGAIN would make + * the caller repeat this and add the frame again. + * Flush blocking seems the only way out of this. */ + *sent = (size_t)n; + result = ws_flush(data, ws, TRUE); + goto out; + } + /* We added the complete data to our sendbuf. Report one byte less as + * sent. This parital success should make the caller invoke us again + * with the last byte. */ + *sent = payload_added - 1; + result = Curl_bufq_unwrite(&ws->sendbuf, 1); + if(!result) + result = CURLE_AGAIN; + } + } - *sent = (size_t)n; - return ws_flush(data, ws, TRUE); +out: + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x, raw=%d) -> %d, %zu", + buflen, fragsize, flags, data->set.ws_raw_mode, result, *sent); + return result; } static void ws_free(struct connectdata *conn) @@ -1156,7 +1275,7 @@ static void ws_free(struct connectdata *conn) static CURLcode ws_setup_conn(struct Curl_easy *data, struct connectdata *conn) { - /* websockets is 1.1 only (for now) */ + /* WebSockets is 1.1 only (for now) */ data->state.httpwant = CURL_HTTP_VERSION_1_1; return Curl_http_setup_conn(data, conn); } diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h index baa77b442..398900cc3 100644 --- a/Utilities/cmcurl/lib/ws.h +++ b/Utilities/cmcurl/lib/ws.h @@ -57,7 +57,7 @@ struct ws_encoder { curl_off_t payload_len; /* payload length of current frame */ curl_off_t payload_remain; /* remaining payload of current */ unsigned int xori; /* xor index */ - unsigned char mask[4]; /* 32 bit mask for this connection */ + unsigned char mask[4]; /* 32-bit mask for this connection */ unsigned char firstbyte; /* first byte of frame we encode */ bool contfragment; /* set TRUE if the previous fragment sent was not final */ }; diff --git a/Utilities/cmexpat/COPYING b/Utilities/cmexpat/COPYING index 3c0142e71..ce9e59392 100644 --- a/Utilities/cmexpat/COPYING +++ b/Utilities/cmexpat/COPYING @@ -1,5 +1,5 @@ Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper -Copyright (c) 2001-2019 Expat maintainers +Copyright (c) 2001-2022 Expat maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Utilities/cmexpat/README.md b/Utilities/cmexpat/README.md index 959c4a6e9..3c20adbee 100644 --- a/Utilities/cmexpat/README.md +++ b/Utilities/cmexpat/README.md @@ -1,13 +1,14 @@ -[![Run Linux Travis CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml) +[![Run Linux CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat) [![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions) [![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/) [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases) -# Expat, Release 2.4.6 +# Expat, Release 2.6.2 -This is Expat, a C library for parsing XML, started by +This is Expat, a C99 library for parsing +[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by [James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997. Expat is a stream-oriented XML parser. This means that you register handlers with the parser before starting the parse. These handlers @@ -222,37 +223,37 @@ CMAKE_INSTALL_PREFIX:PATH=/usr/local // Path to a program. DOCBOOK_TO_MAN:FILEPATH=/usr/bin/docbook2x-man -// build man page for xmlwf +// Build man page for xmlwf EXPAT_BUILD_DOCS:BOOL=ON -// build the examples for expat library +// Build the examples for expat library EXPAT_BUILD_EXAMPLES:BOOL=ON -// build fuzzers for the expat library +// Build fuzzers for the expat library EXPAT_BUILD_FUZZERS:BOOL=OFF -// build pkg-config file +// Build pkg-config file EXPAT_BUILD_PKGCONFIG:BOOL=ON -// build the tests for expat library +// Build the tests for expat library EXPAT_BUILD_TESTS:BOOL=ON -// build the xmlwf tool for expat library +// Build the xmlwf tool for expat library EXPAT_BUILD_TOOLS:BOOL=ON // Character type to use (char|ushort|wchar_t) [default=char] EXPAT_CHAR_TYPE:STRING=char -// install expat files in cmake install target +// Install expat files in cmake install target EXPAT_ENABLE_INSTALL:BOOL=ON // Use /MT flag (static CRT) when compiling in MSVC EXPAT_MSVC_STATIC_CRT:BOOL=OFF -// build fuzzers via ossfuzz for the expat library +// Build fuzzers via ossfuzz for the expat library EXPAT_OSSFUZZ_BUILD:BOOL=OFF -// build a shared expat library +// Build a shared expat library EXPAT_SHARED_LIBS:BOOL=ON // Treat all compiler warnings as errors @@ -261,7 +262,7 @@ EXPAT_WARNINGS_AS_ERRORS:BOOL=OFF // Make use of getrandom function (ON|OFF|AUTO) [default=AUTO] EXPAT_WITH_GETRANDOM:STRING=AUTO -// utilize libbsd (for arc4random_buf) +// Utilize libbsd (for arc4random_buf) EXPAT_WITH_LIBBSD:BOOL=OFF // Make use of syscall SYS_getrandom (ON|OFF|AUTO) [default=AUTO] diff --git a/Utilities/cmexpat/expat_config.h.cmake b/Utilities/cmexpat/expat_config.h.cmake index e91861ecf..c82053338 100644 --- a/Utilities/cmexpat/expat_config.h.cmake +++ b/Utilities/cmexpat/expat_config.h.cmake @@ -1,5 +1,8 @@ /* expat_config.h.cmake. Based upon generated expat_config.h.in. */ +#ifndef EXPAT_CONFIG_H +#define EXPAT_CONFIG_H 1 + /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ #cmakedefine BYTEORDER @BYTEORDER@ @@ -58,7 +61,9 @@ #cmakedefine HAVE_UNISTD_H /* Define to 1 if you have the ANSI C header files. */ +#ifndef STDC_HEADERS #cmakedefine STDC_HEADERS +#endif /* whether byteorder is bigendian */ #cmakedefine WORDS_BIGENDIAN @@ -68,17 +73,20 @@ #cmakedefine XML_ATTR_INFO /* Define to specify how much context to retain around the current parse - point. */ + point, 0 to disable. */ #define XML_CONTEXT_BYTES 1024 #if ! defined(_WIN32) /* Define to include code reading entropy from `/dev/urandom'. */ - #cmakedefine XML_DEV_URANDOM +#cmakedefine XML_DEV_URANDOM #endif /* Define to make parameter entity parsing functionality available. */ /* #undef XML_DTD */ +/* Define as 1/0 to enable/disable support for general entities. */ +#define XML_GE 0 + /* Define to make XML Namespaces functionality available. */ /* #undef XML_NS */ @@ -86,3 +94,5 @@ #ifdef _MSC_VER # define __func__ __FUNCTION__ #endif + +#endif // ndef EXPAT_CONFIG_H diff --git a/Utilities/cmexpat/lib/expat.h b/Utilities/cmexpat/lib/expat.h index 46a0e1bcd..c2770be38 100644 --- a/Utilities/cmexpat/lib/expat.h +++ b/Utilities/cmexpat/lib/expat.h @@ -11,10 +11,14 @@ Copyright (c) 2000-2005 Fred L. Drake, Jr. Copyright (c) 2001-2002 Greg Stein Copyright (c) 2002-2016 Karl Waclawek - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2016 Cristian Rodríguez Copyright (c) 2016 Thomas Beutlich Copyright (c) 2017 Rhodri James + Copyright (c) 2022 Thijs Schreijer + Copyright (c) 2023 Hanno Böck + Copyright (c) 2023 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -174,8 +178,10 @@ struct XML_cp { }; /* This is called for an element declaration. See above for - description of the model argument. It's the caller's responsibility - to free model when finished with it. + description of the model argument. It's the user code's responsibility + to free model when finished with it. See XML_FreeContentModel. + There is no need to free the model from the handler, it can be kept + around and freed at a later stage. */ typedef void(XMLCALL *XML_ElementDeclHandler)(void *userData, const XML_Char *name, @@ -237,6 +243,17 @@ XML_ParserCreate(const XML_Char *encoding); and the local part will be concatenated without any separator. It is a programming error to use the separator '\0' with namespace triplets (see XML_SetReturnNSTriplet). + If a namespace separator is chosen that can be part of a URI or + part of an XML name, splitting an expanded name back into its + 1, 2 or 3 original parts on application level in the element handler + may end up vulnerable, so these are advised against; sane choices for + a namespace separator are e.g. '\n' (line feed) and '|' (pipe). + + Note that Expat does not validate namespace URIs (beyond encoding) + against RFC 3986 today (and is not required to do so with regard to + the XML 1.0 namespaces specification) but it may start doing that + in future releases. Before that, an application using Expat must + be ready to receive namespace URIs containing non-URI characters. */ XMLPARSEAPI(XML_Parser) XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); @@ -255,7 +272,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *memsuite, const XML_Char *namespaceSeparator); -/* Prepare a parser object to be re-used. This is particularly +/* Prepare a parser object to be reused. This is particularly valuable when memory allocation overhead is disproportionately high, such as when a large number of small documnents need to be parsed. All handlers are cleared from the parser, except for the @@ -317,7 +334,7 @@ typedef void(XMLCALL *XML_StartDoctypeDeclHandler)(void *userData, const XML_Char *pubid, int has_internal_subset); -/* This is called for the start of the DOCTYPE declaration when the +/* This is called for the end of the DOCTYPE declaration when the closing > is encountered, but after processing any external subset. */ @@ -937,7 +954,7 @@ XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); XMLPARSEAPI(int) XML_GetCurrentByteCount(XML_Parser parser); -/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets +/* If XML_CONTEXT_BYTES is >=1, returns the input buffer, sets the integer pointed to by offset to the offset within this buffer of the current parse position, and sets the integer pointed to by size to the size of this buffer (the number of input bytes). Otherwise @@ -1011,7 +1028,9 @@ enum XML_FeatureEnum { XML_FEATURE_ATTR_INFO, /* Added in Expat 2.4.0. */ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, - XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT + XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, + /* Added in Expat 2.6.0. */ + XML_FEATURE_GE /* Additional features must be added to the end of this enum. */ }; @@ -1024,24 +1043,30 @@ typedef struct { XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); -#ifdef XML_DTD -/* Added in Expat 2.4.0. */ +#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) +/* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ XMLPARSEAPI(XML_Bool) XML_SetBillionLaughsAttackProtectionMaximumAmplification( XML_Parser parser, float maximumAmplificationFactor); -/* Added in Expat 2.4.0. */ +/* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ XMLPARSEAPI(XML_Bool) XML_SetBillionLaughsAttackProtectionActivationThreshold( XML_Parser parser, unsigned long long activationThresholdBytes); #endif +/* Added in Expat 2.6.0. */ +XMLPARSEAPI(XML_Bool) +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); + /* Expat follows the semantic versioning convention. - See http://semver.org. + See https://semver.org */ #define XML_MAJOR_VERSION 2 -#define XML_MINOR_VERSION 4 -#define XML_MICRO_VERSION 6 +#define XML_MINOR_VERSION 6 +#define XML_MICRO_VERSION 2 #ifdef __cplusplus } diff --git a/Utilities/cmexpat/lib/internal.h b/Utilities/cmexpat/lib/internal.h index 444eba0fb..167ec3680 100644 --- a/Utilities/cmexpat/lib/internal.h +++ b/Utilities/cmexpat/lib/internal.h @@ -28,9 +28,11 @@ Copyright (c) 2002-2003 Fred L. Drake, Jr. Copyright (c) 2002-2006 Karl Waclawek Copyright (c) 2003 Greg Stein - Copyright (c) 2016-2021 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2018 Yury Gribov Copyright (c) 2019 David Loffredo + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow + Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -107,7 +109,9 @@ #include // ULONG_MAX -#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO) +#if defined(_WIN32) \ + && (! defined(__USE_MINGW_ANSI_STDIO) \ + || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0)) # define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" # if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW # define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" @@ -152,12 +156,21 @@ extern "C" { void _INTERNAL_trim_to_complete_utf8_characters(const char *from, const char **fromLimRef); -#if defined(XML_DTD) +#if defined(XML_GE) && XML_GE == 1 unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser); unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser); const char *unsignedCharToPrintable(unsigned char c); #endif +extern +#if ! defined(XML_TESTING) + const +#endif + XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c +#if defined(XML_TESTING) +extern unsigned int g_bytesScanned; // used for testing only +#endif + #ifdef __cplusplus } #endif diff --git a/Utilities/cmexpat/lib/siphash.h b/Utilities/cmexpat/lib/siphash.h index 952f1c88b..c4f6fe912 100644 --- a/Utilities/cmexpat/lib/siphash.h +++ b/Utilities/cmexpat/lib/siphash.h @@ -117,7 +117,7 @@ * if this code is included and compiled as C++; related GCC warning is: * warning: use of C++11 long long integer constant [-Wlong-long] */ -#define _SIP_ULL(high, low) (((uint64_t)high << 32) | low) +#define SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low)) #define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) @@ -201,10 +201,10 @@ sip_round(struct siphash *H, const int rounds) { static struct siphash * sip24_init(struct siphash *H, const struct sipkey *key) { - H->v0 = _SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0]; - H->v1 = _SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1]; - H->v2 = _SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0]; - H->v3 = _SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1]; + H->v0 = SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0]; + H->v1 = SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1]; + H->v2 = SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0]; + H->v3 = SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1]; H->p = H->buf; H->c = 0; diff --git a/Utilities/cmexpat/lib/winconfig.h b/Utilities/cmexpat/lib/winconfig.h index a689db69b..bc7d33335 100644 --- a/Utilities/cmexpat/lib/winconfig.h +++ b/Utilities/cmexpat/lib/winconfig.h @@ -9,7 +9,8 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Greg Stein Copyright (c) 2005 Karl Waclawek - Copyright (c) 2017-2021 Sebastian Pipping + Copyright (c) 2017-2023 Sebastian Pipping + Copyright (c) 2023 Orgad Shaneh Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -35,7 +36,9 @@ #ifndef WINCONFIG_H #define WINCONFIG_H -#define WIN32_LEAN_AND_MEAN +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif #include #undef WIN32_LEAN_AND_MEAN diff --git a/Utilities/cmexpat/lib/xmlparse.c b/Utilities/cmexpat/lib/xmlparse.c index 7db28d07a..2951fec70 100644 --- a/Utilities/cmexpat/lib/xmlparse.c +++ b/Utilities/cmexpat/lib/xmlparse.c @@ -1,4 +1,4 @@ -/* a30d2613dcfdef81475a9d1a349134d2d42722172fdaa7d5bb12ed2aa74b9596 (2.4.6+) +/* 2a14271ad4d35e82bde8ba210b4edb7998794bcbae54deab114046a300f9639a (2.6.2+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -13,13 +13,13 @@ Copyright (c) 2002-2016 Karl Waclawek Copyright (c) 2005-2009 Steven Solie Copyright (c) 2016 Eric Rahm - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2016 Gaurav Copyright (c) 2016 Thomas Beutlich Copyright (c) 2016 Gustavo Grieco Copyright (c) 2016 Pascal Cuoq Copyright (c) 2016 Ed Schouten - Copyright (c) 2017-2018 Rhodri James + Copyright (c) 2017-2022 Rhodri James Copyright (c) 2017 Václav Slavík Copyright (c) 2017 Viktor Szakats Copyright (c) 2017 Chanho Park @@ -32,8 +32,13 @@ Copyright (c) 2019 David Loffredo Copyright (c) 2019-2020 Ben Wagner Copyright (c) 2019 Vadim Zeitlin - Copyright (c) 2021 Dong-hee Na + Copyright (c) 2021 Donghee Na Copyright (c) 2022 Samanta Navarro + Copyright (c) 2022 Jeffrey Walton + Copyright (c) 2022 Jann Horn + Copyright (c) 2022 Sean McBride + Copyright (c) 2023 Owain Davies + Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -58,10 +63,25 @@ #define XML_BUILDING_EXPAT 1 -#include +#include "expat_config.h" -#if ! defined(_GNU_SOURCE) -# define _GNU_SOURCE 1 /* syscall prototype */ +#if ! defined(XML_GE) || (1 - XML_GE - 1 == 2) || (XML_GE < 0) || (XML_GE > 1) +# error XML_GE (for general entities) must be defined, non-empty, either 1 or 0 (0 to disable, 1 to enable; 1 is a common default) +#endif + +#if defined(XML_DTD) && XML_GE == 0 +# error Either undefine XML_DTD or define XML_GE to 1. +#endif + +#if ! defined(XML_CONTEXT_BYTES) || (1 - XML_CONTEXT_BYTES - 1 == 2) \ + || (XML_CONTEXT_BYTES + 0 < 0) +# error XML_CONTEXT_BYTES must be defined, non-empty and >=0 (0 to disable, >=1 to enable; 1024 is a common default) +#endif + +#if defined(HAVE_SYSCALL_GETRANDOM) +# if ! defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 /* syscall prototype */ +# endif #endif #ifdef _WIN32 @@ -71,6 +91,7 @@ # endif #endif +#include #include #include /* memset(), memcpy() */ #include @@ -129,11 +150,11 @@ Your options include: \ * Linux >=3.17 + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \ * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \ - * BSD / macOS >=10.7 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ - * BSD / macOS (including <10.7) (arc4random): HAVE_ARC4RANDOM, \ + * BSD / macOS >=10.7 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ + * BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \ * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \ * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \ - * Linux (including <3.17) / BSD / macOS (including <10.7) (/dev/urandom): XML_DEV_URANDOM, \ + * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \ * Windows >=Vista (rand_s): _WIN32. \ \ If insist on not using any of these, bypass this error by defining \ @@ -189,11 +210,13 @@ typedef char ICHAR; #endif /* Round up n to be a multiple of sz, where sz is a power of 2. */ -#define ROUND_UP(n, sz) (((n) + ((sz)-1)) & ~((sz)-1)) +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) /* Do safe (NULL-aware) pointer arithmetic */ #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) +#define EXPAT_MIN(a, b) (((a) < (b)) ? (a) : (b)) + #include "internal.h" #include "xmltok.h" #include "xmlrole.h" @@ -225,7 +248,7 @@ static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key); it odd, since odd numbers are always relative prime to a power of 2. */ #define SECOND_HASH(hash, mask, power) \ - ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2)) + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) #define PROBE_STEP(hash, mask, power) \ ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) @@ -277,7 +300,7 @@ typedef struct { XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to contain the 'raw' name as well. - A parser re-uses these structures, maintaining a list of allocated + A parser reuses these structures, maintaining a list of allocated TAG objects in a free list. */ typedef struct tag { @@ -406,12 +429,12 @@ enum XML_Account { XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ }; -#ifdef XML_DTD +#if XML_GE == 1 typedef unsigned long long XmlBigCount; typedef struct accounting { XmlBigCount countBytesDirect; XmlBigCount countBytesIndirect; - int debugLevel; + unsigned long debugLevel; float maximumAmplificationFactor; // >=1.0 unsigned long long activationThresholdBytes; } ACCOUNTING; @@ -420,9 +443,9 @@ typedef struct entity_stats { unsigned int countEverOpened; unsigned int currentDepth; unsigned int maximumDepthSeen; - int debugLevel; + unsigned long debugLevel; } ENTITY_STATS; -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, const char *end, const char **endPtr); @@ -462,41 +485,47 @@ static enum XML_Error doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, const char *start, const char *end, const char **endPtr, XML_Bool haveMore, enum XML_Account account); -static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *, +static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, const char *end, const char **nextPtr, XML_Bool haveMore, enum XML_Account account); #ifdef XML_DTD -static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *, +static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, const char *end, const char **nextPtr, XML_Bool haveMore); #endif /* XML_DTD */ static void freeBindings(XML_Parser parser, BINDING *bindings); -static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, - const char *s, TAG_NAME *tagNamePtr, +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, BINDING **bindingsPtr, enum XML_Account account); static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr); -static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, - XML_Bool isId, const XML_Char *dfltValue, - XML_Parser parser); -static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *, - XML_Bool isCdata, const char *, - const char *, STRING_POOL *, +static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, + XML_Bool isCdata, XML_Bool isId, + const XML_Char *value, XML_Parser parser); +static enum XML_Error storeAttributeValue(XML_Parser parser, + const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool, enum XML_Account account); -static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, - XML_Bool isCdata, const char *, - const char *, STRING_POOL *, +static enum XML_Error appendAttributeValue(XML_Parser parser, + const ENCODING *enc, + XML_Bool isCdata, const char *ptr, + const char *end, STRING_POOL *pool, enum XML_Account account); static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); -static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType); +#if XML_GE == 1 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, const char *end, enum XML_Account account); +#else +static enum XML_Error storeSelfEntityValue(XML_Parser parser, ENTITY *entity); +#endif static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); static int reportComment(XML_Parser parser, const ENCODING *enc, @@ -516,21 +545,22 @@ static void dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); -static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, - const HASH_TABLE *); +static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, + STRING_POOL *newPool, const HASH_TABLE *oldTable); static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); -static void FASTCALL hashTableInit(HASH_TABLE *, +static void FASTCALL hashTableInit(HASH_TABLE *table, const XML_Memory_Handling_Suite *ms); -static void FASTCALL hashTableClear(HASH_TABLE *); -static void FASTCALL hashTableDestroy(HASH_TABLE *); -static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); -static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *); +static void FASTCALL hashTableClear(HASH_TABLE *table); +static void FASTCALL hashTableDestroy(HASH_TABLE *table); +static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, + const HASH_TABLE *table); +static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); -static void FASTCALL poolInit(STRING_POOL *, +static void FASTCALL poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms); -static void FASTCALL poolClear(STRING_POOL *); -static void FASTCALL poolDestroy(STRING_POOL *); +static void FASTCALL poolClear(STRING_POOL *pool); +static void FASTCALL poolDestroy(STRING_POOL *pool); static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, const char *end); static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, @@ -560,7 +590,7 @@ static XML_Parser parserCreate(const XML_Char *encodingName, static void parserInit(XML_Parser parser, const XML_Char *encodingName); -#ifdef XML_DTD +#if XML_GE == 1 static float accountingGetCurrentAmplification(XML_Parser rootParser); static void accountingReportStats(XML_Parser originParser, const char *epilog); static void accountingOnAbort(XML_Parser originParser); @@ -583,13 +613,12 @@ static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, static XML_Parser getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff); -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ static unsigned long getDebugLevel(const char *variableName, unsigned long defaultDebugLevel); #define poolStart(pool) ((pool)->start) -#define poolEnd(pool) ((pool)->ptr) #define poolLength(pool) ((pool)->ptr - (pool)->start) #define poolChop(pool) ((void)--(pool->ptr)) #define poolLastChar(pool) (((pool)->ptr)[-1]) @@ -600,21 +629,41 @@ static unsigned long getDebugLevel(const char *variableName, ? 0 \ : ((*((pool)->ptr)++ = c), 1)) +#if ! defined(XML_TESTING) +const +#endif + XML_Bool g_reparseDeferralEnabledDefault + = XML_TRUE; // write ONLY in runtests.c +#if defined(XML_TESTING) +unsigned int g_bytesScanned = 0; // used for testing only +#endif + struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData macro works. */ void *m_userData; void *m_handlerArg; - char *m_buffer; + + // How the four parse buffer pointers below relate in time and space: + // + // m_buffer <= m_bufferPtr <= m_bufferEnd <= m_bufferLim + // | | | | + // <--parsed-->| | | + // <---parsing--->| | + // <--unoccupied-->| + // <---------total-malloced/realloced-------->| + + char *m_buffer; // malloc/realloc base pointer of parse buffer const XML_Memory_Handling_Suite m_mem; - /* first character to be parsed */ - const char *m_bufferPtr; - /* past last character to be parsed */ - char *m_bufferEnd; - /* allocated end of m_buffer */ - const char *m_bufferLim; + const char *m_bufferPtr; // first character to be parsed + char *m_bufferEnd; // past last character to be parsed + const char *m_bufferLim; // allocated end of m_buffer + XML_Index m_parseEndByteIndex; const char *m_parseEndPtr; + size_t m_partialTokenBytesBefore; /* used in heuristic to avoid O(n^2) */ + XML_Bool m_reparseDeferralEnabled; + int m_lastBufferRequestSize; XML_Char *m_dataBuf; XML_Char *m_dataBufEnd; XML_StartElementHandler m_startElementHandler; @@ -701,7 +750,7 @@ struct XML_ParserStruct { enum XML_ParamEntityParsing m_paramEntityParsing; #endif unsigned long m_hash_secret_salt; -#ifdef XML_DTD +#if XML_GE == 1 ACCOUNTING m_accounting; ENTITY_STATS m_entity_stats; #endif @@ -722,6 +771,7 @@ XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { return XML_ParserCreate_MM(encodingName, NULL, tmp); } +// "xml=http://www.w3.org/XML/1998/namespace" static const XML_Char implicitContext[] = {ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, @@ -945,6 +995,49 @@ get_hash_secret_salt(XML_Parser parser) { return parser->m_hash_secret_salt; } +static enum XML_Error +callProcessor(XML_Parser parser, const char *start, const char *end, + const char **endPtr) { + const size_t have_now = EXPAT_SAFE_PTR_DIFF(end, start); + + if (parser->m_reparseDeferralEnabled + && ! parser->m_parsingStatus.finalBuffer) { + // Heuristic: don't try to parse a partial token again until the amount of + // available data has increased significantly. + const size_t had_before = parser->m_partialTokenBytesBefore; + // ...but *do* try anyway if we're close to causing a reallocation. + size_t available_buffer + = EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); +#if XML_CONTEXT_BYTES > 0 + available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); +#endif + available_buffer + += EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd); + // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok + const bool enough + = (have_now >= 2 * had_before) + || ((size_t)parser->m_lastBufferRequestSize > available_buffer); + + if (! enough) { + *endPtr = start; // callers may expect this to be set + return XML_ERROR_NONE; + } + } +#if defined(XML_TESTING) + g_bytesScanned += (unsigned)have_now; +#endif + const enum XML_Error ret = parser->m_processor(parser, start, end, endPtr); + if (ret == XML_ERROR_NONE) { + // if we consumed nothing, remember what we had on this parse attempt. + if (*endPtr == start) { + parser->m_partialTokenBytesBefore = have_now; + } else { + parser->m_partialTokenBytesBefore = 0; + } + } + return ret; +} + static XML_Bool /* only valid for root parser */ startParsing(XML_Parser parser) { /* hash functions must be initialized before setContext() is called */ @@ -1066,6 +1159,14 @@ parserCreate(const XML_Char *encodingName, parserInit(parser, encodingName); if (encodingName && ! parser->m_protocolEncodingName) { + if (dtd) { + // We need to stop the upcoming call to XML_ParserFree from happily + // destroying parser->m_dtd because the DTD is shared with the parent + // parser and the only guard that keeps XML_ParserFree from destroying + // parser->m_dtd is parser->m_isParamEntity but it will be set to + // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all). + parser->m_dtd = NULL; + } XML_ParserFree(parser); return NULL; } @@ -1118,6 +1219,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_bufferEnd = parser->m_buffer; parser->m_parseEndByteIndex = 0; parser->m_parseEndPtr = NULL; + parser->m_partialTokenBytesBefore = 0; + parser->m_reparseDeferralEnabled = g_reparseDeferralEnabledDefault; + parser->m_lastBufferRequestSize = 0; parser->m_declElementType = NULL; parser->m_declAttributeId = NULL; parser->m_declEntity = NULL; @@ -1152,7 +1256,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { #endif parser->m_hash_secret_salt = 0; -#ifdef XML_DTD +#if XML_GE == 1 memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); parser->m_accounting.maximumAmplificationFactor @@ -1287,6 +1391,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ unsigned long oldhash_secret_salt; + XML_Bool oldReparseDeferralEnabled; /* Validate the oldParser parameter before we pull everything out of it */ if (oldParser == NULL) @@ -1331,6 +1436,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, to worry which hash secrets each table has. */ oldhash_secret_salt = parser->m_hash_secret_salt; + oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; #ifdef XML_DTD if (! context) @@ -1383,6 +1489,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; parser->m_ns_triplets = oldns_triplets; parser->m_hash_secret_salt = oldhash_secret_salt; + parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled; parser->m_parentParser = oldParser; #ifdef XML_DTD parser->m_paramEntityParsing = oldParamEntityParsing; @@ -1837,55 +1944,8 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { parser->m_parsingStatus.parsing = XML_PARSING; } - if (len == 0) { - parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; - if (! isFinal) - return XML_STATUS_OK; - parser->m_positionPtr = parser->m_bufferPtr; - parser->m_parseEndPtr = parser->m_bufferEnd; - - /* If data are left over from last buffer, and we now know that these - data are the final chunk of input, then we have to check them again - to detect errors based on that fact. - */ - parser->m_errorCode - = parser->m_processor(parser, parser->m_bufferPtr, - parser->m_parseEndPtr, &parser->m_bufferPtr); - - if (parser->m_errorCode == XML_ERROR_NONE) { - switch (parser->m_parsingStatus.parsing) { - case XML_SUSPENDED: - /* It is hard to be certain, but it seems that this case - * cannot occur. This code is cleaning up a previous parse - * with no new data (since len == 0). Changing the parsing - * state requires getting to execute a handler function, and - * there doesn't seem to be an opportunity for that while in - * this circumstance. - * - * Given the uncertainty, we retain the code but exclude it - * from coverage tests. - * - * LCOV_EXCL_START - */ - XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, - parser->m_bufferPtr, &parser->m_position); - parser->m_positionPtr = parser->m_bufferPtr; - return XML_STATUS_SUSPENDED; - /* LCOV_EXCL_STOP */ - case XML_INITIALIZED: - case XML_PARSING: - parser->m_parsingStatus.parsing = XML_FINISHED; - /* fall through */ - default: - return XML_STATUS_OK; - } - } - parser->m_eventEndPtr = parser->m_eventPtr; - parser->m_processor = errorProcessor; - return XML_STATUS_ERROR; - } -#ifndef XML_CONTEXT_BYTES - else if (parser->m_bufferPtr == parser->m_bufferEnd) { +#if XML_CONTEXT_BYTES == 0 + if (parser->m_bufferPtr == parser->m_bufferEnd) { const char *end; int nLeftOver; enum XML_Status result; @@ -1896,12 +1956,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { parser->m_processor = errorProcessor; return XML_STATUS_ERROR; } + // though this isn't a buffer request, we assume that `len` is the app's + // preferred buffer fill size, and therefore save it here. + parser->m_lastBufferRequestSize = len; parser->m_parseEndByteIndex += len; parser->m_positionPtr = s; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; parser->m_errorCode - = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); + = callProcessor(parser, s, parser->m_parseEndPtr = s + len, &end); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; @@ -1928,23 +1991,25 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { &parser->m_position); nLeftOver = s + len - end; if (nLeftOver) { - if (parser->m_buffer == NULL - || nLeftOver > parser->m_bufferLim - parser->m_buffer) { - /* avoid _signed_ integer overflow */ - char *temp = NULL; - const int bytesToAllocate = (int)((unsigned)len * 2U); - if (bytesToAllocate > 0) { - temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate); - } - if (temp == NULL) { - parser->m_errorCode = XML_ERROR_NO_MEMORY; - parser->m_eventPtr = parser->m_eventEndPtr = NULL; - parser->m_processor = errorProcessor; - return XML_STATUS_ERROR; - } - parser->m_buffer = temp; - parser->m_bufferLim = parser->m_buffer + bytesToAllocate; + // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED + // (and XML_ERROR_FINISHED) from XML_GetBuffer. + const enum XML_Parsing originalStatus = parser->m_parsingStatus.parsing; + parser->m_parsingStatus.parsing = XML_PARSING; + void *const temp = XML_GetBuffer(parser, nLeftOver); + parser->m_parsingStatus.parsing = originalStatus; + // GetBuffer may have overwritten this, but we want to remember what the + // app requested, not how many bytes were left over after parsing. + parser->m_lastBufferRequestSize = len; + if (temp == NULL) { + // NOTE: parser->m_errorCode has already been set by XML_GetBuffer(). + parser->m_eventPtr = parser->m_eventEndPtr = NULL; + parser->m_processor = errorProcessor; + return XML_STATUS_ERROR; } + // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we + // don't have any data to preserve, and can copy straight into the start + // of the buffer rather than the GetBuffer return pointer (which may be + // pointing further into the allocated buffer). memcpy(parser->m_buffer, end, nLeftOver); } parser->m_bufferPtr = parser->m_buffer; @@ -1955,16 +2020,15 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { parser->m_eventEndPtr = parser->m_bufferPtr; return result; } -#endif /* not defined XML_CONTEXT_BYTES */ - else { - void *buff = XML_GetBuffer(parser, len); - if (buff == NULL) - return XML_STATUS_ERROR; - else { - memcpy(buff, s, len); - return XML_ParseBuffer(parser, len, isFinal); - } +#endif /* XML_CONTEXT_BYTES == 0 */ + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + if (len > 0) { + assert(s != NULL); // make sure s==NULL && len!=0 was rejected above + memcpy(buff, s, len); } + return XML_ParseBuffer(parser, len, isFinal); } enum XML_Status XMLCALL @@ -2004,8 +2068,8 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { parser->m_parseEndByteIndex += len; parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; - parser->m_errorCode = parser->m_processor( - parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); + parser->m_errorCode = callProcessor(parser, start, parser->m_parseEndPtr, + &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { parser->m_eventEndPtr = parser->m_eventPtr; @@ -2050,10 +2114,14 @@ XML_GetBuffer(XML_Parser parser, int len) { default:; } - if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { -#ifdef XML_CONTEXT_BYTES + // whether or not the request succeeds, `len` seems to be the app's preferred + // buffer fill size; remember it. + parser->m_lastBufferRequestSize = len; + if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd) + || parser->m_buffer == NULL) { +#if XML_CONTEXT_BYTES > 0 int keep; -#endif /* defined XML_CONTEXT_BYTES */ +#endif /* XML_CONTEXT_BYTES > 0 */ /* Do not invoke signed arithmetic overflow: */ int neededSize = (int)((unsigned)len + (unsigned)EXPAT_SAFE_PTR_DIFF( @@ -2062,7 +2130,7 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL; } -#ifdef XML_CONTEXT_BYTES +#if XML_CONTEXT_BYTES > 0 keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); if (keep > XML_CONTEXT_BYTES) keep = XML_CONTEXT_BYTES; @@ -2072,10 +2140,11 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } neededSize += keep; -#endif /* defined XML_CONTEXT_BYTES */ - if (neededSize - <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { -#ifdef XML_CONTEXT_BYTES +#endif /* XML_CONTEXT_BYTES > 0 */ + if (parser->m_buffer && parser->m_bufferPtr + && neededSize + <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { +#if XML_CONTEXT_BYTES > 0 if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { int offset = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) @@ -2088,19 +2157,17 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_bufferPtr -= offset; } #else - if (parser->m_buffer && parser->m_bufferPtr) { - memmove(parser->m_buffer, parser->m_bufferPtr, - EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); - parser->m_bufferEnd - = parser->m_buffer - + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); - parser->m_bufferPtr = parser->m_buffer; - } -#endif /* not defined XML_CONTEXT_BYTES */ + memmove(parser->m_buffer, parser->m_bufferPtr, + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); + parser->m_bufferEnd + = parser->m_buffer + + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); + parser->m_bufferPtr = parser->m_buffer; +#endif /* XML_CONTEXT_BYTES > 0 */ } else { char *newBuf; int bufferSize - = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr); + = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer); if (bufferSize == 0) bufferSize = INIT_BUFFER_SIZE; do { @@ -2117,7 +2184,7 @@ XML_GetBuffer(XML_Parser parser, int len) { return NULL; } parser->m_bufferLim = newBuf + bufferSize; -#ifdef XML_CONTEXT_BYTES +#if XML_CONTEXT_BYTES > 0 if (parser->m_bufferPtr) { memcpy(newBuf, &parser->m_bufferPtr[-keep], EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) @@ -2147,7 +2214,7 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_bufferEnd = newBuf; } parser->m_bufferPtr = parser->m_buffer = newBuf; -#endif /* not defined XML_CONTEXT_BYTES */ +#endif /* XML_CONTEXT_BYTES > 0 */ } parser->m_eventPtr = parser->m_eventEndPtr = NULL; parser->m_positionPtr = NULL; @@ -2197,7 +2264,7 @@ XML_ResumeParser(XML_Parser parser) { } parser->m_parsingStatus.parsing = XML_PARSING; - parser->m_errorCode = parser->m_processor( + parser->m_errorCode = callProcessor( parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); if (parser->m_errorCode != XML_ERROR_NONE) { @@ -2261,7 +2328,7 @@ XML_GetCurrentByteCount(XML_Parser parser) { const char *XMLCALL XML_GetInputContext(XML_Parser parser, int *offset, int *size) { -#ifdef XML_CONTEXT_BYTES +#if XML_CONTEXT_BYTES > 0 if (parser == NULL) return NULL; if (parser->m_eventPtr && parser->m_buffer) { @@ -2275,7 +2342,7 @@ XML_GetInputContext(XML_Parser parser, int *offset, int *size) { (void)parser; (void)offset; (void)size; -#endif /* defined XML_CONTEXT_BYTES */ +#endif /* XML_CONTEXT_BYTES > 0 */ return (const char *)0; } @@ -2495,7 +2562,7 @@ XML_GetFeatureList(void) { #ifdef XML_DTD {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, #endif -#ifdef XML_CONTEXT_BYTES +#if XML_CONTEXT_BYTES > 0 {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), XML_CONTEXT_BYTES}, #endif @@ -2511,8 +2578,9 @@ XML_GetFeatureList(void) { #ifdef XML_ATTR_INFO {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, #endif -#ifdef XML_DTD - /* Added in Expat 2.4.0. */ +#if XML_GE == 1 + /* Added in Expat 2.4.0 for XML_DTD defined and + * added in Expat 2.6.0 for XML_GE == 1. */ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, XML_L("XML_BLAP_MAX_AMP"), (long int) @@ -2520,13 +2588,15 @@ XML_GetFeatureList(void) { {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, XML_L("XML_BLAP_ACT_THRES"), EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, + /* Added in Expat 2.6.0. */ + {XML_FEATURE_GE, XML_L("XML_GE"), 0}, #endif {XML_FEATURE_END, NULL, 0}}; return features; } -#ifdef XML_DTD +#if XML_GE == 1 XML_Bool XMLCALL XML_SetBillionLaughsAttackProtectionMaximumAmplification( XML_Parser parser, float maximumAmplificationFactor) { @@ -2548,7 +2618,16 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( parser->m_accounting.activationThresholdBytes = activationThresholdBytes; return XML_TRUE; } -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ + +XML_Bool XMLCALL +XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled) { + if (parser != NULL && (enabled == XML_TRUE || enabled == XML_FALSE)) { + parser->m_reparseDeferralEnabled = enabled; + return XML_TRUE; + } + return XML_FALSE; +} /* Initially tag->rawName always points into the parse buffer; for those TAG instances opened while the current parse buffer was @@ -2570,7 +2649,7 @@ storeRawNames(XML_Parser parser) { */ if (tag->rawName == rawNameBuf) break; - /* For re-use purposes we need to ensure that the + /* For reuse purposes we need to ensure that the size of tag->buf is a multiple of sizeof(XML_Char). */ rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); @@ -2634,13 +2713,13 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start, int tok = XmlContentTok(parser->m_encoding, start, end, &next); switch (tok) { case XML_TOK_BOM: -#ifdef XML_DTD +#if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; } -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ /* If we are at the end of the buffer, this would cause the next stage, i.e. externalEntityInitProcessor3, to pass control directly to @@ -2754,7 +2833,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, for (;;) { const char *next = s; /* XmlContentTok doesn't always set the last arg */ int tok = XmlContentTok(enc, s, end, &next); -#ifdef XML_DTD +#if XML_GE == 1 const char *accountAfter = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) ? (haveMore ? s /* i.e. 0 bytes */ : end) @@ -2820,14 +2899,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, XML_Char ch = (XML_Char)XmlPredefinedEntityName( enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); if (ch) { -#ifdef XML_DTD +#if XML_GE == 1 /* NOTE: We are replacing 4-6 characters original input for 1 character * so there is no amplification and hence recording without * protection. */ accountingDiffTolerated(parser, tok, (char *)&ch, ((char *)&ch) + sizeof(XML_Char), __LINE__, XML_ACCOUNT_ENTITY_EXPANSION); -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ if (parser->m_characterDataHandler) parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); else if (parser->m_defaultHandler) @@ -3009,9 +3088,6 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, int len; const char *rawName; TAG *tag = parser->m_tagStack; - parser->m_tagStack = tag->parent; - tag->parent = parser->m_freeTagList; - parser->m_freeTagList = tag; rawName = s + enc->minBytesPerChar * 2; len = XmlNameLength(enc, rawName); if (len != tag->rawNameLength @@ -3019,6 +3095,9 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, *eventPP = rawName; return XML_ERROR_TAG_MISMATCH; } + parser->m_tagStack = tag->parent; + tag->parent = parser->m_freeTagList; + parser->m_freeTagList = tag; --parser->m_tagLevel; if (parser->m_endElementHandler) { const XML_Char *localPart; @@ -3028,13 +3107,13 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, if (parser->m_ns && localPart) { /* localPart and prefix may have been overwritten in tag->name.str, since this points to the binding->uri - buffer which gets re-used; so we have to add them again + buffer which gets reused; so we have to add them again */ uri = (XML_Char *)tag->name.str + tag->name.uriLen; /* don't need to check for space - already done in storeAtts() */ while (*localPart) *uri++ = *localPart++; - prefix = (XML_Char *)tag->name.prefix; + prefix = tag->name.prefix; if (parser->m_ns_triplets && prefix) { *uri++ = parser->m_namespaceSeparator; while (*prefix) @@ -3101,7 +3180,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, However, now we have a start/endCdataSectionHandler, so it seems easier to let the user deal with this. */ - else if (0 && parser->m_characterDataHandler) + else if ((0) && parser->m_characterDataHandler) parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0); /* END disabled code */ @@ -3130,8 +3209,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); } else parser->m_characterDataHandler( - parser->m_handlerArg, (XML_Char *)s, - (int)((XML_Char *)end - (XML_Char *)s)); + parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)end - (const XML_Char *)s)); } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, end); /* We are at the end of the final buffer, should we check for @@ -3164,8 +3243,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, *eventPP = s; } } else - charDataHandler(parser->m_handlerArg, (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); + charDataHandler(parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)next - (const XML_Char *)s)); } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); } break; @@ -3704,12 +3783,124 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, return XML_ERROR_NONE; } +static XML_Bool +is_rfc3986_uri_char(XML_Char candidate) { + // For the RFC 3986 ANBF grammar see + // https://datatracker.ietf.org/doc/html/rfc3986#appendix-A + + switch (candidate) { + // From rule "ALPHA" (uppercase half) + 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': + + // From rule "ALPHA" (lowercase half) + 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': + + // From rule "DIGIT" + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + + // From rule "pct-encoded" + case '%': + + // From rule "unreserved" + case '-': + case '.': + case '_': + case '~': + + // From rule "gen-delims" + case ':': + case '/': + case '?': + case '#': + case '[': + case ']': + case '@': + + // From rule "sub-delims" + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return XML_TRUE; + + default: + return XML_FALSE; + } +} + /* addBinding() overwrites the value of prefix->binding without checking. Therefore one must keep track of the old value outside of addBinding(). */ static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr) { + // "http://www.w3.org/XML/1998/namespace" static const XML_Char xmlNamespace[] = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, @@ -3720,6 +3911,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0'}; static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1; + // "http://www.w3.org/2000/xmlns/" static const XML_Char xmlnsNamespace[] = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, @@ -3760,14 +3952,26 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) isXMLNS = XML_FALSE; - // NOTE: While Expat does not validate namespace URIs against RFC 3986, - // we have to at least make sure that the XML processor on top of - // Expat (that is splitting tag names by namespace separator into - // 2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused - // by an attacker putting additional namespace separator characters - // into namespace declarations. That would be ambiguous and not to - // be expected. - if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) { + // NOTE: While Expat does not validate namespace URIs against RFC 3986 + // today (and is not REQUIRED to do so with regard to the XML 1.0 + // namespaces specification) we have to at least make sure, that + // the application on top of Expat (that is likely splitting expanded + // element names ("qualified names") of form + // "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces + // in its element handler code) cannot be confused by an attacker + // putting additional namespace separator characters into namespace + // declarations. That would be ambiguous and not to be expected. + // + // While the HTML API docs of function XML_ParserCreateNS have been + // advising against use of a namespace separator character that can + // appear in a URI for >20 years now, some widespread applications + // are using URI characters (':' (colon) in particular) for a + // namespace separator, in practice. To keep these applications + // functional, we only reject namespaces URIs containing the + // application-chosen namespace separator if the chosen separator + // is a non-URI character with regard to RFC 3986. + if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator) + && ! is_rfc3986_uri_char(uri[len])) { return XML_ERROR_SYNTAX; } } @@ -3904,7 +4108,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, for (;;) { const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok = XmlCdataSectionTok(enc, s, end, &next); -#ifdef XML_DTD +#if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; @@ -3919,7 +4123,7 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, parser->m_endCdataSectionHandler(parser->m_handlerArg); /* BEGIN disabled code */ /* see comment under XML_TOK_CDATA_SECT_OPEN */ - else if (0 && parser->m_characterDataHandler) + else if ((0) && parser->m_characterDataHandler) parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0); /* END disabled code */ @@ -3955,8 +4159,8 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, *eventPP = s; } } else - charDataHandler(parser->m_handlerArg, (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); + charDataHandler(parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)next - (const XML_Char *)s)); } else if (parser->m_defaultHandler) reportDefault(parser, enc, s, next); } break; @@ -4056,7 +4260,7 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, *eventPP = s; *startPtr = NULL; tok = XmlIgnoreSectionTok(enc, s, end, &next); -# ifdef XML_DTD +# if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); @@ -4144,11 +4348,11 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, const XML_Char *storedEncName = NULL; const ENCODING *newEncoding = NULL; const char *version = NULL; - const char *versionend; + const char *versionend = NULL; const XML_Char *storedversion = NULL; int standalone = -1; -#ifdef XML_DTD +#if XML_GE == 1 if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); @@ -4346,16 +4550,16 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, parser->m_processor = entityValueProcessor; return entityValueProcessor(parser, next, end, nextPtr); } - /* If we are at the end of the buffer, this would cause XmlPrologTok to - return XML_TOK_NONE on the next call, which would then cause the - function to exit with *nextPtr set to s - that is what we want for other - tokens, but not for the BOM - we would rather like to skip it; - then, when this routine is entered the next time, XmlPrologTok will - return XML_TOK_INVALID, since the BOM is still in the buffer + /* XmlPrologTok has now set the encoding based on the BOM it found, and we + must move s and nextPtr forward to consume the BOM. + + If we didn't, and got XML_TOK_NONE from the next XmlPrologTok call, we + would leave the BOM in the buffer and return. On the next call to this + function, our XmlPrologTok call would return XML_TOK_INVALID, since it + is not valid to have multiple BOMs. */ - else if (tok == XML_TOK_BOM && next == end - && ! parser->m_parsingStatus.finalBuffer) { -# ifdef XML_DTD + else if (tok == XML_TOK_BOM) { +# if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); @@ -4364,7 +4568,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, # endif *nextPtr = next; - return XML_ERROR_NONE; + s = next; } /* If we get this token, we have the start of what might be a normal tag, but not a declaration (i.e. it doesn't begin with @@ -4571,11 +4775,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, } } role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); -#ifdef XML_DTD +#if XML_GE == 1 switch (role) { case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl - case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl +# ifdef XML_DTD + case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl +# endif break; default: if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { @@ -4848,10 +5054,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, parser->m_handlerArg, parser->m_declElementType->name, parser->m_declAttributeId->name, parser->m_declAttributeType, 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); - poolClear(&parser->m_tempPool); handleDefault = XML_FALSE; } } + poolClear(&parser->m_tempPool); break; case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: case XML_ROLE_FIXED_ATTRIBUTE_VALUE: @@ -4893,6 +5099,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, break; case XML_ROLE_ENTITY_VALUE: if (dtd->keepProcessing) { +#if XML_GE == 1 + // This will store the given replacement text in + // parser->m_declEntity->textPtr. enum XML_Error result = storeEntityValue(parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar, XML_ACCOUNT_NONE); @@ -4913,6 +5122,25 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, poolDiscard(&dtd->entityValuePool); if (result != XML_ERROR_NONE) return result; +#else + // This will store "&entity123;" in parser->m_declEntity->textPtr + // to end up as "&entity123;" in the handler. + if (parser->m_declEntity != NULL) { + const enum XML_Error result + = storeSelfEntityValue(parser, parser->m_declEntity); + if (result != XML_ERROR_NONE) + return result; + + if (parser->m_entityDeclHandler) { + *eventEndPP = s; + parser->m_entityDeclHandler( + parser->m_handlerArg, parser->m_declEntity->name, + parser->m_declEntity->is_param, parser->m_declEntity->textPtr, + parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } +#endif } break; case XML_ROLE_DOCTYPE_SYSTEM_ID: @@ -4971,6 +5199,16 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, } break; case XML_ROLE_ENTITY_COMPLETE: +#if XML_GE == 0 + // This will store "&entity123;" in entity->textPtr + // to end up as "&entity123;" in the handler. + if (parser->m_declEntity != NULL) { + const enum XML_Error result + = storeSelfEntityValue(parser, parser->m_declEntity); + if (result != XML_ERROR_NONE) + return result; + } +#endif if (dtd->keepProcessing && parser->m_declEntity && parser->m_entityDeclHandler) { *eventEndPP = s; @@ -5259,7 +5497,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, * * If 'standalone' is false, the DTD must have no * parameter entities or we wouldn't have passed the outer - * 'if' statement. That measn the only entity in the hash + * 'if' statement. That means the only entity in the hash * table is the external subset name "#" which cannot be * given as a parameter entity name in XML syntax, so the * lookup must have returned NULL and we don't even reach @@ -5512,7 +5750,7 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end, for (;;) { const char *next = NULL; int tok = XmlPrologTok(parser->m_encoding, s, end, &next); -#ifdef XML_DTD +#if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, XML_ACCOUNT_DIRECT)) { accountingOnAbort(parser); @@ -5592,7 +5830,7 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { return XML_ERROR_NO_MEMORY; } entity->open = XML_TRUE; -#ifdef XML_DTD +#if XML_GE == 1 entityTrackingOnOpen(parser, entity, __LINE__); #endif entity->processed = 0; @@ -5625,10 +5863,10 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { entity->processed = (int)(next - textStart); parser->m_processor = internalEntityProcessor; - } else { -#ifdef XML_DTD + } else if (parser->m_openInternalEntities->entity == entity) { +#if XML_GE == 1 entityTrackingOnClose(parser, entity, __LINE__); -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ entity->open = XML_FALSE; parser->m_openInternalEntities = openEntity->next; /* put openEntity back in list of free instances */ @@ -5671,19 +5909,27 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, if (result != XML_ERROR_NONE) return result; - else if (textEnd != next - && parser->m_parsingStatus.parsing == XML_SUSPENDED) { + + if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { entity->processed = (int)(next - (const char *)entity->textPtr); return result; - } else { -#ifdef XML_DTD - entityTrackingOnClose(parser, entity, __LINE__); + } + +#if XML_GE == 1 + entityTrackingOnClose(parser, entity, __LINE__); #endif - entity->open = XML_FALSE; - parser->m_openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ - openEntity->next = parser->m_freeInternalEntities; - parser->m_freeInternalEntities = openEntity; + entity->open = XML_FALSE; + parser->m_openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = parser->m_freeInternalEntities; + parser->m_freeInternalEntities = openEntity; + + // If there are more open entities we want to stop right here and have the + // upcoming call to XML_ResumeParser continue with entity content, or it would + // be ignored altogether. + if (parser->m_openInternalEntities != NULL + && parser->m_parsingStatus.parsing == XML_SUSPENDED) { + return XML_ERROR_NONE; } #ifdef XML_DTD @@ -5699,10 +5945,15 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, { parser->m_processor = contentProcessor; /* see externalEntityContentProcessor vs contentProcessor */ - return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, - s, end, nextPtr, - (XML_Bool)! parser->m_parsingStatus.finalBuffer, - XML_ACCOUNT_DIRECT); + result = doContent(parser, parser->m_parentParser ? 1 : 0, + parser->m_encoding, s, end, nextPtr, + (XML_Bool)! parser->m_parsingStatus.finalBuffer, + XML_ACCOUNT_DIRECT); + if (result == XML_ERROR_NONE) { + if (! storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; } } @@ -5743,7 +5994,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, const char *next = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ int tok = XmlAttributeValueTok(enc, ptr, end, &next); -#ifdef XML_DTD +#if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; @@ -5808,14 +6059,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, XML_Char ch = (XML_Char)XmlPredefinedEntityName( enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); if (ch) { -#ifdef XML_DTD +#if XML_GE == 1 /* NOTE: We are replacing 4-6 characters original input for 1 character * so there is no amplification and hence recording without * protection. */ accountingDiffTolerated(parser, tok, (char *)&ch, ((char *)&ch) + sizeof(XML_Char), __LINE__, XML_ACCOUNT_ENTITY_EXPANSION); -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ if (! poolAppendChar(pool, ch)) return XML_ERROR_NO_MEMORY; break; @@ -5893,14 +6144,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, enum XML_Error result; const XML_Char *textEnd = entity->textPtr + entity->textLen; entity->open = XML_TRUE; -#ifdef XML_DTD +#if XML_GE == 1 entityTrackingOnOpen(parser, entity, __LINE__); #endif result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata, (const char *)entity->textPtr, (const char *)textEnd, pool, XML_ACCOUNT_ENTITY_EXPANSION); -#ifdef XML_DTD +#if XML_GE == 1 entityTrackingOnClose(parser, entity, __LINE__); #endif entity->open = XML_FALSE; @@ -5930,6 +6181,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, /* not reached */ } +#if XML_GE == 1 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *entityTextPtr, const char *entityTextEnd, @@ -5937,12 +6189,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, DTD *const dtd = parser->m_dtd; /* save one level of indirection */ STRING_POOL *pool = &(dtd->entityValuePool); enum XML_Error result = XML_ERROR_NONE; -#ifdef XML_DTD +# ifdef XML_DTD int oldInEntityValue = parser->m_prologState.inEntityValue; parser->m_prologState.inEntityValue = 1; -#else +# else UNUSED_P(account); -#endif /* XML_DTD */ +# endif /* XML_DTD */ /* never return Null for the value argument in EntityDeclHandler, since this would indicate an external entity; therefore we have to make sure that entityValuePool.start is not null */ @@ -5956,18 +6208,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); -#ifdef XML_DTD if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, account)) { accountingOnAbort(parser); result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; goto endEntityValue; } -#endif switch (tok) { case XML_TOK_PARAM_ENTITY_REF: -#ifdef XML_DTD +# ifdef XML_DTD if (parser->m_isParamEntity || enc != parser->m_encoding) { const XML_Char *name; ENTITY *entity; @@ -5990,7 +6240,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, dtd->keepProcessing = dtd->standalone; goto endEntityValue; } - if (entity->open) { + if (entity->open || (entity == parser->m_declEntity)) { if (enc == parser->m_encoding) parser->m_eventPtr = entityTextPtr; result = XML_ERROR_RECURSIVE_ENTITY_REF; @@ -6029,7 +6279,7 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, } break; } -#endif /* XML_DTD */ +# endif /* XML_DTD */ /* In the internal subset, PE references are not legal within markup declarations, e.g entity values in this case. */ parser->m_eventPtr = entityTextPtr; @@ -6110,12 +6360,38 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, entityTextPtr = next; } endEntityValue: -#ifdef XML_DTD +# ifdef XML_DTD parser->m_prologState.inEntityValue = oldInEntityValue; -#endif /* XML_DTD */ +# endif /* XML_DTD */ return result; } +#else /* XML_GE == 0 */ + +static enum XML_Error +storeSelfEntityValue(XML_Parser parser, ENTITY *entity) { + // This will store "&entity123;" in entity->textPtr + // to end up as "&entity123;" in the handler. + const char *const entity_start = "&"; + const char *const entity_end = ";"; + + STRING_POOL *const pool = &(parser->m_dtd->entityValuePool); + if (! poolAppendString(pool, entity_start) + || ! poolAppendString(pool, entity->name) + || ! poolAppendString(pool, entity_end)) { + poolDiscard(pool); + return XML_ERROR_NO_MEMORY; + } + + entity->textPtr = poolStart(pool); + entity->textLen = (int)(poolLength(pool)); + poolFinish(pool); + + return XML_ERROR_NONE; +} + +#endif /* XML_GE == 0 */ + static void FASTCALL normalizeLines(XML_Char *s) { XML_Char *p; @@ -6226,8 +6502,9 @@ reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, } while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); } else - parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s, - (int)((XML_Char *)end - (XML_Char *)s)); + parser->m_defaultHandler( + parser->m_handlerArg, (const XML_Char *)s, + (int)((const XML_Char *)end - (const XML_Char *)s)); } static int @@ -6331,7 +6608,7 @@ getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, name = poolStoreString(&dtd->pool, enc, start, end); if (! name) return NULL; - /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + /* skip quotation mark - its storage will be reused (like in name[-1]) */ ++name; id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); @@ -6481,6 +6758,10 @@ getContext(XML_Parser parser) { static XML_Bool setContext(XML_Parser parser, const XML_Char *context) { + if (context == NULL) { + return XML_FALSE; + } + DTD *const dtd = parser->m_dtd; /* save one level of indirection */ const XML_Char *s = context; @@ -7071,7 +7352,7 @@ poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, return NULL; for (;;) { const enum XML_Convert_Result convert_res = XmlConvert( - enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + enc, &ptr, end, (ICHAR **)&(pool->ptr), (const ICHAR *)pool->end); if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) break; @@ -7502,10 +7783,12 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { return result; } -#ifdef XML_DTD +#if XML_GE == 1 static float accountingGetCurrentAmplification(XML_Parser rootParser) { + // 1.........1.........12 => 22 + const size_t lenOfShortestInclude = sizeof("") - 1; const XmlBigCount countBytesOutput = rootParser->m_accounting.countBytesDirect + rootParser->m_accounting.countBytesIndirect; @@ -7513,7 +7796,9 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { = rootParser->m_accounting.countBytesDirect ? (countBytesOutput / (float)(rootParser->m_accounting.countBytesDirect)) - : 1.0f; + : ((lenOfShortestInclude + + rootParser->m_accounting.countBytesIndirect) + / (float)lenOfShortestInclude); assert(! rootParser->m_parentParser); return amplificationFactor; } @@ -7523,7 +7808,7 @@ accountingReportStats(XML_Parser originParser, const char *epilog) { const XML_Parser rootParser = getRootParserOf(originParser, NULL); assert(! rootParser->m_parentParser); - if (rootParser->m_accounting.debugLevel < 1) { + if (rootParser->m_accounting.debugLevel == 0u) { return; } @@ -7560,7 +7845,7 @@ accountingReportDiff(XML_Parser rootParser, /* Note: Performance is of no concern here */ const char *walker = before; - if ((rootParser->m_accounting.debugLevel >= 3) + if ((rootParser->m_accounting.debugLevel >= 3u) || (after - before) <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) { for (; walker < after; walker++) { @@ -7625,7 +7910,7 @@ accountingDiffTolerated(XML_Parser originParser, int tok, const char *before, || (amplificationFactor <= rootParser->m_accounting.maximumAmplificationFactor); - if (rootParser->m_accounting.debugLevel >= 2) { + if (rootParser->m_accounting.debugLevel >= 2u) { accountingReportStats(rootParser, ""); accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after, bytesMore, source_line, account); @@ -7652,7 +7937,7 @@ static void entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity, const char *action, int sourceLine) { assert(! rootParser->m_parentParser); - if (rootParser->m_entity_stats.debugLevel < 1) + if (rootParser->m_entity_stats.debugLevel == 0u) return; # if defined(XML_UNICODE) @@ -8233,7 +8518,7 @@ unsignedCharToPrintable(unsigned char c) { assert(0); /* never gets here */ } -#endif /* XML_DTD */ +#endif /* XML_GE == 1 */ static unsigned long getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { @@ -8244,9 +8529,9 @@ getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { const char *const value = valueOrNull; errno = 0; - char *afterValue = (char *)value; + char *afterValue = NULL; unsigned long debugLevel = strtoul(value, &afterValue, 10); - if ((errno != 0) || (afterValue[0] != '\0')) { + if ((errno != 0) || (afterValue == value) || (afterValue[0] != '\0')) { errno = 0; return defaultDebugLevel; } diff --git a/Utilities/cmexpat/lib/xmlrole.c b/Utilities/cmexpat/lib/xmlrole.c index 3f0f5c150..2c48bf408 100644 --- a/Utilities/cmexpat/lib/xmlrole.c +++ b/Utilities/cmexpat/lib/xmlrole.c @@ -12,10 +12,10 @@ Copyright (c) 2002-2006 Karl Waclawek Copyright (c) 2002-2003 Fred L. Drake, Jr. Copyright (c) 2005-2009 Steven Solie - Copyright (c) 2016-2021 Sebastian Pipping + Copyright (c) 2016-2023 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 David Loffredo - Copyright (c) 2021 Dong-hee Na + Copyright (c) 2021 Donghee Na Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -38,7 +38,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include "expat_config.h" #include diff --git a/Utilities/cmexpat/lib/xmlrole.h b/Utilities/cmexpat/lib/xmlrole.h index d6e1fa150..a7904274c 100644 --- a/Utilities/cmexpat/lib/xmlrole.h +++ b/Utilities/cmexpat/lib/xmlrole.h @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Karl Waclawek Copyright (c) 2002 Fred L. Drake, Jr. - Copyright (c) 2017 Sebastian Pipping + Copyright (c) 2017-2024 Sebastian Pipping Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -127,9 +127,9 @@ typedef struct prolog_state { #endif /* XML_DTD */ } PROLOG_STATE; -void XmlPrologStateInit(PROLOG_STATE *); +void XmlPrologStateInit(PROLOG_STATE *state); #ifdef XML_DTD -void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +void XmlPrologStateInitExternalEntity(PROLOG_STATE *state); #endif /* XML_DTD */ #define XmlTokenRole(state, tok, ptr, end, enc) \ diff --git a/Utilities/cmexpat/lib/xmltok.c b/Utilities/cmexpat/lib/xmltok.c index c659983b4..29a66d72c 100644 --- a/Utilities/cmexpat/lib/xmltok.c +++ b/Utilities/cmexpat/lib/xmltok.c @@ -12,7 +12,7 @@ Copyright (c) 2002 Greg Stein Copyright (c) 2002-2016 Karl Waclawek Copyright (c) 2005-2009 Steven Solie - Copyright (c) 2016-2022 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2016 Pascal Cuoq Copyright (c) 2016 Don Lewis Copyright (c) 2017 Rhodri James @@ -20,7 +20,10 @@ Copyright (c) 2017 Benbuck Nason Copyright (c) 2017 José Gutiérrez de la Concha Copyright (c) 2019 David Loffredo - Copyright (c) 2021 Dong-hee Na + Copyright (c) 2021 Donghee Na + Copyright (c) 2022 Martin Ettl + Copyright (c) 2022 Sean McBride + Copyright (c) 2023 Hanno Böck Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -43,7 +46,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include "expat_config.h" #include #include /* memcpy */ @@ -75,7 +78,7 @@ #define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) #define UCS2_GET_NAMING(pages, hi, lo) \ - (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo)&0x1F))) + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F))) /* A 2 byte UTF-8 representation splits the characters 11 bits between the bottom 5 and 6 bits of the bytes. We need 8 bits to index into @@ -99,7 +102,7 @@ & (1u << (((byte)[2]) & 0x1F))) /* Detection of invalid UTF-8 sequences is based on Table 3.1B - of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + of Unicode 3.2: https://www.unicode.org/unicode/reports/tr28/ with the additional restriction of not allowing the Unicode code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). Implementation details: @@ -224,7 +227,7 @@ struct normal_encoding { /* isNmstrt2 */ NULL, /* isNmstrt3 */ NULL, /* isNmstrt4 */ NULL, \ /* isInvalid2 */ NULL, /* isInvalid3 */ NULL, /* isInvalid4 */ NULL -static int FASTCALL checkCharRefNumber(int); +static int FASTCALL checkCharRefNumber(int result); #include "xmltok_impl.h" #include "ascii.h" @@ -242,7 +245,7 @@ static int FASTCALL checkCharRefNumber(int); #endif #define SB_BYTE_TYPE(enc, p) \ - (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + (((const struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) #ifdef XML_MIN_SIZE static int PTRFASTCALL @@ -296,7 +299,7 @@ sb_charMatches(const ENCODING *enc, const char *p, int c) { } #else /* c is an ASCII character */ -# define CHAR_MATCHES(enc, p, c) (*(p) == c) +# define CHAR_MATCHES(enc, p, c) (*(p) == (c)) #endif #define PREFIX(ident) normal_##ident @@ -406,7 +409,7 @@ utf8_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, unsigned short *to = *toP; const char *from = *fromP; while (from < fromLim && to < toLim) { - switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + switch (SB_BYTE_TYPE(enc, from)) { case BT_LEAD2: if (fromLim - from < 2) { res = XML_CONVERT_INPUT_INCOMPLETE; @@ -714,33 +717,28 @@ unicode_byte_type(char hi, char lo) { return res; \ } -#define SET2(ptr, ch) (((ptr)[0] = ((ch)&0xff)), ((ptr)[1] = ((ch) >> 8))) #define GET_LO(ptr) ((unsigned char)(ptr)[0]) #define GET_HI(ptr) ((unsigned char)(ptr)[1]) DEFINE_UTF16_TO_UTF8(little2_) DEFINE_UTF16_TO_UTF16(little2_) -#undef SET2 #undef GET_LO #undef GET_HI -#define SET2(ptr, ch) (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch)&0xFF))) #define GET_LO(ptr) ((unsigned char)(ptr)[1]) #define GET_HI(ptr) ((unsigned char)(ptr)[0]) DEFINE_UTF16_TO_UTF8(big2_) DEFINE_UTF16_TO_UTF16(big2_) -#undef SET2 #undef GET_LO #undef GET_HI #define LITTLE2_BYTE_TYPE(enc, p) \ - ((p)[1] == 0 ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ - : unicode_byte_type((p)[1], (p)[0])) + ((p)[1] == 0 ? SB_BYTE_TYPE(enc, p) : unicode_byte_type((p)[1], (p)[0])) #define LITTLE2_BYTE_TO_ASCII(p) ((p)[1] == 0 ? (p)[0] : -1) -#define LITTLE2_CHAR_MATCHES(p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_CHAR_MATCHES(p, c) ((p)[1] == 0 && (p)[0] == (c)) #define LITTLE2_IS_NAME_CHAR_MINBPC(p) \ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) #define LITTLE2_IS_NMSTRT_CHAR_MINBPC(p) \ @@ -871,11 +869,9 @@ static const struct normal_encoding internal_little2_encoding #endif #define BIG2_BYTE_TYPE(enc, p) \ - ((p)[0] == 0 \ - ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ - : unicode_byte_type((p)[0], (p)[1])) + ((p)[0] == 0 ? SB_BYTE_TYPE(enc, p + 1) : unicode_byte_type((p)[0], (p)[1])) #define BIG2_BYTE_TO_ASCII(p) ((p)[0] == 0 ? (p)[1] : -1) -#define BIG2_CHAR_MATCHES(p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_CHAR_MATCHES(p, c) ((p)[0] == 0 && (p)[1] == (c)) #define BIG2_IS_NAME_CHAR_MINBPC(p) \ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) #define BIG2_IS_NMSTRT_CHAR_MINBPC(p) \ diff --git a/Utilities/cmexpat/lib/xmltok.h b/Utilities/cmexpat/lib/xmltok.h index 6f630c2f9..c51fce1ec 100644 --- a/Utilities/cmexpat/lib/xmltok.h +++ b/Utilities/cmexpat/lib/xmltok.h @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper Copyright (c) 2002 Fred L. Drake, Jr. Copyright (c) 2002-2005 Karl Waclawek - Copyright (c) 2016-2017 Sebastian Pipping + Copyright (c) 2016-2024 Sebastian Pipping Copyright (c) 2017 Rhodri James Licensed under the MIT license: @@ -289,7 +289,8 @@ int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING *enc, const char **encodingNamePtr, const ENCODING **namedEncodingPtr, int *standalonePtr); -int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +int XmlInitEncoding(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name); const ENCODING *XmlGetUtf8InternalEncoding(void); const ENCODING *XmlGetUtf16InternalEncoding(void); int FASTCALL XmlUtf8Encode(int charNumber, char *buf); @@ -307,7 +308,8 @@ int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc, const char **encodingNamePtr, const ENCODING **namedEncodingPtr, int *standalonePtr); -int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name); const ENCODING *XmlGetUtf8InternalEncodingNS(void); const ENCODING *XmlGetUtf16InternalEncodingNS(void); ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert, diff --git a/Utilities/cmexpat/lib/xmltok_impl.c b/Utilities/cmexpat/lib/xmltok_impl.c index 4072b0649..239a2d06c 100644 --- a/Utilities/cmexpat/lib/xmltok_impl.c +++ b/Utilities/cmexpat/lib/xmltok_impl.c @@ -16,6 +16,7 @@ Copyright (c) 2018 Anton Maklakov Copyright (c) 2019 David Loffredo Copyright (c) 2020 Boris Kolpackov + Copyright (c) 2022 Martin Ettl Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -96,7 +97,7 @@ # define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ case BT_LEAD##n: \ - if (end - ptr < n) \ + if ((end) - (ptr) < (n)) \ return XML_TOK_PARTIAL_CHAR; \ if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) { \ *nextTokPtr = ptr; \ @@ -124,7 +125,8 @@ # define PREFIX(ident) ident # endif -# define HAS_CHARS(enc, ptr, end, count) (end - ptr >= count * MINBPC(enc)) +# define HAS_CHARS(enc, ptr, end, count) \ + ((end) - (ptr) >= ((count) * MINBPC(enc))) # define HAS_CHAR(enc, ptr, end) HAS_CHARS(enc, ptr, end, 1) diff --git a/Utilities/cmexpat/lib/xmltok_impl.h b/Utilities/cmexpat/lib/xmltok_impl.h index c518aada0..3469c4ae1 100644 --- a/Utilities/cmexpat/lib/xmltok_impl.h +++ b/Utilities/cmexpat/lib/xmltok_impl.h @@ -45,7 +45,7 @@ enum { BT_LF, /* line feed = "\n" */ BT_GT, /* greater than = ">" */ BT_QUOT, /* quotation character = "\"" */ - BT_APOS, /* aposthrophe = "'" */ + BT_APOS, /* apostrophe = "'" */ BT_EQUALS, /* equal sign = "=" */ BT_QUEST, /* question mark = "?" */ BT_EXCL, /* exclamation mark = "!" */ diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index e47184b7d..feeaa3b77 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt @@ -1,12 +1,19 @@ # IF(0) # CMake handles policy settings in its own build. CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR) +if(APPLE AND CMAKE_VERSION VERSION_LESS "3.17.0") + message(WARNING "CMake>=3.17.0 required to make the generated shared library have the same Mach-O headers as autotools") +endif() + if(POLICY CMP0065) cmake_policy(SET CMP0065 NEW) #3.4 don't use `-rdynamic` with executables endif() if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) #3.12.0 `find_package()`` uses ``_ROOT`` variables. endif() +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) #3.12.0 `check_include_file()`` and friends use ``CMAKE_REQUIRED_LIBRARIES``. +endif() ENDIF() # PROJECT(libarchive C) @@ -23,6 +30,7 @@ IF(0) # CMake handles build type selection in its own build. # Release : Release build # RelWithDebInfo : Release build with Debug Info # MinSizeRel : Release Min Size build +# None : No build type IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE) ENDIF(NOT CMAKE_BUILD_TYPE) @@ -33,13 +41,15 @@ IF("${cached_type}" STREQUAL "UNINITIALIZED") SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build Type" FORCE) ENDIF("${cached_type}" STREQUAL "UNINITIALIZED") # Check the Build Type. -IF(NOT "${CMAKE_BUILD_TYPE}" - MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") +# Convert the CMAKE_BUILD_TYPE to uppercase to perform a case-insensitive comparison. +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) +IF(NOT "${CMAKE_BUILD_TYPE_UPPER}" + MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|NONE)\$") MESSAGE(FATAL_ERROR "Unknown keyword for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n" - "Acceptable keywords: Debug,Release,RelWithDebInfo,MinSizeRel") -ENDIF(NOT "${CMAKE_BUILD_TYPE}" - MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$") + "Acceptable keywords: Debug, Release, RelWithDebInfo, MinSizeRel, None") +ENDIF(NOT "${CMAKE_BUILD_TYPE_UPPER}" + MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|NONE)\$") ENDIF() # On MacOS, prefer MacPorts libraries to system libraries. @@ -81,9 +91,21 @@ SET(LIBARCHIVE_VERSION_STRING "${VERSION}") # libarchive 3.1 == interface version 13 math(EXPR INTERFACE_VERSION "13 + ${_minor}") -# Set SOVERSION == Interface version -# ?? Should there be more here ?? -SET(SOVERSION "${INTERFACE_VERSION}") +# Set SOVERSION so it matches libtool's conventions +# libtool accepts a string "current:revision:age"; in libarchive, that's set to +# - current: ${INTERFACE_VERSION} = 13 + ${_minor} +# - revision: ${_revision} +# - age: ${_minor} +# Since libtool computes SOVERSION as "current - age", it's just '13' again +math(EXPR SOVERSION "${INTERFACE_VERSION} - ${_minor}") +set(SOVERSION_FULL "${SOVERSION}.${_trimmed_minor}.${_trimmed_revision}") + +# Override CMake's default shared library versioning scheme, which uses SOVERSION and VERSION, +# to match libtool's conventions (see https://github.com/mesonbuild/meson/issues/1451) +# - compatibility version: current + 1 = ${INTERFACE_VERSION} + 1 +# - current version: ${current + 1}.${revision} +math(EXPR MACHO_COMPATIBILITY_VERSION "${INTERFACE_VERSION} + 1") +set(MACHO_CURRENT_VERSION "${MACHO_COMPATIBILITY_VERSION}.${_revision}") # Enable CMAKE_PUSH_CHECK_STATE() and CMAKE_POP_CHECK_STATE() macros # saving and restoring the state of the variables. @@ -125,7 +147,7 @@ endif () # aggressive about diagnosing build problems; this can get # relaxed somewhat in final shipping versions. IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR - CMAKE_C_COMPILER_ID MATCHES "^Clang$") + CMAKE_C_COMPILER_ID MATCHES "^Clang$" AND NOT MSVC) SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security") ################################################################# # Set compile flags for all build types. @@ -148,8 +170,6 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR # either of the following two, yet neither is supported as of 3.0.2 # - check_linker_flag - does not exist # - try_compile - does not support linker flags - # - # The CI fails with this on MacOS IF(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") # Place the functions and data into separate sections, allowing the linker # to garbage collect the unused ones. @@ -159,9 +179,12 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR # Printing the discarded section is "too much", so enable on demand. #SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -Wl,--print-gc-sections") #SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -Wl,--print-gc-sections") + ELSE() + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-dead_strip") ENDIF(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR - CMAKE_C_COMPILER_ID MATCHES "^Clang$") + CMAKE_C_COMPILER_ID MATCHES "^Clang$" AND NOT MSVC) IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") SET(CMAKE_C_COMPILER "xlc_r") SET(CMAKE_REQUIRED_FLAGS "-qflag=e:e -qformat=sec") @@ -241,6 +264,7 @@ OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON) OPTION(ENABLE_LIBXML2 "Enable the use of the system libxml2 library if found" ON) OPTION(ENABLE_EXPAT "Enable the use of the system EXPAT library if found" ON) OPTION(ENABLE_PCREPOSIX "Enable the use of the system PCREPOSIX library if found" ON) +OPTION(ENABLE_PCRE2POSIX "Enable the use of the system PCRE2POSIX library if found" ON) OPTION(ENABLE_LIBGCC "Enable the use of the system LibGCC library if found" ON) # CNG is used for encrypt/decrypt Zip archives on Windows. OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON) @@ -549,7 +573,7 @@ IF(LIBLZMA_FOUND) "#include \nint main() {return (int)lzma_version_number(); }" "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC") CHECK_C_SOURCE_COMPILES( - "#include \n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}" + "#include \n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){int ignored __attribute__((unused)); ignored = lzma_stream_encoder_mt(0, 0); return 0;}" HAVE_LZMA_STREAM_ENCODER_MT) IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC) ADD_DEFINITIONS(-DLZMA_API_STATIC) @@ -675,13 +699,13 @@ IF(ZSTD_FOUND) INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY}) SET(HAVE_LIBZSTD 1) - SET(HAVE_LIBZSTD_COMPRESSOR 1) + SET(HAVE_ZSTD_compressStream 1) IF(0) # CMake expects the zstd library to work. CMAKE_PUSH_CHECK_STATE() SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY}) SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR}) CHECK_FUNCTION_EXISTS(ZSTD_decompressStream HAVE_LIBZSTD) - CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD_COMPRESSOR) + CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_ZSTD_compressStream) # # TODO: test for static library. # @@ -766,7 +790,6 @@ LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H) LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H) LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H) LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H) -LA_CHECK_INCLUDE_FILE("sys/queue.h" HAVE_SYS_QUEUE_H) LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H) LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) @@ -1220,7 +1243,7 @@ ENDIF(ENABLE_ICONV) # # Find Libxml2 # -IF(ENABLE_LIBXML2) +IF(ENABLE_LIBXML2 AND HAVE_ICONV) FIND_PACKAGE(LibXml2) ELSE() SET(LIBXML2_FOUND FALSE) @@ -1379,6 +1402,68 @@ IF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$" MARK_AS_ADVANCED(CLEAR LIBGCC_LIBRARIES) ENDIF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$") +IF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCRE2POSIX)$") + # + # If requested, try finding library for PCRE2POSIX + # + IF(ENABLE_LIBGCC) + FIND_PACKAGE(LIBGCC) + ELSE() + MESSAGE(FATAL_ERROR "libgcc not found.") + SET(LIBGCC_FOUND FALSE) # Override cached value + ENDIF() + IF(ENABLE_PCRE2POSIX) + FIND_PACKAGE(PCRE2POSIX) + ELSE() + SET(PCRE2POSIX_FOUND FALSE) # Override cached value + ENDIF() + IF(PCRE2POSIX_FOUND) + INCLUDE_DIRECTORIES(${PCRE2_INCLUDE_DIR}) + LIST(APPEND ADDITIONAL_LIBS ${PCRE2POSIX_LIBRARIES}) + # Test if a macro is needed for the library. + TRY_MACRO_FOR_LIBRARY( + "${PCRE2_INCLUDE_DIR}" "${PCRE2POSIX_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return pcre2_regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE2_STATIC;PCRE2_STATIC") + IF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ADD_DEFINITIONS(-DPCRE2_STATIC) + ELSEIF(NOT WITHOUT_PCRE2_STATIC AND NOT PCRE2_STATIC AND PCRE2_FOUND) + # Determine if pcre2 static libraries are to be used. + LIST(APPEND ADDITIONAL_LIBS ${PCRE2_LIBRARIES}) + SET(TMP_LIBRARIES ${PCRE2POSIX_LIBRARIES} ${PCRE2_LIBRARIES}) + MESSAGE(STATUS "trying again with -lpcre2-8 included") + TRY_MACRO_FOR_LIBRARY( + "${PCRE2_INCLUDE_DIR}" "${TMP_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return pcre2_regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE2_STATIC;PCRE2_STATIC") + IF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ADD_DEFINITIONS(-DPCRE2_STATIC) + ELSEIF(NOT WITHOUT_PCRE2_STATIC AND NOT PCRE2_STATIC AND MSVC AND LIBGCC_FOUND) + # When doing a Visual Studio build using pcre2 static libraries + # built using the mingw toolchain, -lgcc is needed to resolve + # ___chkstk_ms. + MESSAGE(STATUS "Visual Studio build detected, trying again with -lgcc included") + LIST(APPEND ADDITIONAL_LIBS ${LIBGCC_LIBRARIES}) + SET(TMP_LIBRARIES ${PCRE2POSIX_LIBRARIES} ${PCRE2_LIBRARIES} ${LIBGCC_LIBRARIES}) + TRY_MACRO_FOR_LIBRARY( + "${PCRE2_INCLUDE_DIR}" "${TMP_LIBRARIES}" + COMPILES + "#include \nint main() {regex_t r;return pcre2_regcomp(&r, \"\", 0);}" + "WITHOUT_PCRE2_STATIC;PCRE2_STATIC") + IF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ADD_DEFINITIONS(-DPCRE2_STATIC) + ENDIF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ENDIF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ENDIF(NOT WITHOUT_PCRE2_STATIC AND PCRE2_STATIC) + ENDIF(PCRE2POSIX_FOUND) + MARK_AS_ADVANCED(CLEAR PCRE2_INCLUDE_DIR) + MARK_AS_ADVANCED(CLEAR PCRE2POSIX_LIBRARIES) + MARK_AS_ADVANCED(CLEAR PCRE2_LIBRARIES) + MARK_AS_ADVANCED(CLEAR LIBGCC_LIBRARIES) +ENDIF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCRE2POSIX)$") + # # Check functions # @@ -1460,6 +1545,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(strncpy_s HAVE_STRNCPY_S) CHECK_FUNCTION_EXISTS_GLIBC(strnlen HAVE_STRNLEN) CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR) CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK) +CHECK_FUNCTION_EXISTS_GLIBC(sysconf HAVE_SYSCONF) CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM) CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET) CHECK_FUNCTION_EXISTS_GLIBC(unlinkat HAVE_UNLINKAT) @@ -2077,6 +2163,11 @@ IF(APPLE) ADD_DEFINITIONS(-Wno-deprecated-declarations) ENDIF(APPLE) +OPTION(DONT_FAIL_ON_CRC_ERROR "Ignore CRC errors during parsing (For fuzzing)" OFF) +IF(DONT_FAIL_ON_CRC_ERROR) + ADD_DEFINITIONS(-DDONT_FAIL_ON_CRC_ERROR=1) +ENDIF(DONT_FAIL_ON_CRC_ERROR) + IF(ENABLE_TEST) ADD_CUSTOM_TARGET(run_all_tests) ENDIF(ENABLE_TEST) diff --git a/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake b/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake new file mode 100644 index 000000000..76bb7a455 --- /dev/null +++ b/Utilities/cmlibarchive/build/cmake/FindPCRE2POSIX.cmake @@ -0,0 +1,34 @@ +# - Find pcre2posix +# Find the native PCRE2-8 and PCRE2-POSIX include and libraries +# +# PCRE2_INCLUDE_DIR - where to find pcre2posix.h, etc. +# PCRE2POSIX_LIBRARIES - List of libraries when using libpcre2-posix. +# PCRE2_LIBRARIES - List of libraries when using libpcre2-8. +# PCRE2POSIX_FOUND - True if libpcre2-posix found. +# PCRE2_FOUND - True if libpcre2-8 found. + +IF (PCRE2_INCLUDE_DIR) + # Already in cache, be silent + SET(PCRE2_FIND_QUIETLY TRUE) +ENDIF (PCRE2_INCLUDE_DIR) + +FIND_PATH(PCRE2_INCLUDE_DIR pcre2posix.h) +FIND_LIBRARY(PCRE2POSIX_LIBRARY NAMES pcre2-posix libpcre2-posix pcre2-posix-static) +FIND_LIBRARY(PCRE2_LIBRARY NAMES pcre2-8 libpcre2-8 pcre2-8-static) + +# handle the QUIETLY and REQUIRED arguments and set PCRE2POSIX_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2POSIX DEFAULT_MSG PCRE2POSIX_LIBRARY PCRE2_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY) + +IF(PCRE2POSIX_FOUND) + SET(PCRE2POSIX_LIBRARIES ${PCRE2POSIX_LIBRARY}) + SET(HAVE_LIBPCRE2POSIX 1) + SET(HAVE_PCRE2POSIX_H 1) +ENDIF(PCRE2POSIX_FOUND) + +IF(PCRE2_FOUND) + SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY}) + SET(HAVE_LIBPCRE2 1) +ENDIF(PCRE2_FOUND) diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index 493c388f5..c3b153dc7 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in @@ -537,6 +537,12 @@ /* Define to 1 if you have the `pcreposix' library (-lpcreposix). */ #cmakedefine HAVE_LIBPCREPOSIX 1 +/* Define to 1 if you have the `pcre2-8' library (-lpcre2-8). */ +#cmakedefine HAVE_LIBPCRE2 1 + +/* Define to 1 if you have the `pcreposix' library (-lpcre2posix). */ +#cmakedefine HAVE_LIBPCRE2POSIX 1 + /* Define to 1 if you have the `xml2' library (-lxml2). */ #cmakedefine HAVE_LIBXML2 1 @@ -552,9 +558,8 @@ /* Define to 1 if you have the `zstd' library (-lzstd). */ #cmakedefine HAVE_LIBZSTD 1 -/* Define to 1 if you have the `zstd' library (-lzstd) with compression - support. */ -#cmakedefine HAVE_LIBZSTD_COMPRESSOR 1 +/* Define to 1 if you have the ZSTD_compressStream function. */ +#cmakedefine HAVE_ZSTD_compressStream 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H 1 @@ -710,6 +715,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PCREPOSIX_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PCRE2POSIX_H 1 + /* Define to 1 if you have the `pipe' function. */ #cmakedefine HAVE_PIPE 1 @@ -867,6 +875,9 @@ /* Define to 1 if you have the `symlink' function. */ #cmakedefine HAVE_SYMLINK 1 +/* Define to 1 if you have the `sysconf' function. */ +#cmakedefine HAVE_SYSCONF 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_ACL_H 1 @@ -902,9 +913,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_POLL_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_QUEUE_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_RICHACL_H 1 diff --git a/Utilities/cmlibarchive/build/utils/gen_archive_string_composition_h.sh b/Utilities/cmlibarchive/build/utils/gen_archive_string_composition_h.sh index 558e9c0c7..93012fe68 100755 --- a/Utilities/cmlibarchive/build/utils/gen_archive_string_composition_h.sh +++ b/Utilities/cmlibarchive/build/utils/gen_archive_string_composition_h.sh @@ -39,9 +39,6 @@ cat > ${outfile} <= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3007002 +#define ARCHIVE_VERSION_NUMBER 3007005 #include #include /* for wchar_t */ @@ -154,7 +152,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.7.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.7.5" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -532,6 +530,10 @@ __LA_DECL int archive_read_open_filenames(struct archive *, const char **_filenames, size_t _block_size); __LA_DECL int archive_read_open_filename_w(struct archive *, const wchar_t *_filename, size_t _block_size); +#if defined(_WIN32) && !defined(__CYGWIN__) +__LA_DECL int archive_read_open_filenames_w(struct archive *, + const wchar_t **_filenames, size_t _block_size); +#endif /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size) __LA_DEPRECATED; @@ -890,7 +892,7 @@ __LA_DECL int archive_write_set_options(struct archive *_a, const char *opts); /* - * Set a encryption passphrase. + * Set an encryption passphrase. */ __LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p); __LA_DECL int archive_write_set_passphrase_callback(struct archive *, diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c index da471a506..51a088121 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_acl.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -81,7 +80,7 @@ static int is_nfs4_flags(const char *start, const char *end, int *result); static int is_nfs4_perms(const char *start, const char *end, int *result); -static void next_field(const char **p, const char **start, +static void next_field(const char **p, size_t *l, const char **start, const char **end, char *sep); static void append_entry(char **p, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id); @@ -1627,6 +1626,13 @@ next_field_w(const wchar_t **wp, const wchar_t **start, int archive_acl_from_text_l(struct archive_acl *acl, const char *text, int want_type, struct archive_string_conv *sc) +{ + return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc); +} + +int +archive_acl_from_text_nl(struct archive_acl *acl, const char *text, + size_t length, int want_type, struct archive_string_conv *sc) { struct { const char *start; @@ -1657,7 +1663,7 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text, ret = ARCHIVE_OK; types = 0; - while (text != NULL && *text != '\0') { + while (text != NULL && length > 0 && *text != '\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. @@ -1665,7 +1671,7 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text, fields = 0; do { const char *start, *end; - next_field(&text, &start, &end, &sep); + next_field(&text, &length, &start, &end, &sep); if (fields < numfields) { field[fields].start = start; field[fields].end = end; @@ -2058,7 +2064,7 @@ is_nfs4_flags(const char *start, const char *end, int *permset) } /* - * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated + * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *p is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start @@ -2066,42 +2072,42 @@ is_nfs4_flags(const char *start, const char *end, int *permset) * whitespace. */ static void -next_field(const char **p, const char **start, +next_field(const char **p, size_t *l, const char **start, const char **end, char *sep) { /* Skip leading whitespace to find start of field. */ - while (**p == ' ' || **p == '\t' || **p == '\n') { + while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) { (*p)++; + (*l)--; } *start = *p; - /* Scan for the separator. */ - while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' && - **p != '#') { + /* Locate end of field, trim trailing whitespace if necessary */ + while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') { (*p)++; + (*l)--; } - *sep = **p; + *end = *p; - /* Locate end of field, trim trailing whitespace if necessary */ - if (*p == *start) { - *end = *p; - } else { - *end = *p - 1; - while (**end == ' ' || **end == '\t' || **end == '\n') { - (*end)--; - } - (*end)++; + /* Scan for the separator. */ + while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') { + (*p)++; + (*l)--; } + *sep = **p; /* Handle in-field comments */ if (*sep == '#') { - while (**p != '\0' && **p != ',' && **p != '\n') { + while (*l > 0 && **p != ',' && **p != '\n') { (*p)++; + (*l)--; } *sep = **p; } - /* Adjust scanner location. */ - if (**p != '\0') + /* Skip separator. */ + if (*l > 0) { (*p)++; + (*l)--; + } } diff --git a/Utilities/cmlibarchive/libarchive/archive_acl_private.h b/Utilities/cmlibarchive/libarchive/archive_acl_private.h index af108162c..2c9b50534 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_acl_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED @@ -79,5 +77,7 @@ int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */, int /* type */); int archive_acl_from_text_l(struct archive_acl *, const char * /* text */, int /* type */, struct archive_string_conv *); +int archive_acl_from_text_nl(struct archive_acl *, const char * /* text */, + size_t /* size of text */, int /* type */, struct archive_string_conv *); #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/Utilities/cmlibarchive/libarchive/archive_check_magic.c b/Utilities/cmlibarchive/libarchive/archive_check_magic.c index 1f40072f8..d12f0c496 100644 --- a/Utilities/cmlibarchive/libarchive/archive_check_magic.c +++ b/Utilities/cmlibarchive/libarchive/archive_check_magic.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_check_magic.c 201089 2009-12-28 02:20:23Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include @@ -62,7 +61,7 @@ errmsg(const char *m) } } -static __LA_DEAD void +static __LA_NORETURN void diediedie(void) { #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) @@ -99,13 +98,12 @@ archive_handle_type_name(unsigned m) } } - -static char * +static void write_all_states(char *buff, unsigned int states) { unsigned int lowbit; - buff[0] = '\0'; + *buff = '\0'; /* A trick for computing the lowest set bit. */ while ((lowbit = states & (1 + ~states)) != 0) { @@ -114,7 +112,6 @@ write_all_states(char *buff, unsigned int states) if (states != 0) strcat(buff, "/"); } - return buff; } /* @@ -160,16 +157,19 @@ __archive_check_magic(struct archive *a, unsigned int magic, if ((a->state & state) == 0) { /* If we're already FATAL, don't overwrite the error. */ - if (a->state != ARCHIVE_STATE_FATAL) + if (a->state != ARCHIVE_STATE_FATAL) { + write_all_states(states1, a->state); + write_all_states(states2, state); archive_set_error(a, -1, "INTERNAL ERROR: Function '%s' invoked with" " archive structure in state '%s'," " should be in state '%s'", function, - write_all_states(states1, a->state), - write_all_states(states2, state)); + states1, + states2); + } a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - return ARCHIVE_OK; + return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_cmdline.c b/Utilities/cmlibarchive/libarchive/archive_cmdline.c index 5c519cd17..2e5428cae 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cmdline.c +++ b/Utilities/cmlibarchive/libarchive/archive_cmdline.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_STRING_H # include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h b/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h index 57a19494f..7495dfed5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_cmdline_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_CMDLINE_PRIVATE_H diff --git a/Utilities/cmlibarchive/libarchive/archive_crc32.h b/Utilities/cmlibarchive/libarchive/archive_crc32.h index 4f1aed305..d86a507ce 100644 --- a/Utilities/cmlibarchive/libarchive/archive_crc32.h +++ b/Utilities/cmlibarchive/libarchive/archive_crc32.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $ */ #ifndef ARCHIVE_CRC32_H @@ -32,6 +30,8 @@ #error This header is only to be used internally to libarchive. #endif +#include + /* * When zlib is unavailable, we should still be able to validate * uncompressed zip archives. That requires us to be able to compute @@ -48,6 +48,9 @@ crc32(unsigned long crc, const void *_p, size_t len) static volatile int crc_tbl_inited = 0; static unsigned long crc_tbl[256]; + if (_p == NULL) + return (0); + if (!crc_tbl_inited) { for (b = 0; b < 256; ++b) { crc2 = b; diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor.c b/Utilities/cmlibarchive/libarchive/archive_cryptor.c index 112baf161..437dba06b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor.c +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor.c @@ -424,8 +424,8 @@ static int aes_ctr_release(archive_crypto_ctx *ctx) { EVP_CIPHER_CTX_free(ctx->ctx); - memset(ctx->key, 0, ctx->key_len); - memset(ctx->nonce, 0, sizeof(ctx->nonce)); + OPENSSL_cleanse(ctx->key, ctx->key_len); + OPENSSL_cleanse(ctx->nonce, sizeof(ctx->nonce)); return 0; } diff --git a/Utilities/cmlibarchive/libarchive/archive_endian.h b/Utilities/cmlibarchive/libarchive/archive_endian.h index e6d3f2ce5..83b2efa53 100644 --- a/Utilities/cmlibarchive/libarchive/archive_endian.h +++ b/Utilities/cmlibarchive/libarchive/archive_endian.h @@ -23,8 +23,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: head/lib/libarchive/archive_endian.h 201085 2009-12-28 02:17:15Z kientzle $ - * * Borrowed from FreeBSD's */ diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.3 b/Utilities/cmlibarchive/libarchive/archive_entry.3 index 2f62a4be2..0fc0f8cc2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry.3 @@ -23,8 +23,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_ENTRY 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.c b/Utilities/cmlibarchive/libarchive/archive_entry.c index ae6dc3336..f68fee65d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include @@ -119,7 +118,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41: static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); -static const char *ae_strtofflags(const char *stringp, +static const char *ae_strtofflags(const char *stringp, size_t length, unsigned long *setp, unsigned long *clrp); #ifndef HAVE_WCSCPY @@ -158,10 +157,9 @@ archive_entry_clear(struct archive_entry *entry) return (NULL); archive_mstring_clean(&entry->ae_fflags_text); archive_mstring_clean(&entry->ae_gname); - archive_mstring_clean(&entry->ae_hardlink); + archive_mstring_clean(&entry->ae_linkname); archive_mstring_clean(&entry->ae_pathname); archive_mstring_clean(&entry->ae_sourcepath); - archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); archive_acl_clear(&entry->acl); @@ -196,10 +194,9 @@ archive_entry_clone(struct archive_entry *entry) * character sets are different? XXX */ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname); - archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink); + archive_mstring_copy(&entry2->ae_linkname, &entry->ae_linkname); archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname); archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); - archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); @@ -372,6 +369,12 @@ archive_entry_filetype(struct archive_entry *entry) return (AE_IFMT & entry->acl.mode); } +int +archive_entry_filetype_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_FILETYPE); +} + void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) @@ -425,6 +428,12 @@ archive_entry_gid(struct archive_entry *entry) return (entry->ae_stat.aest_gid); } +int +archive_entry_gid_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_GID); +} + const char * archive_entry_gname(struct archive_entry *entry) { @@ -466,6 +475,15 @@ _archive_entry_gname_l(struct archive_entry *entry, return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc)); } +void +archive_entry_set_link_to_hardlink(struct archive_entry *entry) +{ + if ((entry->ae_set & AE_SET_SYMLINK) != 0) { + entry->ae_set &= ~AE_SET_SYMLINK; + } + entry->ae_set |= AE_SET_HARDLINK; +} + const char * archive_entry_hardlink(struct archive_entry *entry) { @@ -473,7 +491,7 @@ archive_entry_hardlink(struct archive_entry *entry) if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_mbs( - entry->archive, &entry->ae_hardlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -487,7 +505,7 @@ archive_entry_hardlink_utf8(struct archive_entry *entry) if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( - entry->archive, &entry->ae_hardlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -501,13 +519,19 @@ archive_entry_hardlink_w(struct archive_entry *entry) if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_wcs( - entry->archive, &entry->ae_hardlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } +int +archive_entry_hardlink_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_HARDLINK) != 0; +} + int _archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) @@ -517,7 +541,7 @@ _archive_entry_hardlink_l(struct archive_entry *entry, *len = 0; return (0); } - return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_hardlink, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_linkname, p, len, sc)); } la_int64_t @@ -631,32 +655,56 @@ archive_entry_perm(struct archive_entry *entry) return (~AE_IFMT & entry->acl.mode); } +int +archive_entry_perm_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_PERM); +} + +int +archive_entry_rdev_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_RDEV); +} + dev_t archive_entry_rdev(struct archive_entry *entry) { - if (entry->ae_stat.aest_rdev_is_broken_down) - return ae_makedev(entry->ae_stat.aest_rdevmajor, - entry->ae_stat.aest_rdevminor); - else - return (entry->ae_stat.aest_rdev); + if (archive_entry_rdev_is_set(entry)) { + if (entry->ae_stat.aest_rdev_is_broken_down) + return ae_makedev(entry->ae_stat.aest_rdevmajor, + entry->ae_stat.aest_rdevminor); + else + return (entry->ae_stat.aest_rdev); + } else { + return 0; + } } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { - if (entry->ae_stat.aest_rdev_is_broken_down) - return (entry->ae_stat.aest_rdevmajor); - else - return major(entry->ae_stat.aest_rdev); + if (archive_entry_rdev_is_set(entry)) { + if (entry->ae_stat.aest_rdev_is_broken_down) + return (entry->ae_stat.aest_rdevmajor); + else + return major(entry->ae_stat.aest_rdev); + } else { + return 0; + } } dev_t archive_entry_rdevminor(struct archive_entry *entry) { - if (entry->ae_stat.aest_rdev_is_broken_down) - return (entry->ae_stat.aest_rdevminor); - else - return minor(entry->ae_stat.aest_rdev); + if (archive_entry_rdev_is_set(entry)) { + if (entry->ae_stat.aest_rdev_is_broken_down) + return (entry->ae_stat.aest_rdevminor); + else + return minor(entry->ae_stat.aest_rdev); + } else { + return 0; + } } la_int64_t @@ -700,13 +748,22 @@ archive_entry_symlink(struct archive_entry *entry) if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_mbs( - entry->archive, &entry->ae_symlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } +void +archive_entry_set_link_to_symlink(struct archive_entry *entry) +{ + if ((entry->ae_set & AE_SET_HARDLINK) != 0) { + entry->ae_set &= ~AE_SET_HARDLINK; + } + entry->ae_set |= AE_SET_SYMLINK; +} + int archive_entry_symlink_type(struct archive_entry *entry) { @@ -720,7 +777,7 @@ archive_entry_symlink_utf8(struct archive_entry *entry) if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_utf8( - entry->archive, &entry->ae_symlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -734,7 +791,7 @@ archive_entry_symlink_w(struct archive_entry *entry) if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( - entry->archive, &entry->ae_symlink, &p) == 0) + entry->archive, &entry->ae_linkname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -750,7 +807,7 @@ _archive_entry_symlink_l(struct archive_entry *entry, *len = 0; return (0); } - return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_symlink, p, len, sc)); + return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_linkname, p, len, sc)); } la_int64_t @@ -759,6 +816,12 @@ archive_entry_uid(struct archive_entry *entry) return (entry->ae_stat.aest_uid); } +int +archive_entry_uid_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_UID); +} + const char * archive_entry_uname(struct archive_entry *entry) { @@ -827,6 +890,7 @@ archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) entry->stat_valid = 0; entry->acl.mode &= ~AE_IFMT; entry->acl.mode |= AE_IFMT & type; + entry->ae_set |= AE_SET_FILETYPE; } void @@ -840,10 +904,17 @@ archive_entry_set_fflags(struct archive_entry *entry, const char * archive_entry_copy_fflags_text(struct archive_entry *entry, - const char *flags) + const char *flags) { - archive_mstring_copy_mbs(&entry->ae_fflags_text, flags); - return (ae_strtofflags(flags, + return archive_entry_copy_fflags_text_len(entry, flags, strlen(flags)); +} + +const char * +archive_entry_copy_fflags_text_len(struct archive_entry *entry, + const char *flags, size_t flags_length) +{ + archive_mstring_copy_mbs_len(&entry->ae_fflags_text, flags, flags_length); + return (ae_strtofflags(flags, flags_length, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } @@ -859,8 +930,12 @@ archive_entry_copy_fflags_text_w(struct archive_entry *entry, void archive_entry_set_gid(struct archive_entry *entry, la_int64_t g) { + if (g < 0) { + g = 0; + } entry->stat_valid = 0; entry->ae_stat.aest_gid = g; + entry->ae_set |= AE_SET_GID; } void @@ -908,6 +983,9 @@ _archive_entry_copy_gname_l(struct archive_entry *entry, void archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino) { + if (ino < 0) { + ino = 0; + } entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; @@ -916,6 +994,9 @@ archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino) void archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino) { + if (ino < 0) { + ino = 0; + } entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; @@ -924,17 +1005,24 @@ archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino) void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { - archive_mstring_copy_mbs(&entry->ae_hardlink, target); - if (target != NULL) - entry->ae_set |= AE_SET_HARDLINK; - else + if (target == NULL) { entry->ae_set &= ~AE_SET_HARDLINK; + if (entry->ae_set & AE_SET_SYMLINK) { + return; + } + } else { + entry->ae_set |= AE_SET_HARDLINK; + } + entry->ae_set &= ~AE_SET_SYMLINK; + archive_mstring_copy_mbs(&entry->ae_linkname, target); } void archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) { - archive_mstring_copy_utf8(&entry->ae_hardlink, target); + if (target == NULL && (entry->ae_set & AE_SET_SYMLINK)) + return; + archive_mstring_copy_utf8(&entry->ae_linkname, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else @@ -944,7 +1032,9 @@ archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { - archive_mstring_copy_mbs(&entry->ae_hardlink, target); + if (target == NULL && (entry->ae_set & AE_SET_SYMLINK)) + return; + archive_mstring_copy_mbs(&entry->ae_linkname, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else @@ -954,7 +1044,9 @@ archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { - archive_mstring_copy_wcs(&entry->ae_hardlink, target); + if (target == NULL && (entry->ae_set & AE_SET_SYMLINK)) + return; + archive_mstring_copy_wcs(&entry->ae_linkname, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else @@ -964,12 +1056,14 @@ archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { + if (target == NULL && (entry->ae_set & AE_SET_SYMLINK)) + return (0); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, - &entry->ae_hardlink, target) == 0) + &entry->ae_linkname, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -982,7 +1076,9 @@ _archive_entry_copy_hardlink_l(struct archive_entry *entry, { int r; - r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, + if (target == NULL && (entry->ae_set & AE_SET_SYMLINK)) + return (0); + r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname, target, len, sc); if (target != NULL && r == 0) entry->ae_set |= AE_SET_HARDLINK; @@ -1073,51 +1169,50 @@ archive_entry_set_devminor(struct archive_entry *entry, dev_t m) void archive_entry_set_link(struct archive_entry *entry, const char *target) { - if (entry->ae_set & AE_SET_SYMLINK) - archive_mstring_copy_mbs(&entry->ae_symlink, target); - else - archive_mstring_copy_mbs(&entry->ae_hardlink, target); + archive_mstring_copy_mbs(&entry->ae_linkname, target); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } } void archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) { - if (entry->ae_set & AE_SET_SYMLINK) - archive_mstring_copy_utf8(&entry->ae_symlink, target); - else - archive_mstring_copy_utf8(&entry->ae_hardlink, target); + archive_mstring_copy_utf8(&entry->ae_linkname, target); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { - if (entry->ae_set & AE_SET_SYMLINK) - archive_mstring_copy_mbs(&entry->ae_symlink, target); - else - archive_mstring_copy_mbs(&entry->ae_hardlink, target); + archive_mstring_copy_mbs(&entry->ae_linkname, target); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { - if (entry->ae_set & AE_SET_SYMLINK) - archive_mstring_copy_wcs(&entry->ae_symlink, target); - else - archive_mstring_copy_wcs(&entry->ae_hardlink, target); + archive_mstring_copy_wcs(&entry->ae_linkname, target); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; - if (entry->ae_set & AE_SET_SYMLINK) - r = archive_mstring_update_utf8(entry->archive, - &entry->ae_symlink, target); - else - r = archive_mstring_update_utf8(entry->archive, - &entry->ae_hardlink, target); + r = archive_mstring_update_utf8(entry->archive, + &entry->ae_linkname, target); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } if (r == 0) return (1); if (errno == ENOMEM) @@ -1131,12 +1226,11 @@ _archive_entry_copy_link_l(struct archive_entry *entry, { int r; - if (entry->ae_set & AE_SET_SYMLINK) - r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, - target, len, sc); - else - r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, + r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname, target, len, sc); + if ((entry->ae_set & AE_SET_SYMLINK) == 0) { + entry->ae_set |= AE_SET_HARDLINK; + } return (r); } @@ -1145,6 +1239,7 @@ archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->acl.mode = m; + entry->ae_set |= AE_SET_PERM | AE_SET_FILETYPE; } void @@ -1220,6 +1315,7 @@ archive_entry_set_perm(struct archive_entry *entry, mode_t p) entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; entry->acl.mode |= ~AE_IFMT & p; + entry->ae_set |= AE_SET_PERM; } void @@ -1228,6 +1324,9 @@ archive_entry_set_rdev(struct archive_entry *entry, dev_t m) entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; + entry->ae_stat.aest_rdevmajor = 0; + entry->ae_stat.aest_rdevminor = 0; + entry->ae_set |= AE_SET_RDEV; } void @@ -1235,7 +1334,9 @@ archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; + entry->ae_stat.aest_rdev = 0; entry->ae_stat.aest_rdevmajor = m; + entry->ae_set |= AE_SET_RDEV; } void @@ -1243,12 +1344,17 @@ archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; + entry->ae_stat.aest_rdev = 0; entry->ae_stat.aest_rdevminor = m; + entry->ae_set |= AE_SET_RDEV; } void archive_entry_set_size(struct archive_entry *entry, la_int64_t s) { + if (s < 0) { + s = 0; + } entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; @@ -1276,11 +1382,14 @@ archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { - archive_mstring_copy_mbs(&entry->ae_symlink, linkname); - if (linkname != NULL) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return; + archive_mstring_copy_mbs(&entry->ae_linkname, linkname); + entry->ae_set &= ~AE_SET_HARDLINK; + if (linkname == NULL) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; } void @@ -1292,42 +1401,54 @@ archive_entry_set_symlink_type(struct archive_entry *entry, int type) void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { - archive_mstring_copy_utf8(&entry->ae_symlink, linkname); - if (linkname != NULL) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return; + archive_mstring_copy_utf8(&entry->ae_linkname, linkname); + entry->ae_set &= ~AE_SET_HARDLINK; + if (linkname == NULL) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { - archive_mstring_copy_mbs(&entry->ae_symlink, linkname); - if (linkname != NULL) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return; + archive_mstring_copy_mbs(&entry->ae_linkname, linkname); + entry->ae_set &= ~AE_SET_HARDLINK; + if (linkname == NULL) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { - archive_mstring_copy_wcs(&entry->ae_symlink, linkname); - if (linkname != NULL) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return; + archive_mstring_copy_wcs(&entry->ae_linkname, linkname); + entry->ae_set &= ~AE_SET_HARDLINK; + if (linkname == NULL) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; } int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { - if (linkname != NULL) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return (0); + entry->ae_set &= ~AE_SET_HARDLINK; + if (linkname == NULL) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, - &entry->ae_symlink, linkname) == 0) + &entry->ae_linkname, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); @@ -1340,20 +1461,27 @@ _archive_entry_copy_symlink_l(struct archive_entry *entry, { int r; - r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, + if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK)) + return (0); + entry->ae_set &= ~AE_SET_HARDLINK; + r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname, linkname, len, sc); - if (linkname != NULL && r == 0) - entry->ae_set |= AE_SET_SYMLINK; - else + if (linkname == NULL || r != 0) entry->ae_set &= ~AE_SET_SYMLINK; + else + entry->ae_set |= AE_SET_SYMLINK; return (r); } void archive_entry_set_uid(struct archive_entry *entry, la_int64_t u) { + if (u < 0) { + u = 0; + } entry->stat_valid = 0; entry->ae_stat.aest_uid = u; + entry->ae_set |= AE_SET_UID; } void @@ -2003,7 +2131,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) * provided string. */ static const char * -ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) +ae_strtofflags(const char *s, size_t l, unsigned long *setp, unsigned long *clrp) { const char *start, *end; const struct flag *flag; @@ -2014,15 +2142,19 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) start = s; failed = NULL; /* Find start of first token. */ - while (*start == '\t' || *start == ' ' || *start == ',') + while (l > 0 && (*start == '\t' || *start == ' ' || *start == ',')) { start++; - while (*start != '\0') { + l--; + } + while (l > 0) { size_t length; /* Locate end of token. */ end = start; - while (*end != '\0' && *end != '\t' && - *end != ' ' && *end != ',') + while (l > 0 && *end != '\t' && + *end != ' ' && *end != ',') { end++; + l--; + } length = end - start; for (flag = fileflags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); @@ -2046,8 +2178,10 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) /* Find start of next token. */ start = end; - while (*start == '\t' || *start == ' ' || *start == ',') + while (l > 0 && (*start == '\t' || *start == ' ' || *start == ',')) { start++; + l--; + } } diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index 0e4ccbb06..f89bdb9e2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h @@ -22,15 +22,13 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3007002 +#define ARCHIVE_VERSION_NUMBER 3007005 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -248,17 +246,21 @@ __LA_DECL int archive_entry_dev_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); +__LA_DECL int archive_entry_filetype_is_set(struct archive_entry *); __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); __LA_DECL la_int64_t archive_entry_gid(struct archive_entry *); +__LA_DECL int archive_entry_gid_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); __LA_DECL const char *archive_entry_gname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); +__LA_DECL void archive_entry_set_link_to_hardlink(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink(struct archive_entry *); __LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); +__LA_DECL int archive_entry_hardlink_is_set(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); __LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(struct archive_entry *); @@ -271,6 +273,8 @@ __LA_DECL const char *archive_entry_pathname(struct archive_entry *); __LA_DECL const char *archive_entry_pathname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *); +__LA_DECL int archive_entry_perm_is_set(struct archive_entry *); +__LA_DECL int archive_entry_rdev_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); @@ -279,11 +283,13 @@ __LA_DECL const wchar_t *archive_entry_sourcepath_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_size(struct archive_entry *); __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); +__LA_DECL void archive_entry_set_link_to_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); __LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); +__LA_DECL int archive_entry_uid_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); __LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); @@ -319,6 +325,8 @@ __LA_DECL void archive_entry_set_fflags(struct archive_entry *, /* Note that all recognized tokens are processed, regardless. */ __LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, const char *); +__LA_DECL const char *archive_entry_copy_fflags_text_len(struct archive_entry *, + const char *, size_t); __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t); diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 index 50dd642c2..4d0d8b50e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_acl.3 @@ -383,7 +383,7 @@ Prefix each default ACL entry with the word The mask and other ACLs don not contain a double colon. .El .Pp -The following flags are effecive only on NFSv4 ACL: +The following flags are effective only on NFSv4 ACL: .Bl -tag -offset indent -compact -width ARCHIV .It Dv ARCHIVE_ENTRY_ACL_STYLE_COMPACT Do not output minus characters for unset permissions and flags in NFSv4 ACL diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_copy_bhfi.c b/Utilities/cmlibarchive/libarchive/archive_entry_copy_bhfi.c index 77bf38e45..d5317a5ea 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_copy_bhfi.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_copy_bhfi.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive_private.h" #include "archive_entry.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_copy_stat.c b/Utilities/cmlibarchive/libarchive/archive_entry_copy_stat.c index ac83868e8..f9c2e8469 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_copy_stat.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_copy_stat.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03-07 00:52:02Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_link_resolver.c b/Utilities/cmlibarchive/libarchive/archive_entry_link_resolver.c index c7d59497a..c2fd6895f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_link_resolver.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_link_resolver.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include @@ -202,16 +201,26 @@ archive_entry_linkify(struct archive_entry_linkresolver *res, le = find_entry(res, *e); if (le != NULL) { archive_entry_unset_size(*e); +#if defined(_WIN32) && !defined(__CYGWIN__) + archive_entry_copy_hardlink_w(*e, + archive_entry_pathname_w(le->canonical)); +#else archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); +#endif } else insert_entry(res, *e); return; case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE: le = find_entry(res, *e); if (le != NULL) { +#if defined(_WIN32) && !defined(__CYGWIN__) + archive_entry_copy_hardlink_w(*e, + archive_entry_pathname_w(le->canonical)); +#else archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); +#endif } else insert_entry(res, *e); return; @@ -230,8 +239,13 @@ archive_entry_linkify(struct archive_entry_linkresolver *res, le->entry = t; /* Make the old entry into a hardlink. */ archive_entry_unset_size(*e); +#if defined(_WIN32) && !defined(__CYGWIN__) + archive_entry_copy_hardlink_w(*e, + archive_entry_pathname_w(le->canonical)); +#else archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); +#endif /* If we ran out of links, return the * final entry as well. */ if (le->links == 0) { diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h index 803c0368b..1b90c57ea 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_locale.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry_locale.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 b/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 index 0291b7b49..4bfbfc3c7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_perms.3 @@ -150,6 +150,7 @@ character strings at the same time. .Fn archive_entry_set_XXX is an alias for .Fn archive_entry_copy_XXX . +The strings are copied, and don't need to outlive the call. .Ss File Flags File flags are transparently converted between a bitmap representation and a textual format. diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_private.h b/Utilities/cmlibarchive/libarchive/archive_entry_private.h index cf4deb24e..15f2a8ee2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $ */ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED @@ -147,6 +145,11 @@ struct archive_entry { #define AE_SET_SIZE 64 #define AE_SET_INO 128 #define AE_SET_DEV 256 +#define AE_SET_PERM 512 +#define AE_SET_FILETYPE 1024 +#define AE_SET_UID 2048 +#define AE_SET_GID 4096 +#define AE_SET_RDEV 8192 /* * Use aes here so that we get transparent mbs<->wcs conversions. @@ -155,9 +158,8 @@ struct archive_entry { unsigned long ae_fflags_set; /* Bitmap fflags */ unsigned long ae_fflags_clear; struct archive_mstring ae_gname; /* Name of owning group */ - struct archive_mstring ae_hardlink; /* Name of target for hardlink */ + struct archive_mstring ae_linkname; /* Name of target for hardlink or symlink */ struct archive_mstring ae_pathname; /* Name of entry */ - struct archive_mstring ae_symlink; /* symlink contents */ struct archive_mstring ae_uname; /* Name of owner */ /* Not used within libarchive; useful for some clients. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_sparse.c b/Utilities/cmlibarchive/libarchive/archive_entry_sparse.c index 74917b37b..b81684d18 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_sparse.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_sparse.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_stat.c b/Utilities/cmlibarchive/libarchive/archive_entry_stat.c index 71a407b1f..c4906838e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_stat.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_stat.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_stat.c 201100 2009-12-28 03:05:31Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c b/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c index af2517a32..5faa2faee 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_strmode.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_time.3 b/Utilities/cmlibarchive/libarchive/archive_entry_time.3 index d0563eaef..0f1dbb025 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_time.3 +++ b/Utilities/cmlibarchive/libarchive/archive_entry_time.3 @@ -23,8 +23,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_ENTRY_TIME 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_xattr.c b/Utilities/cmlibarchive/libarchive/archive_entry_xattr.c index 5fe726b99..14848a5ab 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry_xattr.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry_xattr.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.c b/Utilities/cmlibarchive/libarchive/archive_getdate.c index fc9516e65..e9c6545d7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_getdate.c +++ b/Utilities/cmlibarchive/libarchive/archive_getdate.c @@ -30,10 +30,6 @@ #ifndef CM_GET_DATE #include "archive_platform.h" #endif -#ifdef __FreeBSD__ -#include -__FBSDID("$FreeBSD$"); -#endif #include #include diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.h b/Utilities/cmlibarchive/libarchive/archive_getdate.h index 900a8f692..cfd49ddf7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_getdate.h +++ b/Utilities/cmlibarchive/libarchive/archive_getdate.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_GETDATE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_match.c b/Utilities/cmlibarchive/libarchive/archive_match.c index 2de00458b..b9af18c71 100644 --- a/Utilities/cmlibarchive/libarchive/archive_match.c +++ b/Utilities/cmlibarchive/libarchive/archive_match.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -47,7 +46,7 @@ __FBSDID("$FreeBSD$"); struct match { struct match *next; - int matches; + int matched; struct archive_mstring pattern; }; @@ -600,17 +599,14 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, int64_t offset; int r; - ar = archive_read_new(); + ar = archive_read_new(); if (ar == NULL) { archive_set_error(&(a->archive), ENOMEM, "No memory"); return (ARCHIVE_FATAL); } r = archive_read_support_format_raw(ar); -#ifdef __clang_analyzer__ - /* Tolerate deadcode.DeadStores to avoid modifying upstream. */ - (void)r; -#endif - r = archive_read_support_format_empty(ar); + if (r == ARCHIVE_OK) + r = archive_read_support_format_empty(ar); if (r != ARCHIVE_OK) { archive_copy_error(&(a->archive), ar); archive_read_free(ar); @@ -729,12 +725,12 @@ path_excluded(struct archive_match *a, int mbs, const void *pathname) matched = NULL; for (match = a->inclusions.first; match != NULL; match = match->next){ - if (match->matches == 0 && + if (!match->matched && (r = match_path_inclusion(a, match, mbs, pathname)) != 0) { if (r < 0) return (r); a->inclusions.unmatched_count--; - match->matches++; + match->matched = 1; matched = match; } } @@ -757,11 +753,10 @@ path_excluded(struct archive_match *a, int mbs, const void *pathname) for (match = a->inclusions.first; match != NULL; match = match->next){ /* We looked at previously-unmatched inclusions already. */ - if (match->matches > 0 && + if (match->matched && (r = match_path_inclusion(a, match, mbs, pathname)) != 0) { if (r < 0) return (r); - match->matches++; return (0); } } @@ -884,7 +879,7 @@ match_list_unmatched_inclusions_next(struct archive_match *a, for (m = list->unmatched_next; m != NULL; m = m->next) { int r; - if (m->matches) + if (m->matched) continue; if (mbs) { const char *p; @@ -1321,7 +1316,7 @@ cmp_node_mbs(const struct archive_rb_node *n1, return (-1); return (strcmp(p1, p2)); } - + static int cmp_key_mbs(const struct archive_rb_node *n, const void *key) { @@ -1350,7 +1345,7 @@ cmp_node_wcs(const struct archive_rb_node *n1, return (-1); return (wcscmp(p1, p2)); } - + static int cmp_key_wcs(const struct archive_rb_node *n, const void *key) { @@ -1798,7 +1793,7 @@ match_owner_name_mbs(struct archive_match *a, struct match_list *list, < 0 && errno == ENOMEM) return (error_nomem(a)); if (p != NULL && strcmp(p, name) == 0) { - m->matches++; + m->matched = 1; return (1); } } @@ -1819,7 +1814,7 @@ match_owner_name_wcs(struct archive_match *a, struct match_list *list, < 0 && errno == ENOMEM) return (error_nomem(a)); if (p != NULL && wcscmp(p, name) == 0) { - m->matches++; + m->matched = 1; return (1); } } diff --git a/Utilities/cmlibarchive/libarchive/archive_options.c b/Utilities/cmlibarchive/libarchive/archive_options.c index 6496025a5..92647c9b4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_options.c +++ b/Utilities/cmlibarchive/libarchive/archive_options.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_options_private.h b/Utilities/cmlibarchive/libarchive/archive_options_private.h index 9a7f8080d..3e49222da 100644 --- a/Utilities/cmlibarchive/libarchive/archive_options_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_options_private.h @@ -27,8 +27,6 @@ #define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #include "archive_private.h" typedef int (*option_handler)(struct archive *a, diff --git a/Utilities/cmlibarchive/libarchive/archive_pack_dev.c b/Utilities/cmlibarchive/libarchive/archive_pack_dev.c index d95444d97..3c6209b98 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pack_dev.c +++ b/Utilities/cmlibarchive/libarchive/archive_pack_dev.c @@ -33,13 +33,6 @@ #include "archive_platform.h" -#if HAVE_SYS_CDEFS_H -#include -#endif -#if !defined(lint) -__RCSID("$NetBSD$"); -#endif /* not lint */ - #ifdef HAVE_LIMITS_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_pathmatch.c b/Utilities/cmlibarchive/libarchive/archive_pathmatch.c index 0867a268e..19e0889ff 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pathmatch.c +++ b/Utilities/cmlibarchive/libarchive/archive_pathmatch.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_pathmatch.h b/Utilities/cmlibarchive/libarchive/archive_pathmatch.h index 999514292..3f406ff74 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pathmatch.h +++ b/Utilities/cmlibarchive/libarchive/archive_pathmatch.h @@ -22,8 +22,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_PATHMATCH_H diff --git a/Utilities/cmlibarchive/libarchive/archive_platform.h b/Utilities/cmlibarchive/libarchive/archive_platform.h index 63b255ee9..7a3e1b561 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $ */ /* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ @@ -87,19 +85,6 @@ * headers as required. */ -/* Get a real definition for __FBSDID or __RCSID if we can */ -#if HAVE_SYS_CDEFS_H -#include -#endif - -/* If not, define them so as to avoid dangling semicolons. */ -#ifndef __FBSDID -#define __FBSDID(a) struct _undefined_hack -#endif -#ifndef __RCSID -#define __RCSID(a) struct _undefined_hack -#endif - /* Old glibc mbsnrtowcs fails assertions in our use case. */ #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 1 # undef HAVE_MBSNRTOWCS diff --git a/Utilities/cmlibarchive/libarchive/archive_platform_acl.h b/Utilities/cmlibarchive/libarchive/archive_platform_acl.h index 264e6de37..48556f87f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform_acl.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform_acl.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ /* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ diff --git a/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h b/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h index ad4b90ab7..2ae222f61 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform_xattr.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ /* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */ diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd8.c b/Utilities/cmlibarchive/libarchive/archive_ppmd8.c index 272ca4c4f..627c311d2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd8.c +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd8.c @@ -683,7 +683,7 @@ static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0))); } - do + while (numPs != 0) { /* Create Child */ CTX_PTR c1; /* = AllocContext(p); */ @@ -704,8 +704,7 @@ static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c SetSuccessor(ps[--numPs], REF(c1)); c = c1; } - while (numPs != 0); - + return c; } diff --git a/Utilities/cmlibarchive/libarchive/archive_private.h b/Utilities/cmlibarchive/libarchive/archive_private.h index b2a2cda25..5c5b5607a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_private.h @@ -21,16 +21,16 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $ */ #ifndef ARCHIVE_PRIVATE_H_INCLUDED #define ARCHIVE_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif +#endif #if HAVE_ICONV_H #include @@ -40,10 +40,12 @@ #include "archive_string.h" #if defined(__GNUC__) && (__GNUC__ > 2 || \ - (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) -#define __LA_DEAD __attribute__((__noreturn__)) + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) +#define __LA_NORETURN __declspec(noreturn) #else -#define __LA_DEAD +#define __LA_NORETURN #endif #if defined(__GNUC__) && (__GNUC__ > 2 || \ @@ -153,7 +155,7 @@ int __archive_check_magic(struct archive *, unsigned int magic, return ARCHIVE_FATAL; \ } while (0) -void __archive_errx(int retvalue, const char *msg) __LA_DEAD; +__LA_NORETURN void __archive_errx(int retvalue, const char *msg); void __archive_ensure_cloexec_flag(int fd); int __archive_mktemp(const char *tmpdir); diff --git a/Utilities/cmlibarchive/libarchive/archive_random.c b/Utilities/cmlibarchive/libarchive/archive_random.c index a410dc089..8c48d2d3b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_random.c +++ b/Utilities/cmlibarchive/libarchive/archive_random.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_STDLIB_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read.3 b/Utilities/cmlibarchive/libarchive/archive_read.3 index cbedd0a19..c81c98be2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read.c b/Utilities/cmlibarchive/libarchive/archive_read.c index 45a38aed0..1fa35853c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.c +++ b/Utilities/cmlibarchive/libarchive/archive_read.c @@ -32,7 +32,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -1383,7 +1382,7 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, if (filter->client_avail <= 0) { if (filter->end_of_file) { if (avail != NULL) - *avail = 0; + *avail = filter->avail; return (NULL); } bytes_read = (filter->vtable->read)(filter, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 index ca60d4fc6..c35cfeb34 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd September 14, 2014 .Dt ARCHIVE_READ_ADD_PASSPHRASE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c index f0b1ab933..c67d1df3d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c index 25dc4b2a2..59ea5c442 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -112,7 +111,7 @@ archive_read_append_filter(struct archive *_a, int code) number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); bidder = a->bidders; - for (i = 0; i < number_bidders; i++, bidder++) + for (i = 1; i < number_bidders; i++, bidder++) { if (!bidder->name || !strcmp(bidder->name, str)) break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_data.3 b/Utilities/cmlibarchive/libarchive/archive_read_data.3 index 78c0c9000..694f29264 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_data.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_data.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_DATA 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c index f16ca5c82..8fd5e1244 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 index 8b568d7b0..990c1514c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd April 3, 2017 .Dt ARCHIVE_READ_DISK 3 .Os @@ -290,11 +288,11 @@ calls. If matched based on calls to .Tn archive_match_time_excluded , or .Tn archive_match_owner_excluded , -then the callback function specified by the _excluded_func parameter will execute. This function will recieve data provided to the fourth parameter, void *_client_data. +then the callback function specified by the _excluded_func parameter will execute. This function will receive data provided to the fourth parameter, void *_client_data. .It Fn archive_read_disk_set_metadata_filter_callback Allows the caller to set a callback function during calls to .Xr archive_read_header 3 -to filter out metadata for each entry. The callback function recieves the +to filter out metadata for each entry. The callback function receives the .Tn struct archive object, void* custom filter data, and the .Tn struct archive_entry . diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c index ab0270bc2..3a4915eff 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD"); /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) @@ -521,6 +520,7 @@ setup_xattr(struct archive_read_disk *a, if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); + free(value); return (ARCHIVE_WARN); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c index ab5306dda..eea8259ff 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c @@ -29,7 +29,6 @@ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h b/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h index bc8abc15d..cf8da99a0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_private.h @@ -22,8 +22,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $ */ #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c index c7fd2471e..3512d343f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_set_standard_lookup.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c index f92a78a21..285747ea9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c @@ -25,7 +25,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #if defined(_WIN32) && !defined(__CYGWIN__) @@ -1956,6 +1955,8 @@ tree_dir_next_windows(struct tree *t, const wchar_t *pattern) t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } + /* Top stack item needs a regular visit. */ + t->current = t->stack; t->findData = &t->_findData; pattern = NULL; } else if (!FindNextFileW(t->d, &t->_findData)) { @@ -2439,6 +2440,7 @@ archive_read_disk_entry_from_file(struct archive *_a, return (ARCHIVE_OK); } + r = ARCHIVE_OK; if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) { r = setup_sparse_from_disk(a, entry, h); if (fd < 0) diff --git a/Utilities/cmlibarchive/libarchive/archive_read_extract.3 b/Utilities/cmlibarchive/libarchive/archive_read_extract.3 index 858f39742..f3feb5ad5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_extract.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_extract.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_EXTRACT 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_extract.c b/Utilities/cmlibarchive/libarchive/archive_read_extract.c index b7973fa8e..d2159c64c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_extract.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_extract.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_extract2.c b/Utilities/cmlibarchive/libarchive/archive_read_extract2.c index 4febd8ce0..e11cac159 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_extract2.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_extract2.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 index 4f5c3518a..72ff240fd 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd June 9, 2020 .Dt ARCHIVE_READ_FILTER 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_format.3 b/Utilities/cmlibarchive/libarchive/archive_read_format.3 index f3804ce37..990293c83 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_format.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_format.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_FORMAT 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_free.3 b/Utilities/cmlibarchive/libarchive/archive_read_free.3 index 8371c3a0c..7dc121fca 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_free.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_free.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_FREE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_header.3 b/Utilities/cmlibarchive/libarchive/archive_read_header.3 index 1e97f3a27..024dc41da 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_header.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_header.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_HEADER 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_new.3 b/Utilities/cmlibarchive/libarchive/archive_read_new.3 index 8bb6b848b..c2b5cddef 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_new.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_new.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_NEW 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open.3 b/Utilities/cmlibarchive/libarchive/archive_read_open.3 index f67677823..081b7114b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_open.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_READ_OPEN 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_fd.c b/Utilities/cmlibarchive/libarchive/archive_read_open_fd.c index f59cd07fe..3ee2423d3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_fd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_fd.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_fd.c 201103 2009-12-28 03:13:49Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c index 03719e8bf..dcf1d4698 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_file.c 201093 2009-12-28 02:28:44Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c index 561289b69..dd2e16022 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009-12-28 02:28:44Z kientzle $"); #ifdef HAVE_SYS_IOCTL_H #include @@ -155,55 +154,73 @@ no_memory: return (ARCHIVE_FATAL); } +/* + * This function is an implementation detail of archive_read_open_filename_w, + * which is exposed as a separate API on Windows. + */ +#if !defined(_WIN32) || defined(__CYGWIN__) +static +#endif int -archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, +archive_read_open_filenames_w(struct archive *a, const wchar_t **wfilenames, size_t block_size) { - struct read_file_data *mine = (struct read_file_data *)calloc(1, - sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); - if (!mine) + struct read_file_data *mine; + const wchar_t *wfilename = NULL; + if (wfilenames) + wfilename = *(wfilenames++); + + archive_clear_error(a); + do { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->fd = -1; - mine->block_size = block_size; + if (wfilename == NULL) + wfilename = L""; + mine = (struct read_file_data *)calloc(1, + sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); + if (mine == NULL) + goto no_memory; + mine->block_size = block_size; + mine->fd = -1; - if (wfilename == NULL || wfilename[0] == L'\0') { - mine->filename_type = FNT_STDIN; - } else { + if (wfilename == NULL || wfilename[0] == L'\0') { + mine->filename_type = FNT_STDIN; + } else { #if defined(_WIN32) && !defined(__CYGWIN__) - mine->filename_type = FNT_WCS; - wcscpy(mine->filename.w, wfilename); + mine->filename_type = FNT_WCS; + wcscpy(mine->filename.w, wfilename); #else - /* - * POSIX system does not support a wchar_t interface for - * open() system call, so we have to translate a wchar_t - * filename to multi-byte one and use it. - */ - struct archive_string fn; - - archive_string_init(&fn); - if (archive_string_append_from_wcs(&fn, wfilename, - wcslen(wfilename)) != 0) { - if (errno == ENOMEM) - archive_set_error(a, errno, - "Can't allocate memory"); - else - archive_set_error(a, EINVAL, - "Failed to convert a wide-character" - " filename to a multi-byte filename"); + /* + * POSIX system does not support a wchar_t interface for + * open() system call, so we have to translate a wchar_t + * filename to multi-byte one and use it. + */ + struct archive_string fn; + + archive_string_init(&fn); + if (archive_string_append_from_wcs(&fn, wfilename, + wcslen(wfilename)) != 0) { + if (errno == ENOMEM) + archive_set_error(a, errno, + "Can't allocate memory"); + else + archive_set_error(a, EINVAL, + "Failed to convert a wide-character" + " filename to a multi-byte filename"); + archive_string_free(&fn); + free(mine); + return (ARCHIVE_FATAL); + } + mine->filename_type = FNT_MBS; + strcpy(mine->filename.m, fn.s); archive_string_free(&fn); - free(mine); - return (ARCHIVE_FATAL); - } - mine->filename_type = FNT_MBS; - strcpy(mine->filename.m, fn.s); - archive_string_free(&fn); #endif - } - if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) - return (ARCHIVE_FATAL); + } + if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) + return (ARCHIVE_FATAL); + if (wfilenames == NULL) + break; + wfilename = *(wfilenames++); + } while (wfilename != NULL && wfilename[0] != '\0'); archive_read_set_open_callback(a, file_open); archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); @@ -212,6 +229,19 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, archive_read_set_seek_callback(a, file_seek); return (archive_read_open1(a)); +no_memory: + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); +} + +int +archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, + size_t block_size) +{ + const wchar_t *wfilenames[2]; + wfilenames[0] = wfilename; + wfilenames[1] = NULL; + return archive_read_open_filenames_w(a, wfilenames, block_size); } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c b/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c index 311be4704..a057ce643 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_open_memory.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $"); #include #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_private.h b/Utilities/cmlibarchive/libarchive/archive_read_private.h index 383405d52..0c374f487 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_read_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $ */ #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c index 796dcdcce..c74361b20 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 index 162b79da4..ef18dfaa2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd January 31, 2020 .Dt ARCHIVE_READ_OPTIONS 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_options.c b/Utilities/cmlibarchive/libarchive/archive_read_set_options.c index 2bd9b811e..c0a4b4207 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_set_options.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_set_options.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive_read_private.h" #include "archive_options_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c index edb508c1d..cb46d120d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c index 94c4af695..ce50d8cdf 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c index 9e5f6d927..a118a0374 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif @@ -192,7 +190,7 @@ bzip2_reader_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_BZIP2; self->name = "bzip2"; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_compress.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_compress.c index 05b80a576..29ae72abe 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_compress.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_compress.c @@ -64,7 +64,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -218,7 +217,7 @@ compress_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_COMPRESS; self->name = "compress (.Z)"; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); out_block = malloc(out_block_size); if (state == NULL || out_block == NULL) { free(out_block); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_grzip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_grzip.c index d4d1737cd..15b6757cb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_grzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_grzip.c @@ -25,9 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c index 976a392e3..d24386090 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_gzip.c @@ -25,9 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - - #ifdef HAVE_ERRNO_H #include #endif @@ -310,7 +307,7 @@ gzip_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_GZIP; self->name = "gzip"; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { free(out_block); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lrzip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lrzip.c index a2389894f..a562d538e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lrzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lrzip.c @@ -25,9 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c index d0fc1a83e..bccf4fb8f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif @@ -225,7 +223,7 @@ lz4_reader_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_LZ4; self->name = "lz4"; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); @@ -449,8 +447,8 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0); chsum = (chsum >> 8) & 0xff; chsum_verifier = read_buf[descriptor_bytes-1] & 0xff; - if (chsum != chsum_verifier) #ifndef DONT_FAIL_ON_CRC_ERROR + if (chsum != chsum_verifier) goto malformed_error; #endif @@ -522,8 +520,8 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) read_buf + 4, (int)compressed_size, 0); unsigned int chsum_block = archive_le32dec(read_buf + 4 + compressed_size); - if (chsum != chsum_block) #ifndef DONT_FAIL_ON_CRC_ERROR + if (chsum != chsum_block) goto malformed_error; #endif } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c index cc1572f9d..0eec7164d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lzop.c @@ -26,8 +26,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_UNISTD_H #include #endif @@ -187,7 +185,7 @@ lzop_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_LZOP; self->name = "lzop"; - state = (struct read_lzop *)calloc(sizeof(*state), 1); + state = (struct read_lzop *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for lzop decompression"); @@ -282,8 +280,8 @@ consume_header(struct archive_read_filter *self) checksum = crc32(crc32(0, NULL, 0), p, len); else checksum = adler32(adler32(0, NULL, 0), p, len); - if (archive_be32dec(p + len) != checksum) #ifndef DONT_FAIL_ON_CRC_ERROR + if (archive_be32dec(p + len) != checksum) goto corrupted; #endif __archive_read_filter_consume(self->upstream, len + 4); @@ -293,7 +291,8 @@ consume_header(struct archive_read_filter *self) if (p == NULL) goto truncated; len = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, len + 4 + 4); + __archive_read_filter_consume(self->upstream, + (int64_t)len + 4 + 4); } state->flags = flags; state->in_stream = 1; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_none.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_none.c index 95e5cfdb1..9eb8e54ae 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_none.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_none.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c index 885b2c205..0482c57c1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_WAIT_H # include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_rpm.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_rpm.c index 67a979cd7..a55bc0cf7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_rpm.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_rpm.c @@ -39,8 +39,8 @@ struct rpm { int64_t total_in; - size_t hpos; - size_t hlen; + uint64_t hpos; + uint64_t hlen; unsigned char header[16]; enum { ST_LEAD, /* Skipping 'Lead' section. */ @@ -53,7 +53,8 @@ struct rpm { } state; int first_header; }; -#define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */ +#define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */ +#define RPM_MIN_HEAD_SIZE 16 /* Minimum size of 'Head'. */ static int rpm_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -63,6 +64,8 @@ static ssize_t rpm_filter_read(struct archive_read_filter *, const void **); static int rpm_filter_close(struct archive_read_filter *); +static inline size_t rpm_limit_bytes(uint64_t, size_t); + #if ARCHIVE_VERSION_NUMBER < 4000000 /* Deprecated; remove in libarchive 4.0 */ int @@ -141,7 +144,7 @@ rpm_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_RPM; self->name = "rpm"; - rpm = (struct rpm *)calloc(sizeof(*rpm), 1); + rpm = (struct rpm *)calloc(1, sizeof(*rpm)); if (rpm == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for rpm"); @@ -155,15 +158,21 @@ rpm_bidder_init(struct archive_read_filter *self) return (ARCHIVE_OK); } +static inline size_t +rpm_limit_bytes(uint64_t bytes, size_t max) +{ + return (bytes > max ? max : (size_t)bytes); +} + static ssize_t rpm_filter_read(struct archive_read_filter *self, const void **buff) { struct rpm *rpm; const unsigned char *b; - ssize_t avail_in, total; - size_t used, n; - uint32_t section; - uint32_t bytes; + ssize_t avail_in, total, used; + size_t n; + uint64_t section; + uint64_t bytes; rpm = (struct rpm *)self->data; *buff = NULL; @@ -197,15 +206,14 @@ rpm_filter_read(struct archive_read_filter *self, const void **buff) } break; case ST_HEADER: - n = 16 - rpm->hpos; - if (n > avail_in - used) - n = avail_in - used; + n = rpm_limit_bytes(RPM_MIN_HEAD_SIZE - rpm->hpos, + avail_in - used); memcpy(rpm->header+rpm->hpos, b, n); b += n; used += n; rpm->hpos += n; - if (rpm->hpos == 16) { + if (rpm->hpos == RPM_MIN_HEAD_SIZE) { if (rpm->header[0] != 0x8e || rpm->header[1] != 0xad || rpm->header[2] != 0xe8 || @@ -219,21 +227,20 @@ rpm_filter_read(struct archive_read_filter *self, const void **buff) } rpm->state = ST_ARCHIVE; *buff = rpm->header; - total = rpm->hpos; + total = RPM_MIN_HEAD_SIZE; break; } /* Calculate 'Header' length. */ section = archive_be32dec(rpm->header+8); bytes = archive_be32dec(rpm->header+12); - rpm->hlen = 16 + section * 16 + bytes; + rpm->hlen = rpm->hpos + section * 16 + bytes; rpm->state = ST_HEADER_DATA; rpm->first_header = 0; } break; case ST_HEADER_DATA: - n = rpm->hlen - rpm->hpos; - if (n > avail_in - used) - n = avail_in - used; + n = rpm_limit_bytes(rpm->hlen - rpm->hpos, + avail_in - used); b += n; used += n; rpm->hpos += n; @@ -241,7 +248,7 @@ rpm_filter_read(struct archive_read_filter *self, const void **buff) rpm->state = ST_PADDING; break; case ST_PADDING: - while (used < (size_t)avail_in) { + while (used < avail_in) { if (*b != 0) { /* Read next header. */ rpm->state = ST_HEADER; @@ -259,7 +266,7 @@ rpm_filter_read(struct archive_read_filter *self, const void **buff) used = avail_in; break; } - if (used == (size_t)avail_in) { + if (used == avail_in) { rpm->total_in += used; __archive_read_filter_consume(self->upstream, used); b = NULL; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c index 802165cd0..de61c4a88 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -48,11 +47,13 @@ __FBSDID("$FreeBSD$"); /* Maximum lookahead during bid phase */ #define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */ +#define UUENCODE_MAX_LINE_LENGTH 34*1024 /* in bytes */ + struct uudecode { int64_t total; unsigned char *in_buff; #define IN_BUFF_SIZE (1024) - int in_cnt; + ssize_t in_cnt; size_t in_allocated; unsigned char *out_buff; #define OUT_BUFF_SIZE (64 * 1024) @@ -378,7 +379,7 @@ uudecode_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_UU; self->name = "uu"; - uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1); + uudecode = (struct uudecode *)calloc(1, sizeof(*uudecode)); out_buff = malloc(OUT_BUFF_SIZE); in_buff = malloc(IN_BUFF_SIZE); if (uudecode == NULL || out_buff == NULL || in_buff == NULL) { @@ -489,6 +490,12 @@ read_more: goto finish; } if (uudecode->in_cnt) { + if (uudecode->in_cnt > UUENCODE_MAX_LINE_LENGTH) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid format data"); + return (ARCHIVE_FATAL); + } /* * If there is remaining data which is saved by * previous calling, use it first. @@ -506,7 +513,7 @@ read_more: uudecode->in_cnt = 0; } for (;used < avail_in; d += llen, used += llen) { - int64_t l, body; + ssize_t l, body; b = d; len = get_line(b, avail_in - used, &nl); @@ -541,7 +548,7 @@ read_more: return (ARCHIVE_FATAL); if (uudecode->in_buff != b) memmove(uudecode->in_buff, b, len); - uudecode->in_cnt = (int)len; + uudecode->in_cnt = len; if (total == 0) { /* Do not return 0; it means end-of-file. * We should try to read bytes more. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c index 90b0da233..fb9317d38 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_xz.c @@ -26,8 +26,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif @@ -478,7 +476,7 @@ xz_lzma_bidder_init(struct archive_read_filter *self) struct private_data *state; int ret; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); if (state == NULL || out_block == NULL) { archive_set_error(&self->archive->archive, ENOMEM, @@ -656,13 +654,16 @@ xz_filter_read(struct archive_read_filter *self, const void **p) struct private_data *state; size_t decompressed; ssize_t avail_in; + int64_t member_in; int ret; state = (struct private_data *)self->data; + redo: /* Empty our output buffer. */ state->stream.next_out = state->out_block; state->stream.avail_out = state->out_block_size; + member_in = state->member_in; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { @@ -707,9 +708,18 @@ xz_filter_read(struct archive_read_filter *self, const void **p) decompressed = state->stream.next_out - state->out_block; state->total_out += decompressed; state->member_out += decompressed; - if (decompressed == 0) + if (decompressed == 0) { + if (member_in != state->member_in && + self->code == ARCHIVE_FILTER_LZIP && + state->eof) { + ret = lzip_tail(self); + if (ret != ARCHIVE_OK) + return (ret); + if (!state->eof) + goto redo; + } *p = NULL; - else { + } else { *p = state->out_block; if (self->code == ARCHIVE_FILTER_LZIP) { state->crc32 = lzma_crc32(state->out_block, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c index 8d20d7cc8..885c10fa0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif @@ -177,7 +175,7 @@ zstd_bidder_init(struct archive_read_filter *self) self->code = ARCHIVE_FILTER_ZSTD; self->name = "zstd"; - state = (struct private_data *)calloc(sizeof(*state), 1); + state = (struct private_data *)calloc(1, sizeof(*state)); out_block = (unsigned char *)malloc(out_block_size); dstream = ZSTD_createDStream(); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index 0bfbf1f3c..8067fba18 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -885,10 +884,9 @@ archive_read_format_7zip_read_data(struct archive_read *a, if (zip->end_of_entry) return (ARCHIVE_EOF); - const uint64_t max_read_size = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time - size_t bytes_to_read = max_read_size; + size_t bytes_to_read = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) { - bytes_to_read = zip->entry_bytes_remaining; + bytes_to_read = (size_t)zip->entry_bytes_remaining; } bytes = read_stream(a, buff, bytes_to_read, 0); if (bytes < 0) @@ -1071,8 +1069,8 @@ ppmd_read(void *p) */ ssize_t bytes_avail = 0; const uint8_t* data = __archive_read_ahead(a, - zip->ppstream.stream_in+1, &bytes_avail); - if(bytes_avail < zip->ppstream.stream_in+1) { + (size_t)zip->ppstream.stream_in+1, &bytes_avail); + if(data == NULL || bytes_avail < zip->ppstream.stream_in+1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7z file data"); @@ -1774,6 +1772,10 @@ free_decompression(struct archive_read *a, struct _7zip *zip) } zip->stream_valid = 0; } +#endif +#ifdef HAVE_ZSTD_H + if (zip->zstdstream_valid) + ZSTD_freeDStream(zip->zstd_dstream); #endif if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( @@ -2045,6 +2047,8 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) if (parse_7zip_uint64( a, &(f->coders[i].propertiesSize)) < 0) return (-1); + if (UMAX_ENTRY < f->coders[i].propertiesSize) + return (-1); if ((p = header_bytes( a, (size_t)f->coders[i].propertiesSize)) == NULL) return (-1); @@ -2314,7 +2318,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, usizes = ss->unpackSizes; for (i = 0; i < numFolders; i++) { unsigned pack; - uint64_t sum; + uint64_t size, sum; if (f[i].numUnpackStreams == 0) continue; @@ -2324,10 +2328,15 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, for (pack = 1; pack < f[i].numUnpackStreams; pack++) { if (parse_7zip_uint64(a, usizes) < 0) return (-1); + if (*usizes > UINT64_MAX - sum) + return (-1); sum += *usizes++; } } - *usizes++ = folder_uncompressed_size(&f[i]) - sum; + size = folder_uncompressed_size(&f[i]); + if (size < sum) + return (-1); + *usizes++ = size - sum; } if (type == kSize) { @@ -2421,6 +2430,8 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) packPos = si->pi.pos; for (i = 0; i < si->pi.numPackStreams; i++) { si->pi.positions[i] = packPos; + if (packPos > UINT64_MAX - si->pi.sizes[i]) + return (-1); packPos += si->pi.sizes[i]; if (packPos > zip->header_offset) return (-1); @@ -2442,6 +2453,10 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) f = si->ci.folders; for (i = 0; i < si->ci.numFolders; i++) { f[i].packIndex = packIndex; + if (f[i].numPackedStreams > UINT32_MAX) + return (-1); + if (packIndex > UINT32_MAX - (uint32_t)f[i].numPackedStreams) + return (-1); packIndex += (uint32_t)f[i].numPackedStreams; if (packIndex > si->pi.numPackStreams) return (-1); @@ -3009,7 +3024,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, /* CRC check. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) { -#ifdef DONT_FAIL_ON_CRC_ERROR +#ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Header CRC error"); return (ARCHIVE_FATAL); #endif @@ -3061,8 +3076,8 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, /* Check the EncodedHeader CRC.*/ if (r == 0 && zip->header_crc32 != next_header_crc) { - archive_set_error(&a->archive, -1, #ifndef DONT_FAIL_ON_CRC_ERROR + archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); r = -1; #endif @@ -3151,7 +3166,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, /* Copy mode. */ *buff = __archive_read_ahead(a, minimum, &bytes_avail); - if (bytes_avail <= 0) { + if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file data"); @@ -3457,7 +3472,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, /* * Skip the bytes we already has skipped in skip_stream(). */ - while (skip_bytes) { + while (1) { ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { @@ -3477,6 +3492,10 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (ARCHIVE_FATAL); } } + + if (!skip_bytes) + break; + skipped = get_uncompressed_data( a, buff, (size_t)skip_bytes, 0); if (skipped < 0) diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c index dea558bbf..3b53c9ad5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_all.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_all.c 174991 2007-12-30 04:58:22Z kientzle $"); #include "archive.h" #include "archive_private.h" @@ -68,7 +67,7 @@ archive_read_support_format_all(struct archive *a) * increase the chance that a high bid from someone else will * make it unnecessary for these to do anything at all. */ - /* These three have potentially large look-ahead. */ + /* These have potentially large look-ahead. */ archive_read_support_format_7zip(a); archive_read_support_format_cab(a); archive_read_support_format_rar(a); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c index 296b7db04..b0d1ddbc5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_ar.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_ar.c 201101 2009-12-28 03:06:27Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include @@ -271,7 +270,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, } if (ar->strtab != NULL) { archive_set_error(&a->archive, EINVAL, - "More than one string tables exist"); + "More than one string table exists"); return (ARCHIVE_FATAL); } @@ -440,9 +439,9 @@ archive_read_format_ar_read_header(struct archive_read *a, if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL) /* Broken header. */ return (ARCHIVE_EOF); - + unconsumed = 60; - + ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed); if (unconsumed) @@ -459,7 +458,6 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry, uint64_t n; /* Copy remaining header */ - archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_mtime(entry, (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L); archive_entry_set_uid(entry, @@ -468,6 +466,7 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry, (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size)); archive_entry_set_mode(entry, (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size)); + archive_entry_set_filetype(entry, AE_IFREG); n = ar_atol10(h + AR_size_offset, AR_size_size); ar->entry_offset = 0; @@ -516,7 +515,7 @@ archive_read_format_ar_read_data(struct archive_read *a, if (ar->entry_padding) { if (skipped >= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated ar archive- failed consuming padding"); + "Truncated ar archive - failed consuming padding"); } return (ARCHIVE_FATAL); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c index 89e96f1f5..7ed045f56 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c index e57b8c330..d5be04ba5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c @@ -1682,7 +1682,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) cfdata->uncompressed_size - cab->xstrm.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); - if (bytes_avail <= 0) { + if (d == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB file data"); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index 9adcfd335..69752cbb0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -835,6 +834,7 @@ static int header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { + int64_t t; const void *h; const char *header; @@ -851,7 +851,12 @@ header_afiol(struct archive_read *a, struct cpio *cpio, archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); - archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size)); + t = atol16(header + afiol_ino_offset, afiol_ino_size); + if (t < 0) { + archive_set_error(&a->archive, 0, "Nonsensical ino value"); + return (ARCHIVE_FATAL); + } + archive_entry_set_ino(entry, t); archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); @@ -864,8 +869,12 @@ header_afiol(struct archive_read *a, struct cpio *cpio, *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size); *name_pad = 0; /* No padding of filename. */ - cpio->entry_bytes_remaining = - atol16(header + afiol_filesize_offset, afiol_filesize_size); + t = atol16(header + afiol_filesize_offset, afiol_filesize_size); + if (t < 0) { + archive_set_error(&a->archive, 0, "Nonsensical file size"); + return (ARCHIVE_FATAL); + } + cpio->entry_bytes_remaining = t; archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, afiol_header_size); @@ -1003,7 +1012,7 @@ be4(const unsigned char *p) static int64_t atol8(const char *p, unsigned char_cnt) { - int64_t l; + uint64_t l; int digit; l = 0; @@ -1011,18 +1020,18 @@ atol8(const char *p, unsigned char_cnt) if (*p >= '0' && *p <= '7') digit = *p - '0'; else - return (l); + return ((int64_t)l); p++; l <<= 3; l |= digit; } - return (l); + return ((int64_t)l); } static int64_t atol16(const char *p, unsigned char_cnt) { - int64_t l; + uint64_t l; int digit; l = 0; @@ -1034,12 +1043,12 @@ atol16(const char *p, unsigned char_cnt) else if (*p >= '0' && *p <= '9') digit = *p - '0'; else - return (l); + return ((int64_t)l); p++; l <<= 4; l |= digit; } - return (l); + return ((int64_t)l); } static int diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c index 53fb6cc47..0dccd9d9b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_empty.c 191524 2009-04-26 18:24:14Z kientzle $"); #include "archive.h" #include "archive_entry.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c index a6219fa7c..137206a58 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -403,6 +402,9 @@ static int isJolietSVD(struct iso9660 *, const unsigned char *); static int isSVD(struct iso9660 *, const unsigned char *); static int isEVD(struct iso9660 *, const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); +static int isRootDirectoryRecord(const unsigned char *); +static int isValid723Integer(const unsigned char *); +static int isValid733Integer(const unsigned char *); static int next_cache_entry(struct archive_read *, struct iso9660 *, struct file_info **); static int next_entry_seek(struct archive_read *, struct iso9660 *, @@ -774,8 +776,9 @@ isSVD(struct iso9660 *iso9660, const unsigned char *h) /* Read Root Directory Record in Volume Descriptor. */ p = h + SVD_root_directory_record_offset; - if (p[DR_length_offset] != 34) + if (!isRootDirectoryRecord(p)) { return (0); + } return (48); } @@ -852,8 +855,9 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; - if (p[DR_length_offset] != 34) + if (!isRootDirectoryRecord(p)) { return (0); + } return (48); } @@ -883,21 +887,43 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); + /* Volume space size must be encoded according to 7.3.3 */ + if (!isValid733Integer(h + PVD_volume_space_size_offset)) { + return (0); + } + volume_block = archive_le32dec(h + PVD_volume_space_size_offset); + if (volume_block <= SYSTEM_AREA_BLOCK+4) + return (0); + /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); + /* Volume set size must be encoded according to 7.2.3 */ + if (!isValid723Integer(h + PVD_volume_set_size_offset)) { + return (0); + } + + /* Volume sequence number must be encoded according to 7.2.3 */ + if (!isValid723Integer(h + PVD_volume_sequence_number_offset)) { + return (0); + } + /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ + if (!isValid723Integer(h + PVD_logical_block_size_offset)) { + return (0); + } logical_block_size = archive_le16dec(h + PVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); - volume_block = archive_le32dec(h + PVD_volume_space_size_offset); - if (volume_block <= SYSTEM_AREA_BLOCK+4) + /* Path Table size must be encoded according to 7.3.3 */ + if (!isValid733Integer(h + PVD_path_table_size_offset)) { return (0); + } /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[PVD_file_structure_version_offset] != 1) @@ -936,8 +962,9 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; - if (p[DR_length_offset] != 34) + if (!isRootDirectoryRecord(p)) { return (0); + } if (!iso9660->primary.location) { iso9660->logical_block_size = logical_block_size; @@ -952,6 +979,51 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (48); } +static int +isRootDirectoryRecord(const unsigned char *p) { + int flags; + + /* ECMA119/ISO9660 requires that the root directory record be _exactly_ 34 bytes. + * However, we've seen images that have root directory records up to 68 bytes. */ + if (p[DR_length_offset] < 34 || p[DR_length_offset] > 68) { + return (0); + } + + /* The root directory location must be a 7.3.3 32-bit integer. */ + if (!isValid733Integer(p + DR_extent_offset)) { + return (0); + } + + /* The root directory size must be a 7.3.3 integer. */ + if (!isValid733Integer(p + DR_size_offset)) { + return (0); + } + + /* According to the standard, certain bits must be one or zero: + * Bit 1: must be 1 (this is a directory) + * Bit 2: must be 0 (not an associated file) + * Bit 3: must be 0 (doesn't use extended attribute record) + * Bit 7: must be 0 (final directory record for this file) + */ + flags = p[DR_flags_offset]; + if ((flags & 0x8E) != 0x02) { + return (0); + } + + /* Volume sequence number must be a 7.2.3 integer. */ + if (!isValid723Integer(p + DR_volume_sequence_number_offset)) { + return (0); + } + + /* Root directory name is a single zero byte... */ + if (p[DR_name_len_offset] != 1 || p[DR_name_offset] != 0) { + return (0); + } + + /* Nothing looked wrong, so let's accept it. */ + return (1); +} + static int read_children(struct archive_read *a, struct file_info *parent) { @@ -1213,7 +1285,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, } } if (iso9660->utf16be_previous_path == NULL) { - iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX); + iso9660->utf16be_previous_path = calloc(1, UTF16_NAME_MAX); if (iso9660->utf16be_previous_path == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); @@ -3015,6 +3087,11 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, uint64_t file_key, parent_key; int hole, parent; + /* Reserve 16 bits for possible key collisions (needed for linked items) */ + /* For ISO files with more than 65535 entries, reordering will still occur */ + key <<= 16; + key += heap->used & 0xFFFF; + #ifndef __clang_analyzer__ /* It cannot see heap->files remains populated. */ /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { @@ -3030,7 +3107,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, return (ARCHIVE_FATAL); } new_pending_files = (struct file_info **) - malloc(new_size * sizeof(new_pending_files[0])); + calloc(new_size, sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); @@ -3125,6 +3202,32 @@ toi(const void *p, int n) return (0); } +/* + * ECMA119/ISO9660 stores multi-byte integers in one of + * three different formats: + * * Little-endian (specified in section 7.2.1 and 7.3.1) + * * Big-endian (specified in section 7.2.2 and 7.3.2) + * * Both (specified in section 7.2.3 and 7.3.3) + * + * For values that follow section 7.2.3 (16-bit) or 7.3.3 (32-bit), we + * can check that the little-endian and big-endian forms agree with + * each other. This helps us avoid trying to decode files that are + * not really ISO images. + */ +static int +isValid723Integer(const unsigned char *p) { + return (p[0] == p[3] && p[1] == p[2]); +} + +static int +isValid733Integer(const unsigned char *p) +{ + return (p[0] == p[7] + && p[1] == p[6] + && p[2] == p[5] + && p[3] == p[4]); +} + static time_t isodate7(const unsigned char *v) { @@ -3162,7 +3265,7 @@ isodate17(const unsigned char *v) tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0') - 1900; - tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0'); + tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0') - 1; tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0'); tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0'); tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0'); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index 1c64b2900..e417baad1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c @@ -227,7 +227,7 @@ static int lha_read_file_header_1(struct archive_read *, struct lha *); static int lha_read_file_header_2(struct archive_read *, struct lha *); static int lha_read_file_header_3(struct archive_read *, struct lha *); static int lha_read_file_extended_header(struct archive_read *, - struct lha *, uint16_t *, int, size_t, size_t *); + struct lha *, uint16_t *, int, uint64_t, size_t *); static size_t lha_check_header_format(const void *); static int lha_skip_sfx(struct archive_read *); static time_t lha_dos_time(const unsigned char *); @@ -945,7 +945,7 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha) /* Read extended headers */ err2 = lha_read_file_extended_header(a, lha, NULL, 2, - (size_t)(lha->compsize + 2), &extdsize); + (uint64_t)(lha->compsize + 2), &extdsize); if (err2 < ARCHIVE_WARN) return (err2); if (err2 < err) @@ -1138,7 +1138,7 @@ invalid: */ static int lha_read_file_extended_header(struct archive_read *a, struct lha *lha, - uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size) + uint16_t *crc, int sizefield_length, uint64_t limitsize, size_t *total_size) { const void *h; const unsigned char *extdheader; @@ -1187,8 +1187,7 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, } /* Sanity check to the extended header size. */ - if (((uint64_t)*total_size + extdsize) > - (uint64_t)limitsize || + if (((uint64_t)*total_size + extdsize) > limitsize || extdsize <= (size_t)sizefield_length) goto invalid; @@ -1347,6 +1346,8 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, lha->compsize = archive_le64dec(extdheader); extdheader += sizeof(uint64_t); lha->origsize = archive_le64dec(extdheader); + if (lha->compsize < 0 || lha->origsize < 0) + goto invalid; } break; case EXT_CODEPAGE: @@ -1693,7 +1694,7 @@ archive_read_format_lha_cleanup(struct archive_read *a) * example. * 1. a symbolic-name is 'aaa/bb/cc' * 2. a filename is 'xxx/bbb' - * then a archived pathname is 'xxx/bbb|aaa/bb/cc' + * then an archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int lha_parse_linkname(struct archive_wstring *linkname, @@ -2385,7 +2386,7 @@ lzh_decode_blocks(struct lzh_stream *strm, int last) return (100); } - /* lzh_br_read_ahead() always try to fill the + /* lzh_br_read_ahead() always tries to fill the * cache buffer up. In specific situation we * are close to the end of the data, the cache * buffer will not be full and thus we have to diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index a5fa30e3c..6971228ee 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include @@ -417,8 +416,8 @@ next_line(struct archive_read *a, } /* - * Compare characters with a mtree keyword. - * Returns the length of a mtree keyword if matched. + * Compare characters with an mtree keyword. + * Returns the length of an mtree keyword if matched. * Returns 0 if not matched. */ static int @@ -516,7 +515,7 @@ bid_keyword(const char *p, ssize_t len) /* * Test whether there is a set of mtree keywords. - * Returns the number of keyword. + * Returns the number of keywords. * Returns -1 if we got incorrect sequence. * This function expects a set of "keyword=value". * When "unset" is specified, expects a set of "keyword". @@ -761,7 +760,7 @@ detect_form(struct archive_read *a, int *is_form_d) multiline = 1; else { /* We've got plenty of correct lines - * to assume that this file is a mtree + * to assume that this file is an mtree * format. */ if (++entry_cnt >= MAX_BID_ENTRY) break; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c index a1c549584..2c3b4ea4d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c @@ -434,7 +434,7 @@ static int make_table_recurse(struct archive_read *, struct huffman_code *, int, struct huffman_table_entry *, int, int); static int expand(struct archive_read *, int64_t *); static int copy_from_lzss_window_to_unp(struct archive_read *, const void **, - int64_t, int); + int64_t, size_t); static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *); static int parse_filter(struct archive_read *, const uint8_t *, uint16_t, uint8_t); @@ -736,7 +736,7 @@ archive_read_support_format_rar(struct archive *_a) archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar"); - rar = (struct rar *)calloc(sizeof(*rar), 1); + rar = (struct rar *)calloc(1, sizeof(*rar)); if (rar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data"); @@ -1375,6 +1375,8 @@ read_header(struct archive_read *a, struct archive_entry *entry, struct archive_string_conv *sconv, *fn_sconv; unsigned long crc32_val; int ret = (ARCHIVE_OK), ret2; + char *newptr; + size_t newsize; rar = (struct rar *)(a->format->data); @@ -1471,6 +1473,11 @@ read_header(struct archive_read *a, struct archive_entry *entry, if (rar->file_flags & FHD_LARGE) { + if (p + 8 > endp) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid header size"); + return (ARCHIVE_FATAL); + } memcpy(packed_size, file_header.pack_size, 4); memcpy(packed_size + 4, p, 4); /* High pack size */ p += 4; @@ -1516,8 +1523,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } if (rar->filename_allocated < filename_size * 2 + 2) { - char *newptr; - size_t newsize = filename_size * 2 + 2; + newsize = filename_size * 2 + 2; newptr = realloc(rar->filename, newsize); if (newptr == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -1541,7 +1547,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, fn_end = filename_size * 2; filename_size = 0; offset = (unsigned)strlen(filename) + 1; - highbyte = *(p + offset++); + highbyte = offset >= end ? 0 : *(p + offset++); flagbits = 0; flagbyte = 0; while (offset < end && filename_size < fn_end) @@ -1556,14 +1562,22 @@ read_header(struct archive_read *a, struct archive_entry *entry, switch((flagbyte >> flagbits) & 3) { case 0: + if (offset >= end) + continue; filename[filename_size++] = '\0'; filename[filename_size++] = *(p + offset++); break; case 1: + if (offset >= end) + continue; filename[filename_size++] = highbyte; filename[filename_size++] = *(p + offset++); break; case 2: + if (offset >= end - 1) { + offset = end; + continue; + } filename[filename_size++] = *(p + offset + 1); filename[filename_size++] = *(p + offset); offset += 2; @@ -1571,9 +1585,15 @@ read_header(struct archive_read *a, struct archive_entry *entry, case 3: { char extra, high; - uint8_t length = *(p + offset++); + uint8_t length; + + if (offset >= end) + continue; + length = *(p + offset++); if (length & 0x80) { + if (offset >= end) + continue; extra = *(p + offset++); high = (char)highbyte; } else @@ -1654,13 +1674,16 @@ read_header(struct archive_read *a, struct archive_entry *entry, rar->cursor++; if (rar->cursor >= rar->nodes) { - rar->nodes++; - if ((rar->dbo = - realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL) + struct data_block_offsets *newdbo; + + newsize = sizeof(*rar->dbo) * (rar->nodes + 1); + if ((newdbo = realloc(rar->dbo, newsize)) == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); return (ARCHIVE_FATAL); } + rar->dbo = newdbo; + rar->nodes++; rar->dbo[rar->cursor].header_size = header_size; rar->dbo[rar->cursor].start_offset = -1; rar->dbo[rar->cursor].end_offset = -1; @@ -1680,9 +1703,14 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } - rar->filename_save = (char*)realloc(rar->filename_save, - filename_size + 1); - memcpy(rar->filename_save, rar->filename, filename_size + 1); + newsize = filename_size + 1; + if ((newptr = realloc(rar->filename_save, newsize)) == NULL) + { + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + return (ARCHIVE_FATAL); + } + rar->filename_save = newptr; + memcpy(rar->filename_save, rar->filename, newsize); rar->filename_save_size = filename_size; /* Set info for seeking */ @@ -2062,7 +2090,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; - ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs); + ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; @@ -2178,6 +2206,19 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { start = rar->offset; end = start + rar->dictionary_size; + + /* We don't want to overflow the window and overwrite data that we write + * at 'start'. Therefore, reduce the end length by the maximum match size, + * which is 260 bytes. You can compute this maximum by looking at the + * definition of 'expand', in particular when 'symbol >= 271'. */ + /* NOTE: It's possible for 'dictionary_size' to be less than this 260 + * value, however that will only be the case when 'unp_size' is small, + * which should only happen when the entry size is small and there's no + * risk of overflowing the buffer */ + if (rar->dictionary_size > 260) { + end -= 260; + } + if (rar->filters.filterstart < end) { end = rar->filters.filterstart; } @@ -2202,7 +2243,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; - ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs); + ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; @@ -2568,8 +2609,7 @@ read_next_symbol(struct archive_read *a, struct huffman_code *code) rar_br_consume(br, code->tablesize); node = value; - while (!(code->tree[node].branches[0] == - code->tree[node].branches[1])) + while (code->tree[node].branches[0] != code->tree[node].branches[1]) { if (!rar_br_read_ahead(a, br, 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2944,7 +2984,7 @@ expand(struct archive_read *a, int64_t *end) if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0) goto bad_data; - if (lensymbol > lengthb_min) + if (lensymbol >= lengthb_min) goto bad_data; len = lengthbases[lensymbol] + 2; if (lengthbits[lensymbol] > 0) { @@ -2976,7 +3016,7 @@ expand(struct archive_read *a, int64_t *end) } else { - if (symbol-271 > lengthb_min) + if (symbol-271 >= lengthb_min) goto bad_data; len = lengthbases[symbol-271]+3; if(lengthbits[symbol-271] > 0) { @@ -2988,7 +3028,7 @@ expand(struct archive_read *a, int64_t *end) if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0) goto bad_data; - if (offssymbol > offsetb_min) + if (offssymbol >= offsetb_min) goto bad_data; offs = offsetbases[offssymbol]+1; if(offsetbits[offssymbol] > 0) @@ -3083,11 +3123,16 @@ copy_from_lzss_window(struct archive_read *a, void *buffer, static int copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, - int64_t startpos, int length) + int64_t startpos, size_t length) { int windowoffs, firstpart; struct rar *rar = (struct rar *)(a->format->data); + if (length > rar->unp_buffer_size) + { + goto fatal; + } + if (!rar->unp_buffer) { if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL) @@ -3099,17 +3144,17 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, } windowoffs = lzss_offset_for_position(&rar->lzss, startpos); - if(windowoffs + length <= lzss_size(&rar->lzss)) { + if(windowoffs + length <= (size_t)lzss_size(&rar->lzss)) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], length); - } else if (length <= lzss_size(&rar->lzss)) { + } else if (length <= (size_t)lzss_size(&rar->lzss)) { firstpart = lzss_size(&rar->lzss) - windowoffs; if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); return (ARCHIVE_FATAL); } - if (firstpart < length) { + if ((size_t)firstpart < length) { memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs], firstpart); memcpy(&rar->unp_buffer[rar->unp_offset + firstpart], @@ -3119,16 +3164,19 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, &rar->lzss.window[windowoffs], length); } } else { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Bad RAR file data"); - return (ARCHIVE_FATAL); + goto fatal; } - rar->unp_offset += length; + rar->unp_offset += (unsigned int) length; if (rar->unp_offset >= rar->unp_buffer_size) *buffer = rar->unp_buffer; else *buffer = NULL; return (ARCHIVE_OK); + +fatal: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Bad RAR file data"); + return (ARCHIVE_FATAL); } static const void * @@ -3314,7 +3362,8 @@ create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t filter->prog = prog; filter->globaldatalen = globaldatalen > PROGRAM_SYSTEM_GLOBAL_SIZE ? globaldatalen : PROGRAM_SYSTEM_GLOBAL_SIZE; filter->globaldata = calloc(1, filter->globaldatalen); - if (!filter->globaldata) { + if (!filter->globaldata) + { free(filter); return NULL; } @@ -3344,7 +3393,7 @@ run_filters(struct archive_read *a) if (filters == NULL || filter == NULL) return (0); - start = filters->filterstart; + start = (size_t)filters->filterstart; end = start + filter->blocklength; filters->filterstart = INT64_MAX; @@ -3381,10 +3430,16 @@ run_filters(struct archive_read *a) return 0; } + if (filter->blocklength > VM_MEMORY_SIZE) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); + return 0; + } + ret = copy_from_lzss_window(a, filters->vm->memory, start, filter->blocklength); if (ret != ARCHIVE_OK) return 0; - if (!execute_filter(a, filter, filters->vm, rar->offset)) + if (!execute_filter(a, filter, filters->vm, (size_t)rar->offset)) return 0; lastfilteraddress = filter->filteredblockaddress; @@ -3396,7 +3451,7 @@ run_filters(struct archive_read *a) while ((filter = filters->stack) != NULL && (int64_t)filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength) { memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength); - if (!execute_filter(a, filter, filters->vm, rar->offset)) + if (!execute_filter(a, filter, filters->vm, (size_t)rar->offset)) return 0; lastfilteraddress = filter->filteredblockaddress; @@ -3604,7 +3659,15 @@ execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm) { uint8_t lastbyte = 0; for (idx = i; idx < length; idx += numchannels) + { + /* + * The src block should not overlap with the dst block. + * If so it would be better to consider this archive is broken. + */ + if (src >= dst) + return 0; lastbyte = dst[idx] = lastbyte - *src++; + } } filter->filteredblockaddress = length; @@ -3620,7 +3683,7 @@ execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, siz uint32_t filesize = 0x1000000; uint32_t i; - if (length > PROGRAM_WORK_SIZE || length < 4) + if (length > PROGRAM_WORK_SIZE || length <= 4) return 0; for (i = 0; i <= length - 5; i++) @@ -3629,7 +3692,7 @@ execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, siz { uint32_t currpos = (uint32_t)pos + i + 1; int32_t address = (int32_t)vm_read_32(vm, i + 1); - if (address < 0 && currpos >= (uint32_t)-address) + if (address < 0 && currpos >= (~(uint32_t)address + 1)) vm_write_32(vm, i + 1, address + filesize); else if (address >= 0 && (uint32_t)address < filesize) vm_write_32(vm, i + 1, address - currpos); @@ -3652,7 +3715,7 @@ execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm) uint8_t *src, *dst; uint32_t i, j; - if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength) + if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength || blocklength < 3 || byteoffset > 2) return 0; src = &vm->memory[0]; @@ -3662,6 +3725,13 @@ execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm) uint8_t *prev = dst + i - stride; for (j = i; j < blocklength; j += 3) { + /* + * The src block should not overlap with the dst block. + * If so it would be better to consider this archive is broken. + */ + if (src >= dst) + return 0; + if (prev >= dst) { uint32_t delta1 = abs(prev[3] - prev[0]); @@ -3706,6 +3776,13 @@ execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm) memset(&state, 0, sizeof(state)); for (j = i; j < length; j += numchannels) { + /* + * The src block should not overlap with the dst block. + * If so it would be better to consider this archive is broken. + */ + if (src >= dst) + return 0; + int8_t delta = (int8_t)*src++; uint8_t predbyte, byte; int prederror; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c index 7f1efb8e7..973cd42be 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c @@ -220,7 +220,7 @@ struct comp_state { decompression. */ uint8_t* filtered_buf; /* Buffer used when applying filters. */ const uint8_t* block_buf; /* Buffer used when merging blocks. */ - size_t window_mask; /* Convenience field; window_size - 1. */ + ssize_t window_mask; /* Convenience field; window_size - 1. */ int64_t write_ptr; /* This amount of data has been unpacked in the window buffer. */ int64_t last_write_ptr; /* This amount of data has been stored in @@ -361,6 +361,7 @@ static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset); +static void clear_data_ready_stack(struct rar5* rar); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { @@ -495,12 +496,17 @@ uint8_t bf_is_table_present(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 7) & 1; } +static inline +uint8_t bf_is_last_block(const struct compressed_block_header* hdr) { + return (hdr->block_flags_u8 >> 6) & 1; +} + static inline struct rar5* get_context(struct archive_read* a) { return (struct rar5*) a->format->data; } /* Convenience functions used by filter implementations. */ -static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, +static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask, int64_t start, int64_t end) { if((start & mask) > (end & mask)) { @@ -647,6 +653,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { int ret; struct rar5* rar = get_context(a); + clear_data_ready_stack(rar); free(rar->cstate.filtered_buf); rar->cstate.filtered_buf = malloc(flt->block_length); @@ -704,7 +711,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { static void push_data(struct archive_read* a, struct rar5* rar, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { - const uint64_t wmask = rar->cstate.window_mask; + const ssize_t wmask = rar->cstate.window_mask; const ssize_t solid_write_ptr = (rar->cstate.solid_offset + rar->cstate.last_write_ptr) & wmask; @@ -1241,7 +1248,7 @@ static int process_main_locator_extra_block(struct archive_read* a, } static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, - ssize_t* extra_data_size) + int64_t* extra_data_size) { size_t hash_type = 0; size_t value_len; @@ -1291,7 +1298,7 @@ static uint64_t time_win_to_unix(uint64_t win_time) { } static int parse_htime_item(struct archive_read* a, char unix_time, - uint64_t* where, ssize_t* extra_data_size) + uint64_t* where, int64_t* extra_data_size) { if(unix_time) { uint32_t time_val; @@ -1313,7 +1320,7 @@ static int parse_htime_item(struct archive_read* a, char unix_time, } static int parse_file_extra_version(struct archive_read* a, - struct archive_entry* e, ssize_t* extra_data_size) + struct archive_entry* e, int64_t* extra_data_size) { size_t flags = 0; size_t version = 0; @@ -1367,7 +1374,7 @@ static int parse_file_extra_version(struct archive_read* a, } static int parse_file_extra_htime(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) + struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size) { char unix_time = 0; size_t flags = 0; @@ -1420,7 +1427,7 @@ static int parse_file_extra_htime(struct archive_read* a, } static int parse_file_extra_redir(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) + struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size) { uint64_t value_size = 0; size_t target_size = 0; @@ -1443,9 +1450,6 @@ static int parse_file_extra_redir(struct archive_read* a, return ARCHIVE_EOF; *extra_data_size -= target_size + 1; - if(!read_ahead(a, target_size, &p)) - return ARCHIVE_EOF; - if(target_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Link target is too long"); @@ -1458,6 +1462,9 @@ static int parse_file_extra_redir(struct archive_read* a, return ARCHIVE_FATAL; } + if(!read_ahead(a, target_size, &p)) + return ARCHIVE_EOF; + memcpy(target_utf8_buf, p, target_size); target_utf8_buf[target_size] = 0; @@ -1491,7 +1498,7 @@ static int parse_file_extra_redir(struct archive_read* a, } static int parse_file_extra_owner(struct archive_read* a, - struct archive_entry* e, ssize_t* extra_data_size) + struct archive_entry* e, int64_t* extra_data_size) { uint64_t flags = 0; uint64_t value_size = 0; @@ -1571,15 +1578,15 @@ static int parse_file_extra_owner(struct archive_read* a, } static int process_head_file_extra(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) + struct archive_entry* e, struct rar5* rar, int64_t extra_data_size) { - size_t extra_field_size; - size_t extra_field_id = 0; + uint64_t extra_field_size; + uint64_t extra_field_id = 0; int ret = ARCHIVE_FATAL; - size_t var_size; + uint64_t var_size; while(extra_data_size > 0) { - if(!read_var_sized(a, &extra_field_size, &var_size)) + if(!read_var(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; @@ -1587,7 +1594,7 @@ static int process_head_file_extra(struct archive_read* a, return ARCHIVE_EOF; } - if(!read_var_sized(a, &extra_field_id, &var_size)) + if(!read_var(a, &extra_field_id, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; @@ -1637,7 +1644,7 @@ static int process_head_file_extra(struct archive_read* a, static int process_head_file(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { - ssize_t extra_data_size = 0; + int64_t extra_data_size = 0; size_t data_size = 0; size_t file_flags = 0; size_t file_attr = 0; @@ -1677,12 +1684,12 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, } if(block_flags & HFL_EXTRA_DATA) { - size_t edata_size = 0; - if(!read_var_sized(a, &edata_size, NULL)) + uint64_t edata_size = 0; + if(!read_var(a, &edata_size, NULL)) return ARCHIVE_EOF; /* Intentional type cast from unsigned to signed. */ - extra_data_size = (ssize_t) edata_size; + extra_data_size = (int64_t) edata_size; } if(block_flags & HFL_DATA) { @@ -1775,11 +1782,18 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(rar->cstate.window_size < (ssize_t) window_size && rar->cstate.window_buf) { + /* The `data_ready` stack contains pointers to the `window_buf` or + * `filtered_buf` buffers. Since we're about to reallocate the first + * buffer, some of those pointers could become invalid. Therefore, we + * need to dispose of all entries from the stack before attempting the + * realloc. */ + clear_data_ready_stack(rar); + /* If window_buf has been allocated before, reallocate it, so * that its size will match new window_size. */ uint8_t* new_window_buf = - realloc(rar->cstate.window_buf, window_size); + realloc(rar->cstate.window_buf, (size_t) window_size); if(!new_window_buf) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, @@ -1871,9 +1885,6 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; - if(!read_ahead(a, name_size, &p)) - return ARCHIVE_EOF; - if(name_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Filename is too long"); @@ -1888,6 +1899,9 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + memcpy(name_utf8_buf, p, name_size); name_utf8_buf[name_size] = 0; if(ARCHIVE_OK != consume(a, name_size)) { @@ -1975,7 +1989,7 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { int ret; - size_t extra_data_size = 0; + uint64_t extra_data_size = 0; size_t extra_field_size = 0; size_t extra_field_id = 0; size_t archive_flags = 0; @@ -1997,7 +2011,7 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, (void) entry; if(block_flags & HFL_EXTRA_DATA) { - if(!read_var_sized(a, &extra_data_size, NULL)) + if(!read_var(a, &extra_data_size, NULL)) return ARCHIVE_EOF; } else { extra_data_size = 0; @@ -2224,10 +2238,12 @@ static int process_base_block(struct archive_read* a, /* Verify the CRC32 of the header data. */ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); if(computed_crc != hdr_crc) { +#ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return ARCHIVE_FATAL; +#endif } /* If the checksum is OK, we proceed with parsing. */ @@ -2448,6 +2464,8 @@ static void init_unpack(struct rar5* rar) { rar->cstate.filtered_buf = NULL; } + clear_data_ready_stack(rar); + rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; @@ -2989,7 +3007,7 @@ static int decode_code_length(struct archive_read* a, struct rar5* rar, static int copy_string(struct archive_read* a, int len, int dist) { struct rar5* rar = get_context(a); - const uint64_t cmask = rar->cstate.window_mask; + const ssize_t cmask = rar->cstate.window_mask; const uint64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; int i; @@ -3633,6 +3651,10 @@ static int use_data(struct rar5* rar, const void** buf, size_t* size, return ARCHIVE_RETRY; } +static void clear_data_ready_stack(struct rar5* rar) { + memset(&rar->cstate.dready, 0, sizeof(rar->cstate.dready)); +} + /* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ @@ -3768,7 +3790,12 @@ static int do_uncompress_file(struct archive_read* a) { if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) { /* The block didn't generate any new data, - * so just process a new block. */ + * so just process a new block if this one + * wasn't the last block in the file. */ + if (bf_is_last_block(&rar->last_block_hdr)) { + return ARCHIVE_EOF; + } + continue; } @@ -4186,6 +4213,7 @@ static int rar5_cleanup(struct archive_read *a) { free(rar->cstate.window_buf); free(rar->cstate.filtered_buf); + clear_data_ready_stack(rar); free(rar->vol.push_buf); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c index ec0520b60..efdbf276b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_raw.c @@ -23,7 +23,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_raw.c 201107 2009-12-28 03:25:33Z kientzle $"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c index 93c3fd585..cb103c368 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2023 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * Copyright (c) 2016 Martin Matuska * All rights reserved. @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -118,33 +117,29 @@ struct sparse_block { }; struct tar { - struct archive_string acl_text; struct archive_string entry_pathname; /* For "GNU.sparse.name" and other similar path extensions. */ struct archive_string entry_pathname_override; - struct archive_string entry_linkpath; struct archive_string entry_uname; struct archive_string entry_gname; - struct archive_string longlink; + struct archive_string entry_linkpath; struct archive_string longname; - struct archive_string pax_header; struct archive_string pax_global; struct archive_string line; - int pax_hdrcharset_binary; - int header_recursion_depth; + int pax_hdrcharset_utf8; int64_t entry_bytes_remaining; int64_t entry_offset; int64_t entry_padding; int64_t entry_bytes_unconsumed; int64_t realsize; - int sparse_allowed; struct sparse_block *sparse_list; struct sparse_block *sparse_last; int64_t sparse_offset; int64_t sparse_numbytes; int sparse_gnu_major; int sparse_gnu_minor; - char sparse_gnu_pending; + char sparse_gnu_attributes_seen; + char filetype; struct archive_string localname; struct archive_string_conv *opt_sconv; @@ -169,25 +164,26 @@ static int gnu_sparse_old_read(struct archive_read *, struct tar *, static int gnu_sparse_old_parse(struct archive_read *, struct tar *, const struct gnu_sparse *sparse, int length); static int gnu_sparse_01_parse(struct archive_read *, struct tar *, - const char *); + const char *, size_t); static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *, - size_t *); + size_t *); static int header_Solaris_ACL(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_common(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, const void *); -static int header_pax_extensions(struct archive_read *, struct tar *, +static int header_pax_extension(struct archive_read *, struct tar *, struct archive_entry *, const void *, size_t *); static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); -static int header_longlink(struct archive_read *, struct tar *, - struct archive_entry *, const void *h, size_t *); -static int header_longname(struct archive_read *, struct tar *, +static int header_gnu_longlink(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); -static int read_mac_metadata_blob(struct archive_read *, struct tar *, +static int header_gnu_longname(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); +static int is_mac_metadata_entry(struct archive_entry *entry); +static int read_mac_metadata_blob(struct archive_read *, + struct archive_entry *, size_t *); static int header_volume(struct archive_read *, struct tar *, struct archive_entry *, const void *h, size_t *); static int header_ustar(struct archive_read *, struct tar *, @@ -205,21 +201,21 @@ static int archive_read_format_tar_read_header(struct archive_read *, struct archive_entry *); static int checksum(struct archive_read *, const void *); static int pax_attribute(struct archive_read *, struct tar *, - struct archive_entry *, const char *key, const char *value, - size_t value_length); -static int pax_attribute_acl(struct archive_read *, struct tar *, - struct archive_entry *, const char *, int); -static int pax_attribute_xattr(struct archive_entry *, const char *, - const char *); -static int pax_header(struct archive_read *, struct tar *, - struct archive_entry *, struct archive_string *); -static void pax_time(const char *, int64_t *sec, long *nanos); + struct archive_entry *, const char *key, size_t key_length, + size_t value_length, size_t *unconsumed); +static int pax_attribute_LIBARCHIVE_xattr(struct archive_entry *, + const char *, size_t, const char *, size_t); +static int pax_attribute_SCHILY_acl(struct archive_read *, struct tar *, + struct archive_entry *, size_t, int); +static int pax_attribute_SUN_holesdata(struct archive_read *, struct tar *, + struct archive_entry *, const char *, size_t); +static void pax_time(const char *, size_t, int64_t *sec, long *nanos); static ssize_t readline(struct archive_read *, struct tar *, const char **, ssize_t limit, size_t *); static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h, size_t *); -static int solaris_sparse_parse(struct archive_read *, struct tar *, - struct archive_entry *, const char *); +static int read_bytes_to_string(struct archive_read *, + struct archive_string *, size_t, size_t *); static int64_t tar_atol(const char *, size_t); static int64_t tar_atol10(const char *, size_t); static int64_t tar_atol256(const char *, size_t); @@ -227,9 +223,21 @@ static int64_t tar_atol8(const char *, size_t); static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *, size_t *); static int tohex(int c); -static char *url_decode(const char *); +static char *url_decode(const char *, size_t); static void tar_flush_unconsumed(struct archive_read *, size_t *); +/* Sanity limits: These numbers should be low enough to + * prevent a maliciously-crafted archive from forcing us to + * allocate extreme amounts of memory. But of course, they + * need to be high enough for any correct value. These + * will likely need some adjustment as we get more experience. */ +static const size_t guname_limit = 65536; /* Longest uname or gname: 64kiB */ +static const size_t pathname_limit = 1048576; /* Longest path name: 1MiB */ +static const size_t sparse_map_limit = 8 * 1048576; /* Longest sparse map: 8MiB */ +static const size_t xattr_limit = 16 * 1048576; /* Longest xattr: 16MiB */ +static const size_t fflags_limit = 512; /* Longest fflags */ +static const size_t acl_limit = 131072; /* Longest textual ACL: 128kiB */ +static const int64_t entry_limit = 0xfffffffffffffffLL; /* 2^60 bytes = 1 ExbiByte */ int archive_read_support_format_gnutar(struct archive *a) @@ -284,17 +292,14 @@ archive_read_format_tar_cleanup(struct archive_read *a) tar = (struct tar *)(a->format->data); gnu_clear_sparse_list(tar); - archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_pathname); archive_string_free(&tar->entry_pathname_override); - archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->entry_uname); archive_string_free(&tar->entry_gname); + archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->line); archive_string_free(&tar->pax_global); - archive_string_free(&tar->pax_header); archive_string_free(&tar->longname); - archive_string_free(&tar->longlink); archive_string_free(&tar->localname); free(tar); (a->format->data) = NULL; @@ -506,6 +511,8 @@ archive_read_format_tar_read_header(struct archive_read *a, * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ + /* TODO: Move this into `struct tar` to avoid conflicts + * when reading multiple archives */ static int default_inode; static int default_dev; struct tar *tar; @@ -628,7 +635,8 @@ archive_read_format_tar_read_data(struct archive_read *a, return (ARCHIVE_FATAL); if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated tar archive"); + "Truncated tar archive" + " detected while reading data"); return (ARCHIVE_FATAL); } if (bytes_read > tar->entry_bytes_remaining) @@ -689,7 +697,7 @@ archive_read_format_tar_skip(struct archive_read *a) } /* - * This function recursively interprets all of the headers associated + * This function reads and interprets all of the headers associated * with a single entry. */ static int @@ -697,190 +705,259 @@ tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, size_t *unconsumed) { ssize_t bytes; - int err, eof_vol_header; + int err = ARCHIVE_OK, err2; + int eof_fatal = 0; /* EOF is okay at some points... */ const char *h; const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; - eof_vol_header = 0; - - /* Loop until we find a workable header record. */ - for (;;) { - tar_flush_unconsumed(a, unconsumed); + /* Bitmask of what header types we've seen. */ + int32_t seen_headers = 0; + static const int32_t seen_A_header = 1; + static const int32_t seen_g_header = 2; + static const int32_t seen_K_header = 4; + static const int32_t seen_L_header = 8; + static const int32_t seen_V_header = 16; + static const int32_t seen_x_header = 32; /* Also X */ + static const int32_t seen_mac_metadata = 512; + + tar->pax_hdrcharset_utf8 = 1; + tar->sparse_gnu_attributes_seen = 0; + archive_string_empty(&(tar->entry_gname)); + archive_string_empty(&(tar->entry_pathname)); + archive_string_empty(&(tar->entry_pathname_override)); + archive_string_empty(&(tar->entry_uname)); - /* Read 512-byte header record */ - h = __archive_read_ahead(a, 512, &bytes); - if (bytes < 0) - return ((int)bytes); - if (bytes == 0) { /* EOF at a block boundary. */ - /* Some writers do omit the block of nulls. */ - return (ARCHIVE_EOF); - } - if (bytes < 512) { /* Short block at EOF; this is bad. */ - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated tar archive"); - return (ARCHIVE_FATAL); - } - *unconsumed = 512; + /* Ensure format is set. */ + if (a->archive.archive_format_name == NULL) { + a->archive.archive_format = ARCHIVE_FORMAT_TAR; + a->archive.archive_format_name = "tar"; + } - /* Header is workable if it's not an end-of-archive mark. */ - if (h[0] != 0 || !archive_block_is_null(h)) - break; + /* + * TODO: Write global/default pax options into + * 'entry' struct here before overwriting with + * file-specific options. + */ - /* Ensure format is set for archives with only null blocks. */ - if (a->archive.archive_format_name == NULL) { - a->archive.archive_format = ARCHIVE_FORMAT_TAR; - a->archive.archive_format_name = "tar"; - } + /* Loop over all the headers needed for the next entry */ + for (;;) { - if (!tar->read_concatenated_archives) { - /* Try to consume a second all-null record, as well. */ + /* Find the next valid header record. */ + while (1) { tar_flush_unconsumed(a, unconsumed); - h = __archive_read_ahead(a, 512, NULL); - if (h != NULL && h[0] == 0 && archive_block_is_null(h)) - __archive_read_consume(a, 512); - archive_clear_error(&a->archive); - return (ARCHIVE_EOF); - } - /* - * We're reading concatenated archives, ignore this block and - * loop to get the next. - */ - } + /* Read 512-byte header record */ + h = __archive_read_ahead(a, 512, &bytes); + if (bytes < 0) + return ((int)bytes); + if (bytes == 0) { /* EOF at a block boundary. */ + if (eof_fatal) { + /* We've read a special header already; + * if there's no regular header, then this is + * a premature EOF. */ + archive_set_error(&a->archive, EINVAL, + "Damaged tar archive"); + return (ARCHIVE_FATAL); + } else { + return (ARCHIVE_EOF); + } + } + if (bytes < 512) { /* Short block at EOF; this is bad. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive" + " detected while reading next heaader"); + return (ARCHIVE_FATAL); + } + *unconsumed += 512; - /* - * Note: If the checksum fails and we return ARCHIVE_RETRY, - * then the client is likely to just retry. This is a very - * crude way to search for the next valid header! - * - * TODO: Improve this by implementing a real header scan. - */ - if (!checksum(a, h)) { - tar_flush_unconsumed(a, unconsumed); - archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); - return (ARCHIVE_RETRY); /* Retryable: Invalid header */ - } + if (h[0] == 0 && archive_block_is_null(h)) { + /* We found a NULL block which indicates end-of-archive */ - if (++tar->header_recursion_depth > 32) { - tar_flush_unconsumed(a, unconsumed); - archive_set_error(&a->archive, EINVAL, "Too many special headers"); - return (ARCHIVE_WARN); - } + if (tar->read_concatenated_archives) { + /* We're ignoring NULL blocks, so keep going. */ + continue; + } - /* Determine the format variant. */ - header = (const struct archive_entry_header_ustar *)h; + /* Try to consume a second all-null record, as well. */ + /* If we can't, that's okay. */ + tar_flush_unconsumed(a, unconsumed); + h = __archive_read_ahead(a, 512, NULL); + if (h != NULL && h[0] == 0 && archive_block_is_null(h)) + __archive_read_consume(a, 512); - switch(header->typeflag[0]) { - case 'A': /* Solaris tar ACL */ - a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive.archive_format_name = "Solaris tar"; - err = header_Solaris_ACL(a, tar, entry, h, unconsumed); - break; - case 'g': /* POSIX-standard 'g' header. */ - a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive.archive_format_name = "POSIX pax interchange format"; - err = header_pax_global(a, tar, entry, h, unconsumed); - if (err == ARCHIVE_EOF) - return (err); - break; - case 'K': /* Long link name (GNU tar, others) */ - err = header_longlink(a, tar, entry, h, unconsumed); - break; - case 'L': /* Long filename (GNU tar, others) */ - err = header_longname(a, tar, entry, h, unconsumed); - break; - case 'V': /* GNU volume header */ - err = header_volume(a, tar, entry, h, unconsumed); - if (err == ARCHIVE_EOF) - eof_vol_header = 1; - break; - case 'X': /* Used by SUN tar; same as 'x'. */ - a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive.archive_format_name = - "POSIX pax interchange format (Sun variant)"; - err = header_pax_extensions(a, tar, entry, h, unconsumed); - break; - case 'x': /* POSIX-standard 'x' header. */ - a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive.archive_format_name = "POSIX pax interchange format"; - err = header_pax_extensions(a, tar, entry, h, unconsumed); - break; - default: - gnuheader = (const struct archive_entry_header_gnutar *)h; - if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) { - a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; - a->archive.archive_format_name = "GNU tar format"; - err = header_gnutar(a, tar, entry, h, unconsumed); - } else if (memcmp(header->magic, "ustar", 5) == 0) { - if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { - a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; - a->archive.archive_format_name = "POSIX ustar format"; + archive_clear_error(&a->archive); + return (ARCHIVE_EOF); } - err = header_ustar(a, tar, entry, h); - } else { - a->archive.archive_format = ARCHIVE_FORMAT_TAR; - a->archive.archive_format_name = "tar (non-POSIX)"; - err = header_old_tar(a, tar, entry, h); - } - } - if (err == ARCHIVE_FATAL) - return (err); - - tar_flush_unconsumed(a, unconsumed); - h = NULL; - header = NULL; + /* This is NOT a null block, so it must be a valid header. */ + if (!checksum(a, h)) { + tar_flush_unconsumed(a, unconsumed); + archive_set_error(&a->archive, EINVAL, "Damaged tar archive"); + /* If we've read some critical information (pax headers, etc) + * and _then_ see a bad header, we can't really recover. */ + if (eof_fatal) { + return (ARCHIVE_FATAL); + } else { + return (ARCHIVE_RETRY); + } + } + break; + } - --tar->header_recursion_depth; - /* Yuck. Apple's design here ends up storing long pathname - * extensions for both the AppleDouble extension entry and the - * regular entry. - */ - if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) && - tar->header_recursion_depth == 0 && - tar->process_mac_extensions) { - int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed); - if (err2 < err) - err = err2; - } - - /* We return warnings or success as-is. Anything else is fatal. */ - if (err == ARCHIVE_WARN || err == ARCHIVE_OK) { - if (tar->sparse_gnu_pending) { - if (tar->sparse_gnu_major == 1 && - tar->sparse_gnu_minor == 0) { - ssize_t bytes_read; - - tar->sparse_gnu_pending = 0; - /* Read initial sparse map. */ - bytes_read = gnu_sparse_10_read(a, tar, unconsumed); - if (bytes_read < 0) - return ((int)bytes_read); - tar->entry_bytes_remaining -= bytes_read; + /* Determine the format variant. */ + header = (const struct archive_entry_header_ustar *)h; + switch(header->typeflag[0]) { + case 'A': /* Solaris tar ACL */ + if (seen_headers & seen_A_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_A_header; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "Solaris tar"; + err2 = header_Solaris_ACL(a, tar, entry, h, unconsumed); + break; + case 'g': /* POSIX-standard 'g' header. */ + if (seen_headers & seen_g_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_g_header; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "POSIX pax interchange format"; + err2 = header_pax_global(a, tar, entry, h, unconsumed); + break; + case 'K': /* Long link name (GNU tar, others) */ + if (seen_headers & seen_K_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_K_header; + err2 = header_gnu_longlink(a, tar, entry, h, unconsumed); + break; + case 'L': /* Long filename (GNU tar, others) */ + if (seen_headers & seen_L_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_L_header; + err2 = header_gnu_longname(a, tar, entry, h, unconsumed); + break; + case 'V': /* GNU volume header */ + if (seen_headers & seen_V_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_V_header; + err2 = header_volume(a, tar, entry, h, unconsumed); + break; + case 'X': /* Used by SUN tar; same as 'x'. */ + if (seen_headers & seen_x_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_x_header; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = + "POSIX pax interchange format (Sun variant)"; + err2 = header_pax_extension(a, tar, entry, h, unconsumed); + break; + case 'x': /* POSIX-standard 'x' header. */ + if (seen_headers & seen_x_header) { + return (ARCHIVE_FATAL); + } + seen_headers |= seen_x_header; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "POSIX pax interchange format"; + err2 = header_pax_extension(a, tar, entry, h, unconsumed); + break; + default: /* Regular header: Legacy tar, GNU tar, or ustar */ + gnuheader = (const struct archive_entry_header_gnutar *)h; + if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) { + a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; + a->archive.archive_format_name = "GNU tar format"; + err2 = header_gnutar(a, tar, entry, h, unconsumed); + } else if (memcmp(header->magic, "ustar", 5) == 0) { + if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) { + a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; + a->archive.archive_format_name = "POSIX ustar format"; + } + err2 = header_ustar(a, tar, entry, h); } else { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Unrecognized GNU sparse file format"); - return (ARCHIVE_WARN); + a->archive.archive_format = ARCHIVE_FORMAT_TAR; + a->archive.archive_format_name = "tar (non-POSIX)"; + err2 = header_old_tar(a, tar, entry, h); + } + err = err_combine(err, err2); + /* We return warnings or success as-is. Anything else is fatal. */ + if (err < ARCHIVE_WARN) { + return (ARCHIVE_FATAL); + } + /* Filename of the form `._filename` is an AppleDouble + * extension entry. The body is the macOS metadata blob; + * this is followed by another entry with the actual + * regular file data. + * This design has two drawbacks: + * = it's brittle; you might just have a file with such a name + * = it duplicates any long pathname extensions + * + * TODO: This probably shouldn't be here at all. Consider + * just returning the contents as a regular entry here and + * then dealing with it when we write data to disk. + */ + if (tar->process_mac_extensions + && ((seen_headers & seen_mac_metadata) == 0) + && is_mac_metadata_entry(entry)) { + err2 = read_mac_metadata_blob(a, entry, unconsumed); + if (err2 < ARCHIVE_WARN) { + return (ARCHIVE_FATAL); + } + err = err_combine(err, err2); + /* Note: Other headers can appear again. */ + seen_headers = seen_mac_metadata; + break; + } + + /* Reconcile GNU sparse attributes */ + if (tar->sparse_gnu_attributes_seen) { + /* Only 'S' (GNU sparse) and ustar '0' regular files can be sparse */ + if (tar->filetype != 'S' && tar->filetype != '0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Non-regular file cannot be sparse"); + return (ARCHIVE_WARN); + } else if (tar->sparse_gnu_major == 0 && + tar->sparse_gnu_minor == 0) { + /* Sparse map already parsed from 'x' header */ + } else if (tar->sparse_gnu_major == 0 && + tar->sparse_gnu_minor == 1) { + /* Sparse map already parsed from 'x' header */ + } else if (tar->sparse_gnu_major == 1 && + tar->sparse_gnu_minor == 0) { + /* Sparse map is prepended to file contents */ + ssize_t bytes_read; + bytes_read = gnu_sparse_10_read(a, tar, unconsumed); + if (bytes_read < 0) + return ((int)bytes_read); + tar->entry_bytes_remaining -= bytes_read; + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Unrecognized GNU sparse file format"); + return (ARCHIVE_WARN); + } } - tar->sparse_gnu_pending = 0; + return (err); } - return (err); - } - if (err == ARCHIVE_EOF) { - if (!eof_vol_header) { - /* EOF when recursively reading a header is bad. */ - archive_set_error(&a->archive, EINVAL, - "Damaged tar archive"); - } else { - /* If we encounter just a GNU volume header treat - * this situation as an empty archive */ - return (ARCHIVE_EOF); + + /* We're between headers ... */ + err = err_combine(err, err2); + if (err == ARCHIVE_FATAL) + return (err); + + /* The GNU volume header and the pax `g` global header + * are both allowed to be the only header in an + * archive. If we've seen any other header, a + * following EOF is fatal. */ + if ((seen_headers & ~seen_V_header & ~seen_g_header) != 0) { + eof_fatal = 1; } } - return (ARCHIVE_FATAL); } /* @@ -960,6 +1037,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { const struct archive_entry_header_ustar *header; + struct archive_string acl_text; size_t size; int err, acl_type; int64_t type; @@ -971,27 +1049,24 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, */ header = (const struct archive_entry_header_ustar *)h; size = (size_t)tar_atol(header->size, sizeof(header->size)); - err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed); + archive_string_init(&acl_text); + err = read_body_to_string(a, tar, &acl_text, h, unconsumed); if (err != ARCHIVE_OK) return (err); - /* Recursively read next header */ - err = tar_read_header(a, tar, entry, unconsumed); - if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) - return (err); - /* TODO: Examine the first characters to see if this * is an AIX ACL descriptor. We'll likely never support * them, but it would be polite to recognize and warn when * we do see them. */ /* Leading octal number indicates ACL type and number of entries. */ - p = acl = tar->acl_text.s; + p = acl = acl_text.s; type = 0; while (*p != '\0' && p < acl + size) { if (*p < '0' || *p > '7') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (invalid digit)"); + archive_string_free(&acl_text); return(ARCHIVE_WARN); } type <<= 3; @@ -999,6 +1074,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, if (type > 077777777) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (count too large)"); + archive_string_free(&acl_text); return (ARCHIVE_WARN); } p++; @@ -1016,6 +1092,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unsupported type %o)", (int)type); + archive_string_free(&acl_text); return (ARCHIVE_WARN); } p++; @@ -1023,6 +1100,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, if (p >= acl + size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (body overflow)"); + archive_string_free(&acl_text); return(ARCHIVE_WARN); } @@ -1036,12 +1114,17 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, if (tar->sconv_acl == NULL) { tar->sconv_acl = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); - if (tar->sconv_acl == NULL) + if (tar->sconv_acl == NULL) { + archive_string_free(&acl_text); return (ARCHIVE_FATAL); + } } archive_strncpy(&(tar->localname), acl, p - acl); err = archive_acl_from_text_l(archive_entry_acl(entry), tar->localname.s, acl_type, tar->sconv_acl); + /* Workaround: Force perm_is_set() to be correct */ + /* If this bit were stored in the ACL, this wouldn't be needed */ + archive_entry_set_perm(entry, archive_entry_perm(entry)); if (err != ARCHIVE_OK) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, @@ -1050,6 +1133,7 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed Solaris ACL attribute (unparsable)"); } + archive_string_free(&acl_text); return (err); } @@ -1057,20 +1141,17 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar, * Interpret 'K' long linkname header. */ static int -header_longlink(struct archive_read *a, struct tar *tar, +header_gnu_longlink(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; - err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed); - if (err != ARCHIVE_OK) - return (err); - err = tar_read_header(a, tar, entry, unconsumed); - if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) - return (err); - /* Set symlink if symlink already set, else hardlink. */ - archive_entry_copy_link(entry, tar->longlink.s); - return (ARCHIVE_OK); + struct archive_string linkpath; + archive_string_init(&linkpath); + err = read_body_to_string(a, tar, &linkpath, h, unconsumed); + archive_entry_set_link(entry, linkpath.s); + archive_string_free(&linkpath); + return (err); } static int @@ -1092,7 +1173,7 @@ set_conversion_failed_error(struct archive_read *a, * Interpret 'L' long filename header. */ static int -header_longname(struct archive_read *a, struct tar *tar, +header_gnu_longname(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { int err; @@ -1100,17 +1181,12 @@ header_longname(struct archive_read *a, struct tar *tar, err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed); if (err != ARCHIVE_OK) return (err); - /* Read and parse "real" header, then override name. */ - err = tar_read_header(a, tar, entry, unconsumed); - if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) - return (err); if (archive_entry_copy_pathname_l(entry, tar->longname.s, archive_strlen(&(tar->longname)), tar->sconv) != 0) err = set_conversion_failed_error(a, tar->sconv, "Pathname"); return (err); } - /* * Interpret 'V' GNU tar volume header. */ @@ -1118,32 +1194,33 @@ static int header_volume(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { - (void)h; + const struct archive_entry_header_ustar *header; + int64_t size, to_consume; + + (void)a; /* UNUSED */ + (void)tar; /* UNUSED */ + (void)entry; /* UNUSED */ - /* Just skip this and read the next header. */ - return (tar_read_header(a, tar, entry, unconsumed)); + header = (const struct archive_entry_header_ustar *)h; + size = tar_atol(header->size, sizeof(header->size)); + if (size > (int64_t)pathname_limit) { + return (ARCHIVE_FATAL); + } + to_consume = ((size + 511) & ~511); + *unconsumed += to_consume; + return (ARCHIVE_OK); } /* - * Read body of an archive entry into an archive_string object. + * Read the next `size` bytes into the provided string. + * Null-terminate the string. */ static int -read_body_to_string(struct archive_read *a, struct tar *tar, - struct archive_string *as, const void *h, size_t *unconsumed) -{ - int64_t size; - const struct archive_entry_header_ustar *header; +read_bytes_to_string(struct archive_read *a, + struct archive_string *as, size_t size, + size_t *unconsumed) { const void *src; - (void)tar; /* UNUSED */ - header = (const struct archive_entry_header_ustar *)h; - size = tar_atol(header->size, sizeof(header->size)); - if ((size > 1048576) || (size < 0)) { - archive_set_error(&a->archive, EINVAL, - "Special header too large"); - return (ARCHIVE_FATAL); - } - /* Fail if we can't make our buffer big enough. */ if (archive_string_ensure(as, (size_t)size+1) == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -1154,18 +1231,54 @@ read_body_to_string(struct archive_read *a, struct tar *tar, tar_flush_unconsumed(a, unconsumed); /* Read the body into the string. */ - *unconsumed = (size_t)((size + 511) & ~ 511); - src = __archive_read_ahead(a, *unconsumed, NULL); + src = __archive_read_ahead(a, size, NULL); if (src == NULL) { + archive_set_error(&a->archive, EINVAL, + "Truncated archive" + " detected while reading metadata"); *unconsumed = 0; return (ARCHIVE_FATAL); } memcpy(as->s, src, (size_t)size); as->s[size] = '\0'; as->length = (size_t)size; + *unconsumed += size; return (ARCHIVE_OK); } +/* + * Read body of an archive entry into an archive_string object. + */ +static int +read_body_to_string(struct archive_read *a, struct tar *tar, + struct archive_string *as, const void *h, size_t *unconsumed) +{ + int64_t size; + const struct archive_entry_header_ustar *header; + int r; + + (void)tar; /* UNUSED */ + header = (const struct archive_entry_header_ustar *)h; + size = tar_atol(header->size, sizeof(header->size)); + if (size > entry_limit) { + return (ARCHIVE_FATAL); + } + if ((size > (int64_t)pathname_limit) || (size < 0)) { + archive_string_empty(as); + int64_t to_consume = ((size + 511) & ~511); + if (to_consume != __archive_read_consume(a, to_consume)) { + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, EINVAL, + "Special header too large: %d > 1MiB", + (int)size); + return (ARCHIVE_WARN); + } + r = read_bytes_to_string(a, as, size, unconsumed); + *unconsumed += 0x1ff & (-size); + return(r); +} + /* * Parse out common header elements. * @@ -1181,21 +1294,28 @@ header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; - char tartype; + const char *existing_linkpath; + const wchar_t *existing_wcs_linkpath; int err = ARCHIVE_OK; header = (const struct archive_entry_header_ustar *)h; - if (header->linkname[0]) - archive_strncpy(&(tar->entry_linkpath), - header->linkname, sizeof(header->linkname)); - else - archive_string_empty(&(tar->entry_linkpath)); /* Parse out the numeric fields (all are octal) */ - archive_entry_set_mode(entry, - (mode_t)tar_atol(header->mode, sizeof(header->mode))); - archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); - archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); + + /* Split mode handling: Set filetype always, perm only if not already set */ + archive_entry_set_filetype(entry, + (mode_t)tar_atol(header->mode, sizeof(header->mode))); + if (!archive_entry_perm_is_set(entry)) { + archive_entry_set_perm(entry, + (mode_t)tar_atol(header->mode, sizeof(header->mode))); + } + if (!archive_entry_uid_is_set(entry)) { + archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid))); + } + if (!archive_entry_gid_is_set(entry)) { + archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); + } + tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size)); if (tar->entry_bytes_remaining < 0) { tar->entry_bytes_remaining = 0; @@ -1203,28 +1323,49 @@ header_common(struct archive_read *a, struct tar *tar, "Tar entry has negative size"); return (ARCHIVE_FATAL); } - if (tar->entry_bytes_remaining == INT64_MAX) { - /* Note: tar_atol returns INT64_MAX on overflow */ + if (tar->entry_bytes_remaining > entry_limit) { tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar entry size overflow"); return (ARCHIVE_FATAL); } - tar->realsize = tar->entry_bytes_remaining; - archive_entry_set_size(entry, tar->entry_bytes_remaining); - archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); + if (!tar->realsize_override) { + tar->realsize = tar->entry_bytes_remaining; + } + archive_entry_set_size(entry, tar->realsize); + + if (!archive_entry_mtime_is_set(entry)) { + archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); + } /* Handle the tar type flag appropriately. */ - tartype = header->typeflag[0]; + tar->filetype = header->typeflag[0]; - switch (tartype) { + /* + * TODO: If the linkpath came from Pax extension header, then + * we should obey the hdrcharset_utf8 flag when converting these. + */ + switch (tar->filetype) { case '1': /* Hard link */ - if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s, - archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, - "Linkname"); - if (err == ARCHIVE_FATAL) - return (err); + archive_entry_set_link_to_hardlink(entry); + existing_wcs_linkpath = archive_entry_hardlink_w(entry); + existing_linkpath = archive_entry_hardlink(entry); + if ((existing_linkpath == NULL || existing_linkpath[0] == '\0') + && (existing_wcs_linkpath == NULL || existing_wcs_linkpath[0] == '\0')) { + struct archive_string linkpath; + archive_string_init(&linkpath); + archive_strncpy(&linkpath, + header->linkname, sizeof(header->linkname)); + if (archive_entry_copy_hardlink_l(entry, linkpath.s, + archive_strlen(&linkpath), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, + "Linkname"); + if (err == ARCHIVE_FATAL) { + archive_string_free(&linkpath); + return (err); + } + } + archive_string_free(&linkpath); } /* * The following may seem odd, but: Technically, tar @@ -1284,16 +1425,29 @@ header_common(struct archive_read *a, struct tar *tar, */ break; case '2': /* Symlink */ + archive_entry_set_link_to_symlink(entry); + existing_wcs_linkpath = archive_entry_symlink_w(entry); + existing_linkpath = archive_entry_symlink(entry); + if ((existing_linkpath == NULL || existing_linkpath[0] == '\0') + && (existing_wcs_linkpath == NULL || existing_wcs_linkpath[0] == '\0')) { + struct archive_string linkpath; + archive_string_init(&linkpath); + archive_strncpy(&linkpath, + header->linkname, sizeof(header->linkname)); + if (archive_entry_copy_symlink_l(entry, linkpath.s, + archive_strlen(&linkpath), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, + "Linkname"); + if (err == ARCHIVE_FATAL) { + archive_string_free(&linkpath); + return (err); + } + } + archive_string_free(&linkpath); + } archive_entry_set_filetype(entry, AE_IFLNK); archive_entry_set_size(entry, 0); tar->entry_bytes_remaining = 0; - if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s, - archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, - "Linkname"); - if (err == ARCHIVE_FATAL) - return (err); - } break; case '3': /* Character device */ archive_entry_set_filetype(entry, AE_IFCHR); @@ -1343,15 +1497,9 @@ header_common(struct archive_read *a, struct tar *tar, * sparse information in the extended area. */ /* FALLTHROUGH */ - case '0': - /* - * Enable sparse file "read" support only for regular - * files and explicit GNU sparse files. However, we - * don't allow non-standard file types to be sparse. - */ - tar->sparse_allowed = 1; + case '0': /* ustar "regular" file */ /* FALLTHROUGH */ - default: /* Regular file and non-standard types */ + default: /* Non-standard file types */ /* * Per POSIX: non-recognized types should always be * treated as regular files. @@ -1391,21 +1539,13 @@ header_old_tar(struct archive_read *a, struct tar *tar, } /* - * Read a Mac AppleDouble-encoded blob of file metadata, - * if there is one. + * Is this likely an AppleDouble extension? */ static int -read_mac_metadata_blob(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const void *h, size_t *unconsumed) -{ - int64_t size; - size_t msize; - const void *data; +is_mac_metadata_entry(struct archive_entry *entry) { const char *p, *name; const wchar_t *wp, *wname; - (void)h; /* UNUSED */ - wname = wp = archive_entry_pathname_w(entry); if (wp != NULL) { /* Find the last path element. */ @@ -1417,8 +1557,8 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, * If last path element starts with "._", then * this is a Mac extension. */ - if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0') - return ARCHIVE_OK; + if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0') + return 1; } else { /* Find the last path element. */ name = p = archive_entry_pathname(entry); @@ -1432,9 +1572,29 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, * If last path element starts with "._", then * this is a Mac extension. */ - if (name[0] != '.' || name[1] != '_' || name[2] == '\0') - return ARCHIVE_OK; + if (name[0] == '.' && name[1] == '_' && name[2] != '\0') + return 1; } + /* Not a mac extension */ + return 0; +} + +/* + * Read a Mac AppleDouble-encoded blob of file metadata, + * if there is one. + * + * TODO: In Libarchive 4, we should consider ripping this + * out -- instead, return a file starting with `._` as + * a regular file and let the client (or archive_write logic) + * handle it. + */ +static int +read_mac_metadata_blob(struct archive_read *a, + struct archive_entry *entry, size_t *unconsumed) +{ + int64_t size; + size_t msize; + const void *data; /* Read the body as a Mac OS metadata blob. */ size = archive_entry_size(entry); @@ -1444,6 +1604,17 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); } + /* TODO: Should this merely skip the overlarge entry and + * WARN? Or is xattr_limit sufficiently large that we can + * safely assume anything larger is malicious? */ + if (size > (int64_t)xattr_limit) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Oversized AppleDouble extension has size %llu > %llu", + (unsigned long long)size, + (unsigned long long)xattr_limit); + return (ARCHIVE_FATAL); + } + /* * TODO: Look beyond the body here to peek at the next header. * If it's a regular header (not an extension header) @@ -1456,15 +1627,16 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, * Q: Is the above idea really possible? Even * when there are GNU or pax extension entries? */ + tar_flush_unconsumed(a, unconsumed); data = __archive_read_ahead(a, msize, NULL); if (data == NULL) { *unconsumed = 0; return (ARCHIVE_FATAL); } + archive_entry_clear(entry); archive_entry_copy_mac_metadata(entry, data, msize); *unconsumed = (msize + 511) & ~ 511; - tar_flush_unconsumed(a, unconsumed); - return (tar_read_header(a, tar, entry, unconsumed)); + return (ARCHIVE_OK); } /* @@ -1474,76 +1646,62 @@ static int header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, size_t *unconsumed) { - int err; - - err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed); - if (err != ARCHIVE_OK) - return (err); - err = tar_read_header(a, tar, entry, unconsumed); - return (err); -} - -static int -header_pax_extensions(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const void *h, size_t *unconsumed) -{ - int err, err2; - - err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed); - if (err != ARCHIVE_OK) - return (err); + const struct archive_entry_header_ustar *header; + int64_t size, to_consume; - /* Parse the next header. */ - err = tar_read_header(a, tar, entry, unconsumed); - if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN)) - return (err); + (void)a; /* UNUSED */ + (void)tar; /* UNUSED */ + (void)entry; /* UNUSED */ - /* - * TODO: Parse global/default options into 'entry' struct here - * before handling file-specific options. - * - * This design (parse standard header, then overwrite with pax - * extended attribute data) usually works well, but isn't ideal; - * it would be better to parse the pax extended attributes first - * and then skip any fields in the standard header that were - * defined in the pax header. - */ - err2 = pax_header(a, tar, entry, &tar->pax_header); - err = err_combine(err, err2); - tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); - return (err); + header = (const struct archive_entry_header_ustar *)h; + size = tar_atol(header->size, sizeof(header->size)); + if (size > entry_limit) { + return (ARCHIVE_FATAL); + } + to_consume = ((size + 511) & ~511); + *unconsumed += to_consume; + return (ARCHIVE_OK); } - /* * Parse a file header for a Posix "ustar" archive entry. This also * handles "pax" or "extended ustar" entries. + * + * In order to correctly handle pax attributes (which precede this), + * we have to skip parsing any field for which the entry already has + * contents. */ static int header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; - struct archive_string *as; + struct archive_string as; int err = ARCHIVE_OK, r; header = (const struct archive_entry_header_ustar *)h; /* Copy name into an internal buffer to ensure null-termination. */ - as = &(tar->entry_pathname); - if (header->prefix[0]) { - archive_strncpy(as, header->prefix, sizeof(header->prefix)); - if (as->s[archive_strlen(as) - 1] != '/') - archive_strappend_char(as, '/'); - archive_strncat(as, header->name, sizeof(header->name)); - } else { - archive_strncpy(as, header->name, sizeof(header->name)); - } - if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as), - tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Pathname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_pathname = archive_entry_pathname(entry); + const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry); + if ((existing_pathname == NULL || existing_pathname[0] == '\0') + && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == '\0')) { + archive_string_init(&as); + if (header->prefix[0]) { + archive_strncpy(&as, header->prefix, sizeof(header->prefix)); + if (as.s[archive_strlen(&as) - 1] != '/') + archive_strappend_char(&as, '/'); + archive_strncat(&as, header->name, sizeof(header->name)); + } else { + archive_strncpy(&as, header->name, sizeof(header->name)); + } + if (archive_entry_copy_pathname_l(entry, as.s, archive_strlen(&as), + tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Pathname"); + if (err == ARCHIVE_FATAL) + return (err); + } + archive_string_free(&as); } /* Handle rest of common fields. */ @@ -1554,26 +1712,36 @@ header_ustar(struct archive_read *a, struct tar *tar, err = r; /* Handle POSIX ustar fields. */ - if (archive_entry_copy_uname_l(entry, - header->uname, sizeof(header->uname), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Uname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_uname = archive_entry_uname(entry); + if (existing_uname == NULL || existing_uname[0] == '\0') { + if (archive_entry_copy_uname_l(entry, + header->uname, sizeof(header->uname), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Uname"); + if (err == ARCHIVE_FATAL) + return (err); + } } - if (archive_entry_copy_gname_l(entry, - header->gname, sizeof(header->gname), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Gname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_gname = archive_entry_gname(entry); + if (existing_gname == NULL || existing_gname[0] == '\0') { + if (archive_entry_copy_gname_l(entry, + header->gname, sizeof(header->gname), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Gname"); + if (err == ARCHIVE_FATAL) + return (err); + } } /* Parse out device numbers only for char and block specials. */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { - archive_entry_set_rdevmajor(entry, (dev_t) - tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); - archive_entry_set_rdevminor(entry, (dev_t) - tar_atol(header->rdevminor, sizeof(header->rdevminor))); + if (!archive_entry_rdev_is_set(entry)) { + archive_entry_set_rdevmajor(entry, (dev_t) + tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); + archive_entry_set_rdevminor(entry, (dev_t) + tar_atol(header->rdevminor, sizeof(header->rdevminor))); + } + } else { + archive_entry_set_rdev(entry, 0); } tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); @@ -1581,117 +1749,206 @@ header_ustar(struct archive_read *a, struct tar *tar, return (err); } - -/* - * Parse the pax extended attributes record. - * - * Returns non-zero if there's an error in the data. - */ static int -pax_header(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, struct archive_string *in_as) +header_pax_extension(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h, size_t *unconsumed) { - size_t attr_length, l, line_length, value_length; - char *p; - char *key, *value; - struct archive_string *as; + /* Sanity checks: The largest `x` body I've ever heard of was + * a little over 4MB. So I doubt there has ever been a + * well-formed archive with an `x` body over 1GiB. Similarly, + * it seems plausible that no single attribute has ever been + * larger than 100MB. So if we see a larger value here, it's + * almost certainly a sign of a corrupted/malicious archive. */ + + /* Maximum sane size for extension body: 1 GiB */ + /* This cannot be raised to larger than 8GiB without + * exceeding the maximum size for a standard ustar + * entry. */ + const int64_t ext_size_limit = 1024 * 1024 * (int64_t)1024; + /* Maximum size for a single line/attr: 100 million characters */ + /* This cannot be raised to more than 2GiB without exceeding + * a `size_t` on 32-bit platforms. */ + const size_t max_parsed_line_length = 99999999ULL; + /* Largest attribute prolog: size + name. */ + const size_t max_size_name = 512; + + /* Size and padding of the full extension body */ + int64_t ext_size, ext_padding; + size_t line_length, value_length, name_length; + ssize_t to_read, did_read; + const struct archive_entry_header_ustar *header; + const char *p, *attr_start, *name_start; struct archive_string_conv *sconv; - int err, err2; - char *attr = in_as->s; + struct archive_string *pas = NULL; + struct archive_string attr_name; + int err = ARCHIVE_OK, r; - attr_length = in_as->length; - tar->pax_hdrcharset_binary = 0; - archive_string_empty(&(tar->entry_gname)); - archive_string_empty(&(tar->entry_linkpath)); - archive_string_empty(&(tar->entry_pathname)); - archive_string_empty(&(tar->entry_pathname_override)); - archive_string_empty(&(tar->entry_uname)); - err = ARCHIVE_OK; - while (attr_length > 0) { - /* Parse decimal length field at start of line. */ + header = (const struct archive_entry_header_ustar *)h; + ext_size = tar_atol(header->size, sizeof(header->size)); + if (ext_size > entry_limit) { + return (ARCHIVE_FATAL); + } + if (ext_size < 0) { + archive_set_error(&a->archive, EINVAL, + "pax extension header has invalid size: %lld", + (long long)ext_size); + return (ARCHIVE_FATAL); + } + + ext_padding = 0x1ff & (-ext_size); + if (ext_size > ext_size_limit) { + /* Consume the pax extension body and return an error */ + if (ext_size + ext_padding != __archive_read_consume(a, ext_size + ext_padding)) { + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, EINVAL, + "Ignoring oversized pax extensions: %d > %d", + (int)ext_size, (int)ext_size_limit); + return (ARCHIVE_WARN); + } + tar_flush_unconsumed(a, unconsumed); + + /* Parse the size/name of each pax attribute in the body */ + archive_string_init(&attr_name); + while (ext_size > 0) { + /* Read enough bytes to parse the size/name of the next attribute */ + to_read = max_size_name; + if (to_read > ext_size) { + to_read = ext_size; + } + p = __archive_read_ahead(a, to_read, &did_read); + if (did_read < 0) { + return ((int)did_read); + } + if (did_read == 0) { /* EOF */ + archive_set_error(&a->archive, EINVAL, + "Truncated tar archive" + " detected while reading pax attribute name"); + return (ARCHIVE_FATAL); + } + if (did_read > ext_size) { + did_read = ext_size; + } + + /* Parse size of attribute */ line_length = 0; - l = attr_length; - p = attr; /* Record start of line. */ - while (l>0) { + attr_start = p; + while (1) { + if (p >= attr_start + did_read) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax attributes: overlarge attribute size field"); + *unconsumed += ext_size + ext_padding; + return (ARCHIVE_WARN); + } if (*p == ' ') { p++; - l--; break; } if (*p < '0' || *p > '9') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignoring malformed pax extended attributes"); + "Ignoring malformed pax attributes: malformed attribute size field"); + *unconsumed += ext_size + ext_padding; return (ARCHIVE_WARN); } line_length *= 10; line_length += *p - '0'; - if (line_length > 999999) { + if (line_length > max_parsed_line_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Rejecting pax extended attribute > 1MB"); + "Ignoring malformed pax attribute: size > %lld", + (long long)max_parsed_line_length); + *unconsumed += ext_size + ext_padding; return (ARCHIVE_WARN); } p++; - l--; } - /* - * Parsed length must be no bigger than available data, - * at least 1, and the last character of the line must - * be '\n'. - */ - if (line_length > attr_length - || line_length < 1 - || attr[line_length - 1] != '\n') - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignoring malformed pax extended attribute"); - return (ARCHIVE_WARN); + if ((int64_t)line_length > ext_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax attribute: %lld > %lld", + (long long)line_length, (long long)ext_size); + *unconsumed += ext_size + ext_padding; + return (ARCHIVE_WARN); } - /* Null-terminate the line. */ - attr[line_length - 1] = '\0'; - - /* Find end of key and null terminate it. */ - key = p; - if (key[0] == '=') - return (-1); - while (*p && *p != '=') - ++p; - if (*p == '\0') { + /* Parse name of attribute */ + if (p >= attr_start + did_read + || p >= attr_start + line_length + || *p == '=') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid pax extended attributes"); + "Ignoring malformed pax attributes: empty name found"); + *unconsumed += ext_size + ext_padding; return (ARCHIVE_WARN); } - *p = '\0'; + name_start = p; + while (1) { + if (p >= attr_start + did_read || p >= attr_start + line_length) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax attributes: overlarge attribute name"); + *unconsumed += ext_size + ext_padding; + return (ARCHIVE_WARN); + } + if (*p == '=') { + break; + } + p++; + } + name_length = p - name_start; + p++; // Skip '=' - value = p + 1; + archive_strncpy(&attr_name, name_start, name_length); - /* Some values may be binary data */ - value_length = attr + line_length - 1 - value; + ext_size -= p - attr_start; + value_length = line_length - (p - attr_start); - /* Identify this attribute and set it in the entry. */ - err2 = pax_attribute(a, tar, entry, key, value, value_length); - if (err2 == ARCHIVE_FATAL) - return (err2); - err = err_combine(err, err2); + /* Consume size, name, and `=` */ + *unconsumed += p - attr_start; + tar_flush_unconsumed(a, unconsumed); + + /* pax_attribute will consume value_length - 1 */ + r = pax_attribute(a, tar, entry, attr_name.s, archive_strlen(&attr_name), value_length - 1, unconsumed); + ext_size -= value_length - 1; + + if (r < ARCHIVE_WARN) { + *unconsumed += ext_size + ext_padding; + return (r); + } + err = err_combine(err, r); - /* Skip to next line */ - attr += line_length; - attr_length -= line_length; + /* Consume the `\n` that follows the pax attribute value. */ + tar_flush_unconsumed(a, unconsumed); + p = __archive_read_ahead(a, 1, &did_read); + if (did_read < 0) { + return ((int)did_read); + } + if (did_read == 0) { + archive_set_error(&a->archive, EINVAL, + "Truncated tar archive" + " detected while completing pax attribute"); + return (ARCHIVE_FATAL); + } + if (p[0] != '\n') { + archive_set_error(&a->archive, EINVAL, + "Malformed pax attributes"); + *unconsumed += ext_size + ext_padding; + return (ARCHIVE_WARN); + } + ext_size -= 1; + *unconsumed += 1; + tar_flush_unconsumed(a, unconsumed); } + archive_string_free(&attr_name); + *unconsumed += ext_size + ext_padding; /* - * PAX format uses UTF-8 as default charset for its metadata - * unless hdrcharset=BINARY is present in its header. - * We apply the charset specified by the hdrcharset option only - * when the hdrcharset attribute(in PAX header) is BINARY because - * we respect the charset described in PAX header and BINARY also - * means that metadata(filename,uname and gname) character-set - * is unknown. + * Some PAX values -- pathname, linkpath, uname, gname -- + * can't be copied into the entry until we know the character + * set to use: */ - if (tar->pax_hdrcharset_binary) + if (!tar->pax_hdrcharset_utf8) + /* PAX specified "BINARY", so use the default charset */ sconv = tar->opt_sconv; else { + /* PAX default UTF-8 */ sconv = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (sconv == NULL) @@ -1701,83 +1958,85 @@ pax_header(struct archive_read *a, struct tar *tar, SCONV_SET_OPT_UTF8_LIBARCHIVE2X); } + /* Pathname */ + pas = NULL; + if (archive_strlen(&(tar->entry_pathname_override)) > 0) { + /* Prefer GNU.sparse.name attribute if present */ + /* GNU sparse files store a fake name under the standard + * "pathname" key. */ + pas = &(tar->entry_pathname_override); + } else if (archive_strlen(&(tar->entry_pathname)) > 0) { + /* Use standard "pathname" PAX extension */ + pas = &(tar->entry_pathname); + } + if (pas != NULL) { + if (archive_entry_copy_pathname_l(entry, pas->s, + archive_strlen(pas), sconv) != 0) { + err = set_conversion_failed_error(a, sconv, "Pathname"); + if (err == ARCHIVE_FATAL) + return (err); + /* Use raw name without conversion */ + archive_entry_copy_pathname(entry, pas->s); + } + } + /* Uname */ + if (archive_strlen(&(tar->entry_uname)) > 0) { + if (archive_entry_copy_uname_l(entry, tar->entry_uname.s, + archive_strlen(&(tar->entry_uname)), sconv) != 0) { + err = set_conversion_failed_error(a, sconv, "Uname"); + if (err == ARCHIVE_FATAL) + return (err); + /* Use raw name without conversion */ + archive_entry_copy_uname(entry, tar->entry_uname.s); + } + } + /* Gname */ if (archive_strlen(&(tar->entry_gname)) > 0) { if (archive_entry_copy_gname_l(entry, tar->entry_gname.s, archive_strlen(&(tar->entry_gname)), sconv) != 0) { err = set_conversion_failed_error(a, sconv, "Gname"); if (err == ARCHIVE_FATAL) return (err); - /* Use a converted an original name. */ + /* Use raw name without conversion */ archive_entry_copy_gname(entry, tar->entry_gname.s); } } + /* Linkpath */ if (archive_strlen(&(tar->entry_linkpath)) > 0) { if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s, archive_strlen(&(tar->entry_linkpath)), sconv) != 0) { - err = set_conversion_failed_error(a, sconv, "Linkname"); + err = set_conversion_failed_error(a, sconv, "Linkpath"); if (err == ARCHIVE_FATAL) return (err); - /* Use a converted an original name. */ + /* Use raw name without conversion */ archive_entry_copy_link(entry, tar->entry_linkpath.s); } } - /* - * Some extensions (such as the GNU sparse file extensions) - * deliberately store a synthetic name under the regular 'path' - * attribute and the real file name under a different attribute. - * Since we're supposed to not care about the order, we - * have no choice but to store all of the various filenames - * we find and figure it all out afterwards. This is the - * figuring out part. - */ - as = NULL; - if (archive_strlen(&(tar->entry_pathname_override)) > 0) - as = &(tar->entry_pathname_override); - else if (archive_strlen(&(tar->entry_pathname)) > 0) - as = &(tar->entry_pathname); - if (as != NULL) { - if (archive_entry_copy_pathname_l(entry, as->s, - archive_strlen(as), sconv) != 0) { - err = set_conversion_failed_error(a, sconv, "Pathname"); - if (err == ARCHIVE_FATAL) - return (err); - /* Use a converted an original name. */ - archive_entry_copy_pathname(entry, as->s); - } - } - if (archive_strlen(&(tar->entry_uname)) > 0) { - if (archive_entry_copy_uname_l(entry, tar->entry_uname.s, - archive_strlen(&(tar->entry_uname)), sconv) != 0) { - err = set_conversion_failed_error(a, sconv, "Uname"); - if (err == ARCHIVE_FATAL) - return (err); - /* Use a converted an original name. */ - archive_entry_copy_uname(entry, tar->entry_uname.s); - } - } + + /* Extension may have given us a corrected `entry_bytes_remaining` for + * the main entry; update the padding appropriately. */ + tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); return (err); } static int -pax_attribute_xattr(struct archive_entry *entry, - const char *name, const char *value) +pax_attribute_LIBARCHIVE_xattr(struct archive_entry *entry, + const char *name, size_t name_length, const char *value, size_t value_length) { char *name_decoded; void *value_decoded; size_t value_len; - if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0) + if (name_length < 1) return 3; - name += 17; - /* URL-decode name */ - name_decoded = url_decode(name); + name_decoded = url_decode(name, name_length); if (name_decoded == NULL) return 2; /* Base-64 decode value */ - value_decoded = base64_decode(value, strlen(value), &value_len); + value_decoded = base64_decode(value, value_length, &value_len); if (value_decoded == NULL) { free(name_decoded); return 1; @@ -1792,21 +2051,26 @@ pax_attribute_xattr(struct archive_entry *entry, } static int -pax_attribute_schily_xattr(struct archive_entry *entry, - const char *name, const char *value, size_t value_length) +pax_attribute_SCHILY_xattr(struct archive_entry *entry, + const char *name, size_t name_length, const char *value, size_t value_length) { - if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0) + if (name_length < 1 || name_length > 128) { return 1; + } - name += 13; - - archive_entry_xattr_add_entry(entry, name, value, value_length); + char * null_terminated_name = malloc(name_length + 1); + if (null_terminated_name != NULL) { + memcpy(null_terminated_name, name, name_length); + null_terminated_name[name_length] = '\0'; + archive_entry_xattr_add_entry(entry, null_terminated_name, value, value_length); + free(null_terminated_name); + } return 0; } static int -pax_attribute_rht_security_selinux(struct archive_entry *entry, +pax_attribute_RHT_security_selinux(struct archive_entry *entry, const char *value, size_t value_length) { archive_entry_xattr_add_entry(entry, "security.selinux", @@ -1816,10 +2080,11 @@ pax_attribute_rht_security_selinux(struct archive_entry *entry, } static int -pax_attribute_acl(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const char *value, int type) +pax_attribute_SCHILY_acl(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, size_t value_length, int type) { int r; + const char *p; const char* errstr; switch (type) { @@ -1846,8 +2111,28 @@ pax_attribute_acl(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); } - r = archive_acl_from_text_l(archive_entry_acl(entry), value, type, - tar->sconv_acl); + if (value_length > acl_limit) { + __archive_read_consume(a, value_length); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unreasonably large ACL: %d > %d", + (int)value_length, (int)acl_limit); + return (ARCHIVE_WARN); + } + + p = __archive_read_ahead(a, value_length, NULL); + if (p == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive " + "detected while reading ACL data"); + return (ARCHIVE_FATAL); + } + + r = archive_acl_from_text_nl(archive_entry_acl(entry), p, value_length, + type, tar->sconv_acl); + __archive_read_consume(a, value_length); + /* Workaround: Force perm_is_set() to be correct */ + /* If this bit were stored in the ACL, this wouldn't be needed */ + archive_entry_set_perm(entry, archive_entry_perm(entry)); if (r != ARCHIVE_OK) { if (r == ARCHIVE_FATAL) { archive_set_error(&a->archive, ENOMEM, @@ -1861,240 +2146,540 @@ pax_attribute_acl(struct archive_read *a, struct tar *tar, return (r); } +static int +pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps, long *pn, size_t *unconsumed) { + struct archive_string as; + int r; + + if (value_length > 128) { + __archive_read_consume(a, value_length); + *ps = 0; + *pn = 0; + return (ARCHIVE_FATAL); + } + + archive_string_init(&as); + r = read_bytes_to_string(a, &as, value_length, unconsumed); + if (r < ARCHIVE_OK) { + archive_string_free(&as); + return (r); + } + + pax_time(as.s, archive_strlen(&as), ps, pn); + archive_string_free(&as); + if (*ps < 0 || *ps == INT64_MAX) { + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +static int +pax_attribute_read_number(struct archive_read *a, size_t value_length, int64_t *result) { + struct archive_string as; + size_t unconsumed = 0; + int r; + + if (value_length > 64) { + __archive_read_consume(a, value_length); + *result = 0; + return (ARCHIVE_FATAL); + } + + archive_string_init(&as); + r = read_bytes_to_string(a, &as, value_length, &unconsumed); + tar_flush_unconsumed(a, &unconsumed); + if (r < ARCHIVE_OK) { + archive_string_free(&as); + return (r); + } + + *result = tar_atol10(as.s, archive_strlen(&as)); + archive_string_free(&as); + if (*result < 0 || *result == INT64_MAX) { + *result = INT64_MAX; + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + /* - * Parse a single key=value attribute. key/value pointers are - * assumed to point into reasonably long-lived storage. + * Parse a single key=value attribute. * - * Note that POSIX reserves all-lowercase keywords. Vendor-specific - * extensions should always have keywords of the form "VENDOR.attribute" - * In particular, it's quite feasible to support many different - * vendor extensions here. I'm using "LIBARCHIVE" for extensions - * unique to this library. + * POSIX reserves all-lowercase keywords. Vendor-specific extensions + * should always have keywords of the form "VENDOR.attribute" In + * particular, it's quite feasible to support many different vendor + * extensions here. I'm using "LIBARCHIVE" for extensions unique to + * this library. * - * Investigate other vendor-specific extensions and see if + * TODO: Investigate other vendor-specific extensions and see if * any of them look useful. */ static int -pax_attribute(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const char *key, const char *value, size_t value_length) +pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *entry, + const char *key, size_t key_length, size_t value_length, size_t *unconsumed) { - int64_t s; + int64_t t; long n; - int err = ARCHIVE_OK, r; + const char *p; + ssize_t bytes_read; + int err = ARCHIVE_OK; - if (value == NULL) - value = ""; /* Disable compiler warning; do not pass - * NULL pointer to strlen(). */ switch (key[0]) { case 'G': - /* Reject GNU.sparse.* headers on non-regular files. */ - if (strncmp(key, "GNU.sparse", 10) == 0 && - !tar->sparse_allowed) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Non-regular file cannot be sparse"); - return (ARCHIVE_FATAL); - } - - /* GNU "0.0" sparse pax format. */ - if (strcmp(key, "GNU.sparse.numblocks") == 0) { - tar->sparse_offset = -1; - tar->sparse_numbytes = -1; - tar->sparse_gnu_major = 0; - tar->sparse_gnu_minor = 0; - } - if (strcmp(key, "GNU.sparse.offset") == 0) { - tar->sparse_offset = tar_atol10(value, strlen(value)); - if (tar->sparse_numbytes != -1) { - if (gnu_add_sparse_entry(a, tar, - tar->sparse_offset, tar->sparse_numbytes) - != ARCHIVE_OK) - return (ARCHIVE_FATAL); - tar->sparse_offset = -1; - tar->sparse_numbytes = -1; + /* GNU.* extensions */ + if (key_length > 4 && memcmp(key, "GNU.", 4) == 0) { + key += 4; + key_length -= 4; + + /* GNU.sparse marks the existence of GNU sparse information */ + if (key_length == 6 && memcmp(key, "sparse", 6) == 0) { + tar->sparse_gnu_attributes_seen = 1; } - } - if (strcmp(key, "GNU.sparse.numbytes") == 0) { - tar->sparse_numbytes = tar_atol10(value, strlen(value)); - if (tar->sparse_offset != -1) { - if (gnu_add_sparse_entry(a, tar, - tar->sparse_offset, tar->sparse_numbytes) - != ARCHIVE_OK) - return (ARCHIVE_FATAL); - tar->sparse_offset = -1; - tar->sparse_numbytes = -1; - } - } - if (strcmp(key, "GNU.sparse.size") == 0) { - tar->realsize = tar_atol10(value, strlen(value)); - archive_entry_set_size(entry, tar->realsize); - tar->realsize_override = 1; - } - /* GNU "0.1" sparse pax format. */ - if (strcmp(key, "GNU.sparse.map") == 0) { - tar->sparse_gnu_major = 0; - tar->sparse_gnu_minor = 1; - if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK) - return (ARCHIVE_WARN); - } - - /* GNU "1.0" sparse pax format */ - if (strcmp(key, "GNU.sparse.major") == 0) { - tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value)); - tar->sparse_gnu_pending = 1; - } - if (strcmp(key, "GNU.sparse.minor") == 0) { - tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value)); - tar->sparse_gnu_pending = 1; - } - if (strcmp(key, "GNU.sparse.name") == 0) { - /* - * The real filename; when storing sparse - * files, GNU tar puts a synthesized name into - * the regular 'path' attribute in an attempt - * to limit confusion. ;-) - */ - archive_strcpy(&(tar->entry_pathname_override), value); - } - if (strcmp(key, "GNU.sparse.realsize") == 0) { - tar->realsize = tar_atol10(value, strlen(value)); - archive_entry_set_size(entry, tar->realsize); - tar->realsize_override = 1; + /* GNU.sparse.* extensions */ + else if (key_length > 7 && memcmp(key, "sparse.", 7) == 0) { + tar->sparse_gnu_attributes_seen = 1; + key += 7; + key_length -= 7; + + /* GNU "0.0" sparse pax format. */ + if (key_length == 9 && memcmp(key, "numblocks", 9) == 0) { + /* GNU.sparse.numblocks */ + tar->sparse_offset = -1; + tar->sparse_numbytes = -1; + tar->sparse_gnu_major = 0; + tar->sparse_gnu_minor = 0; + } + else if (key_length == 6 && memcmp(key, "offset", 6) == 0) { + /* GNU.sparse.offset */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->sparse_offset = t; + if (tar->sparse_numbytes != -1) { + if (gnu_add_sparse_entry(a, tar, + tar->sparse_offset, tar->sparse_numbytes) + != ARCHIVE_OK) + return (ARCHIVE_FATAL); + tar->sparse_offset = -1; + tar->sparse_numbytes = -1; + } + } + return (err); + } + else if (key_length == 8 && memcmp(key, "numbytes", 8) == 0) { + /* GNU.sparse.numbytes */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->sparse_numbytes = t; + if (tar->sparse_offset != -1) { + if (gnu_add_sparse_entry(a, tar, + tar->sparse_offset, tar->sparse_numbytes) + != ARCHIVE_OK) + return (ARCHIVE_FATAL); + tar->sparse_offset = -1; + tar->sparse_numbytes = -1; + } + } + return (err); + } + else if (key_length == 4 && memcmp(key, "size", 4) == 0) { + /* GNU.sparse.size */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->realsize = t; + archive_entry_set_size(entry, tar->realsize); + tar->realsize_override = 1; + } + return (err); + } + + /* GNU "0.1" sparse pax format. */ + else if (key_length == 3 && memcmp(key, "map", 3) == 0) { + /* GNU.sparse.map */ + tar->sparse_gnu_major = 0; + tar->sparse_gnu_minor = 1; + if (value_length > sparse_map_limit) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unreasonably large sparse map: %d > %d", + (int)value_length, (int)sparse_map_limit); + err = ARCHIVE_FAILED; + } else { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p != NULL) { + if (gnu_sparse_01_parse(a, tar, p, value_length) != ARCHIVE_OK) { + err = ARCHIVE_WARN; + } + } else { + return (ARCHIVE_FATAL); + } + } + __archive_read_consume(a, value_length); + return (err); + } + + /* GNU "1.0" sparse pax format */ + else if (key_length == 5 && memcmp(key, "major", 5) == 0) { + /* GNU.sparse.major */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK + && t >= 0 + && t <= 10) { + tar->sparse_gnu_major = (int)t; + } + return (err); + } + else if (key_length == 5 && memcmp(key, "minor", 5) == 0) { + /* GNU.sparse.minor */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK + && t >= 0 + && t <= 10) { + tar->sparse_gnu_minor = (int)t; + } + return (err); + } + else if (key_length == 4 && memcmp(key, "name", 4) == 0) { + /* GNU.sparse.name */ + /* + * The real filename; when storing sparse + * files, GNU tar puts a synthesized name into + * the regular 'path' attribute in an attempt + * to limit confusion. ;-) + */ + if (value_length > pathname_limit) { + *unconsumed += value_length; + err = ARCHIVE_WARN; + } else { + err = read_bytes_to_string(a, &(tar->entry_pathname_override), + value_length, unconsumed); + } + return (err); + } + else if (key_length == 8 && memcmp(key, "realsize", 8) == 0) { + /* GNU.sparse.realsize */ + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->realsize = t; + archive_entry_set_size(entry, tar->realsize); + tar->realsize_override = 1; + } + return (err); + } + } } break; case 'L': - /* Our extensions */ -/* TODO: Handle arbitrary extended attributes... */ -/* - if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0) - archive_entry_set_xxxxxx(entry, value); -*/ - if (strcmp(key, "LIBARCHIVE.creationtime") == 0) { - pax_time(value, &s, &n); - archive_entry_set_birthtime(entry, s, n); - } - if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) { - if (strcmp(value, "file") == 0) { - archive_entry_set_symlink_type(entry, - AE_SYMLINK_TYPE_FILE); - } else if (strcmp(value, "dir") == 0) { - archive_entry_set_symlink_type(entry, - AE_SYMLINK_TYPE_DIRECTORY); + /* LIBARCHIVE extensions */ + if (key_length > 11 && memcmp(key, "LIBARCHIVE.", 11) == 0) { + key_length -= 11; + key += 11; + + /* TODO: Handle arbitrary extended attributes... */ + /* + if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0) + archive_entry_set_xxxxxx(entry, value); + */ + if (key_length == 12 && memcmp(key, "creationtime", 12) == 0) { + /* LIBARCHIVE.creationtime */ + if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_birthtime(entry, t, n); + } + return (err); + } + else if (key_length == 11 && memcmp(key, "symlinktype", 11) == 0) { + /* LIBARCHIVE.symlinktype */ + if (value_length < 16) { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p != NULL) { + if (value_length == 4 && memcmp(p, "file", 4) == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_FILE); + } else if (value_length == 3 && memcmp(p, "dir", 3) == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_DIRECTORY); + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unrecognized symlink type"); + err = ARCHIVE_WARN; + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive " + "detected while reading `symlinktype` attribute"); + return (ARCHIVE_FATAL); + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "symlink type is very long" + "(longest recognized value is 4 bytes, this is %d)", + (int)value_length); + err = ARCHIVE_WARN; + } + __archive_read_consume(a, value_length); + return (err); + } + else if (key_length > 6 && memcmp(key, "xattr.", 6) == 0) { + key_length -= 6; + key += 6; + if (value_length > xattr_limit) { + err = ARCHIVE_WARN; + } else { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p == NULL + || pax_attribute_LIBARCHIVE_xattr(entry, key, key_length, p, value_length)) { + /* TODO: Unable to parse xattr */ + err = ARCHIVE_WARN; + } + } + __archive_read_consume(a, value_length); + return (err); } } - if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) - pax_attribute_xattr(entry, key, value); break; case 'R': /* GNU tar uses RHT.security header to store SELinux xattrs * SCHILY.xattr.security.selinux == RHT.security.selinux */ - if (strcmp(key, "RHT.security.selinux") == 0) { - pax_attribute_rht_security_selinux(entry, value, - value_length); + if (key_length == 20 && memcmp(key, "RHT.security.selinux", 20) == 0) { + if (value_length > xattr_limit) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring unreasonably large security.selinux attribute:" + " %d > %d", + (int)value_length, (int)xattr_limit); + /* TODO: Should this be FAILED instead? */ + err = ARCHIVE_WARN; + } else { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p == NULL + || pax_attribute_RHT_security_selinux(entry, p, value_length)) { + /* TODO: Unable to parse xattr */ + err = ARCHIVE_WARN; + } } + __archive_read_consume(a, value_length); + return (err); + } break; case 'S': - /* We support some keys used by the "star" archiver */ - if (strcmp(key, "SCHILY.acl.access") == 0) { - r = pax_attribute_acl(a, tar, entry, value, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS); - if (r == ARCHIVE_FATAL) - return (r); - } else if (strcmp(key, "SCHILY.acl.default") == 0) { - r = pax_attribute_acl(a, tar, entry, value, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); - if (r == ARCHIVE_FATAL) - return (r); - } else if (strcmp(key, "SCHILY.acl.ace") == 0) { - r = pax_attribute_acl(a, tar, entry, value, - ARCHIVE_ENTRY_ACL_TYPE_NFS4); - if (r == ARCHIVE_FATAL) - return (r); - } else if (strcmp(key, "SCHILY.devmajor") == 0) { - archive_entry_set_rdevmajor(entry, - (dev_t)tar_atol10(value, strlen(value))); - } else if (strcmp(key, "SCHILY.devminor") == 0) { - archive_entry_set_rdevminor(entry, - (dev_t)tar_atol10(value, strlen(value))); - } else if (strcmp(key, "SCHILY.fflags") == 0) { - archive_entry_copy_fflags_text(entry, value); - } else if (strcmp(key, "SCHILY.dev") == 0) { - archive_entry_set_dev(entry, - (dev_t)tar_atol10(value, strlen(value))); - } else if (strcmp(key, "SCHILY.ino") == 0) { - archive_entry_set_ino(entry, - tar_atol10(value, strlen(value))); - } else if (strcmp(key, "SCHILY.nlink") == 0) { - archive_entry_set_nlink(entry, (unsigned) - tar_atol10(value, strlen(value))); - } else if (strcmp(key, "SCHILY.realsize") == 0) { - tar->realsize = tar_atol10(value, strlen(value)); - tar->realsize_override = 1; - archive_entry_set_size(entry, tar->realsize); - } else if (strncmp(key, "SCHILY.xattr.", 13) == 0) { - pax_attribute_schily_xattr(entry, key, value, - value_length); - } else if (strcmp(key, "SUN.holesdata") == 0) { - /* A Solaris extension for sparse. */ - r = solaris_sparse_parse(a, tar, entry, value); - if (r < err) { - if (r == ARCHIVE_FATAL) - return (r); - err = r; - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Parse error: SUN.holesdata"); + /* SCHILY.* extensions used by "star" archiver */ + if (key_length > 7 && memcmp(key, "SCHILY.", 7) == 0) { + key_length -= 7; + key += 7; + + if (key_length == 10 && memcmp(key, "acl.access", 10) == 0) { + err = pax_attribute_SCHILY_acl(a, tar, entry, value_length, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + // TODO: Mark mode as set + return (err); + } + else if (key_length == 11 && memcmp(key, "acl.default", 11) == 0) { + err = pax_attribute_SCHILY_acl(a, tar, entry, value_length, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + return (err); + } + else if (key_length == 7 && memcmp(key, "acl.ace", 7) == 0) { + err = pax_attribute_SCHILY_acl(a, tar, entry, value_length, + ARCHIVE_ENTRY_ACL_TYPE_NFS4); + // TODO: Mark mode as set + return (err); + } + else if (key_length == 8 && memcmp(key, "devmajor", 8) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_rdevmajor(entry, (dev_t)t); + } + return (err); + } + else if (key_length == 8 && memcmp(key, "devminor", 8) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_rdevminor(entry, (dev_t)t); + } + return (err); + } + else if (key_length == 6 && memcmp(key, "fflags", 6) == 0) { + if (value_length < fflags_limit) { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p != NULL) { + archive_entry_copy_fflags_text_len(entry, p, value_length); + err = ARCHIVE_OK; + } else { + /* Truncated archive */ + err = ARCHIVE_FATAL; + } + } else { + /* Overlong fflags field */ + err = ARCHIVE_WARN; + } + __archive_read_consume(a, value_length); + return (err); + } + else if (key_length == 3 && memcmp(key, "dev", 3) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_dev(entry, (dev_t)t); + } + return (err); + } + else if (key_length == 3 && memcmp(key, "ino", 3) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_ino(entry, t); + } + return (err); + } + else if (key_length == 5 && memcmp(key, "nlink", 5) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_nlink(entry, (unsigned int)t); + } + return (err); + } + else if (key_length == 8 && memcmp(key, "realsize", 8) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->realsize = t; + tar->realsize_override = 1; + archive_entry_set_size(entry, tar->realsize); + } + return (err); + } + else if (key_length > 6 && memcmp(key, "xattr.", 6) == 0) { + key_length -= 6; + key += 6; + if (value_length < xattr_limit) { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p == NULL + || pax_attribute_SCHILY_xattr(entry, key, key_length, p, value_length)) { + /* TODO: Unable to parse xattr */ + err = ARCHIVE_WARN; + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unreasonably large xattr: %d > %d", + (int)value_length, (int)xattr_limit); + err = ARCHIVE_WARN; + } + __archive_read_consume(a, value_length); + return (err); + } + } + /* SUN.* extensions from Solaris tar */ + if (key_length > 4 && memcmp(key, "SUN.", 4) == 0) { + key_length -= 4; + key += 4; + + if (key_length == 9 && memcmp(key, "holesdata", 9) == 0) { + /* SUN.holesdata */ + if (value_length < sparse_map_limit) { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p != NULL) { + err = pax_attribute_SUN_holesdata(a, tar, entry, p, value_length); + if (err < ARCHIVE_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Parse error: SUN.holesdata"); + } + } else { + return (ARCHIVE_FATAL); + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unreasonably large sparse map: %d > %d", + (int)value_length, (int)sparse_map_limit); + err = ARCHIVE_FAILED; + } + __archive_read_consume(a, value_length); + return (err); } } break; case 'a': - if (strcmp(key, "atime") == 0) { - pax_time(value, &s, &n); - archive_entry_set_atime(entry, s, n); + if (key_length == 5 && memcmp(key, "atime", 5) == 0) { + if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_atime(entry, t, n); + } + return (err); } break; case 'c': - if (strcmp(key, "ctime") == 0) { - pax_time(value, &s, &n); - archive_entry_set_ctime(entry, s, n); - } else if (strcmp(key, "charset") == 0) { + if (key_length == 5 && memcmp(key, "ctime", 5) == 0) { + if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_ctime(entry, t, n); + } + return (err); + } else if (key_length == 7 && memcmp(key, "charset", 7) == 0) { /* TODO: Publish charset information in entry. */ - } else if (strcmp(key, "comment") == 0) { + } else if (key_length == 7 && memcmp(key, "comment", 7) == 0) { /* TODO: Publish comment in entry. */ } break; case 'g': - if (strcmp(key, "gid") == 0) { - archive_entry_set_gid(entry, - tar_atol10(value, strlen(value))); - } else if (strcmp(key, "gname") == 0) { - archive_strcpy(&(tar->entry_gname), value); + if (key_length == 3 && memcmp(key, "gid", 3) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_gid(entry, t); + } + return (err); + } else if (key_length == 5 && memcmp(key, "gname", 5) == 0) { + if (value_length > guname_limit) { + *unconsumed += value_length; + err = ARCHIVE_WARN; + } else { + err = read_bytes_to_string(a, &(tar->entry_gname), value_length, unconsumed); + } + return (err); } break; case 'h': - if (strcmp(key, "hdrcharset") == 0) { - if (strcmp(value, "BINARY") == 0) - /* Binary mode. */ - tar->pax_hdrcharset_binary = 1; - else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0) - tar->pax_hdrcharset_binary = 0; + if (key_length == 10 && memcmp(key, "hdrcharset", 10) == 0) { + if (value_length < 64) { + p = __archive_read_ahead(a, value_length, &bytes_read); + if (p != NULL) { + if (value_length == 6 + && memcmp(p, "BINARY", 6) == 0) { + /* Binary mode. */ + tar->pax_hdrcharset_utf8 = 0; + err = ARCHIVE_OK; + } else if (value_length == 23 + && memcmp(p, "ISO-IR 10646 2000 UTF-8", 23) == 0) { + tar->pax_hdrcharset_utf8 = 1; + err = ARCHIVE_OK; + } else { + /* TODO: Unrecognized character set */ + err = ARCHIVE_WARN; + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive " + "detected while reading hdrcharset attribute"); + return (ARCHIVE_FATAL); + } + } else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "hdrcharset attribute is unreasonably large (%d bytes)", + (int)value_length); + err = ARCHIVE_WARN; + } + __archive_read_consume(a, value_length); + return (err); } break; case 'l': /* pax interchange doesn't distinguish hardlink vs. symlink. */ - if (strcmp(key, "linkpath") == 0) { - archive_strcpy(&(tar->entry_linkpath), value); + if (key_length == 8 && memcmp(key, "linkpath", 8) == 0) { + if (value_length > pathname_limit) { + *unconsumed += value_length; + err = ARCHIVE_WARN; + } else { + err = read_bytes_to_string(a, &tar->entry_linkpath, value_length, unconsumed); + } + return (err); } break; case 'm': - if (strcmp(key, "mtime") == 0) { - pax_time(value, &s, &n); - archive_entry_set_mtime(entry, s, n); + if (key_length == 5 && memcmp(key, "mtime", 5) == 0) { + if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_mtime(entry, t, n); + } + return (err); } break; case 'p': - if (strcmp(key, "path") == 0) { - archive_strcpy(&(tar->entry_pathname), value); + if (key_length == 4 && memcmp(key, "path", 4) == 0) { + if (value_length > pathname_limit) { + *unconsumed += value_length; + err = ARCHIVE_WARN; + } else { + err = read_bytes_to_string(a, &(tar->entry_pathname), value_length, unconsumed); + } + return (err); } break; case 'r': @@ -2103,48 +2688,54 @@ pax_attribute(struct archive_read *a, struct tar *tar, case 's': /* POSIX has reserved 'security.*' */ /* Someday: if (strcmp(key, "security.acl") == 0) { ... } */ - if (strcmp(key, "size") == 0) { + if (key_length == 4 && memcmp(key, "size", 4) == 0) { /* "size" is the size of the data in the entry. */ - tar->entry_bytes_remaining - = tar_atol10(value, strlen(value)); - if (tar->entry_bytes_remaining < 0) { - tar->entry_bytes_remaining = 0; - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Tar size attribute is negative"); - return (ARCHIVE_FATAL); + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + tar->entry_bytes_remaining = t; + /* + * The "size" pax header keyword always overrides the + * "size" field in the tar header. + * GNU.sparse.realsize, GNU.sparse.size and + * SCHILY.realsize override this value. + */ + if (!tar->realsize_override) { + archive_entry_set_size(entry, + tar->entry_bytes_remaining); + tar->realsize + = tar->entry_bytes_remaining; + } } - if (tar->entry_bytes_remaining == INT64_MAX) { - /* Note: tar_atol returns INT64_MAX on overflow */ + else if (t == INT64_MAX) { + /* Note: pax_attr_read_number returns INT64_MAX on overflow or < 0 */ tar->entry_bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Tar size attribute overflow"); return (ARCHIVE_FATAL); } - /* - * The "size" pax header keyword always overrides the - * "size" field in the tar header. - * GNU.sparse.realsize, GNU.sparse.size and - * SCHILY.realsize override this value. - */ - if (!tar->realsize_override) { - archive_entry_set_size(entry, - tar->entry_bytes_remaining); - tar->realsize - = tar->entry_bytes_remaining; - } + return (err); } break; case 'u': - if (strcmp(key, "uid") == 0) { - archive_entry_set_uid(entry, - tar_atol10(value, strlen(value))); - } else if (strcmp(key, "uname") == 0) { - archive_strcpy(&(tar->entry_uname), value); + if (key_length == 3 && memcmp(key, "uid", 3) == 0) { + if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) { + archive_entry_set_uid(entry, t); + } + return (err); + } else if (key_length == 5 && memcmp(key, "uname", 5) == 0) { + if (value_length > guname_limit) { + *unconsumed += value_length; + err = ARCHIVE_WARN; + } else { + err = read_bytes_to_string(a, &(tar->entry_uname), value_length, unconsumed); + } + return (err); } break; } + + /* Unrecognized key, just skip the entire value. */ + __archive_read_consume(a, value_length); return (err); } @@ -2154,7 +2745,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, * parse a decimal time value, which may include a fractional portion */ static void -pax_time(const char *p, int64_t *ps, long *pn) +pax_time(const char *p, size_t length, int64_t *ps, long *pn) { char digit; int64_t s; @@ -2165,13 +2756,18 @@ pax_time(const char *p, int64_t *ps, long *pn) limit = INT64_MAX / 10; last_digit_limit = INT64_MAX % 10; + if (length <= 0) { + *ps = 0; + return; + } s = 0; sign = 1; if (*p == '-') { sign = -1; p++; + length--; } - while (*p >= '0' && *p <= '9') { + while (length > 0 && *p >= '0' && *p <= '9') { digit = *p - '0'; if (s > limit || (s == limit && digit > last_digit_limit)) { @@ -2180,6 +2776,7 @@ pax_time(const char *p, int64_t *ps, long *pn) } s = (s * 10) + digit; ++p; + --length; } *ps = s * sign; @@ -2187,13 +2784,14 @@ pax_time(const char *p, int64_t *ps, long *pn) /* Calculate nanoseconds. */ *pn = 0; - if (*p != '.') + if (length <= 0 || *p != '.') return; l = 100000000UL; do { ++p; - if (*p >= '0' && *p <= '9') + --length; + if (length > 0 && *p >= '0' && *p <= '9') *pn += (*p - '0') * l; else break; @@ -2224,49 +2822,65 @@ header_gnutar(struct archive_read *a, struct tar *tar, /* Copy filename over (to ensure null termination). */ header = (const struct archive_entry_header_gnutar *)h; - if (archive_entry_copy_pathname_l(entry, - header->name, sizeof(header->name), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Pathname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_pathname = archive_entry_pathname(entry); + if (existing_pathname == NULL || existing_pathname[0] == '\0') { + if (archive_entry_copy_pathname_l(entry, + header->name, sizeof(header->name), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Pathname"); + if (err == ARCHIVE_FATAL) + return (err); + } } /* Fields common to ustar and GNU */ /* XXX Can the following be factored out since it's common * to ustar and gnu tar? Is it okay to move it down into * header_common, perhaps? */ - if (archive_entry_copy_uname_l(entry, - header->uname, sizeof(header->uname), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Uname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_uname = archive_entry_uname(entry); + if (existing_uname == NULL || existing_uname[0] == '\0') { + if (archive_entry_copy_uname_l(entry, + header->uname, sizeof(header->uname), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Uname"); + if (err == ARCHIVE_FATAL) + return (err); + } } - if (archive_entry_copy_gname_l(entry, - header->gname, sizeof(header->gname), tar->sconv) != 0) { - err = set_conversion_failed_error(a, tar->sconv, "Gname"); - if (err == ARCHIVE_FATAL) - return (err); + const char *existing_gname = archive_entry_gname(entry); + if (existing_gname == NULL || existing_gname[0] == '\0') { + if (archive_entry_copy_gname_l(entry, + header->gname, sizeof(header->gname), tar->sconv) != 0) { + err = set_conversion_failed_error(a, tar->sconv, "Gname"); + if (err == ARCHIVE_FATAL) + return (err); + } } /* Parse out device numbers only for char and block specials */ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') { - archive_entry_set_rdevmajor(entry, (dev_t) - tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); - archive_entry_set_rdevminor(entry, (dev_t) - tar_atol(header->rdevminor, sizeof(header->rdevminor))); - } else + if (!archive_entry_rdev_is_set(entry)) { + archive_entry_set_rdevmajor(entry, (dev_t) + tar_atol(header->rdevmajor, sizeof(header->rdevmajor))); + archive_entry_set_rdevminor(entry, (dev_t) + tar_atol(header->rdevminor, sizeof(header->rdevminor))); + } + } else { archive_entry_set_rdev(entry, 0); + } tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining); /* Grab GNU-specific fields. */ - t = tar_atol(header->atime, sizeof(header->atime)); - if (t > 0) - archive_entry_set_atime(entry, t, 0); - t = tar_atol(header->ctime, sizeof(header->ctime)); - if (t > 0) - archive_entry_set_ctime(entry, t, 0); + if (!archive_entry_atime_is_set(entry)) { + t = tar_atol(header->atime, sizeof(header->atime)); + if (t > 0) + archive_entry_set_atime(entry, t, 0); + } + if (!archive_entry_ctime_is_set(entry)) { + t = tar_atol(header->ctime, sizeof(header->ctime)); + if (t > 0) + archive_entry_set_ctime(entry, t, 0); + } if (header->realsize[0] != 0) { tar->realsize @@ -2418,19 +3032,19 @@ gnu_sparse_old_parse(struct archive_read *a, struct tar *tar, * importantly, the sparse data was lost when extracted by archivers * that didn't recognize this extension. */ - static int -gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p) +gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p, size_t length) { const char *e; int64_t offset = -1, size = -1; for (;;) { e = p; - while (*e != '\0' && *e != ',') { + while (length > 0 && *e != ',') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; + length--; } if (offset < 0) { offset = tar_atol10(p, e - p); @@ -2445,9 +3059,10 @@ gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p) return (ARCHIVE_FATAL); offset = -1; } - if (*e == '\0') + if (length == 0) return (ARCHIVE_OK); p = e + 1; + length--; } } @@ -2569,8 +3184,8 @@ gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed) * consist of both data and hole. */ static int -solaris_sparse_parse(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const char *p) +pax_attribute_SUN_holesdata(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const char *p, size_t length) { const char *e; int64_t start, end; @@ -2579,16 +3194,21 @@ solaris_sparse_parse(struct archive_read *a, struct tar *tar, (void)entry; /* UNUSED */ end = 0; - if (*p == ' ') + if (length <= 0) + return (ARCHIVE_WARN); + if (*p == ' ') { p++; - else + length--; + } else { return (ARCHIVE_WARN); + } for (;;) { e = p; - while (*e != '\0' && *e != ' ') { + while (length > 0 && *e != ' ') { if (*e < '0' || *e > '9') return (ARCHIVE_WARN); e++; + length--; } start = end; end = tar_atol10(p, e - p); @@ -2600,9 +3220,15 @@ solaris_sparse_parse(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); tar->sparse_last->hole = hole; } - if (*e == '\0') - return (ARCHIVE_OK); + if (length == 0 || *e == '\n') { + if (length == 0 && *e == '\n') { + return (ARCHIVE_OK); + } else { + return (ARCHIVE_WARN); + } + } p = e + 1; + length--; hole = hole == 0; } } @@ -2904,22 +3530,23 @@ base64_decode(const char *s, size_t len, size_t *out_len) } static char * -url_decode(const char *in) +url_decode(const char *in, size_t length) { char *out, *d; const char *s; - out = (char *)malloc(strlen(in) + 1); + out = (char *)malloc(length + 1); if (out == NULL) return (NULL); - for (s = in, d = out; *s != '\0'; ) { - if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') { + for (s = in, d = out; length > 0 && *s != '\0'; ) { + if (s[0] == '%' && length > 2) { /* Try to convert % escape */ int digit1 = tohex(s[1]); int digit2 = tohex(s[2]); if (digit1 >= 0 && digit2 >= 0) { /* Looks good, consume three chars */ s += 3; + length -= 3; /* Convert output */ *d++ = ((digit1 << 4) | digit2); continue; @@ -2927,6 +3554,7 @@ url_decode(const char *in) /* Else fall through and treat '%' as normal char */ } *d++ = *s++; + --length; } *d = '\0'; return (out); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c index 61ab29ea1..fcec5bc4c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); /** * WARC is standardised by ISO TC46/SC4/WG12 and currently available as @@ -216,6 +215,7 @@ _warc_rdhdr(struct archive_read *a, struct archive_entry *entry) const char *buf; ssize_t nrd; const char *eoh; + char *tmp; /* for the file name, saves some strndup()'ing */ warc_string_t fnam; /* warc record type, not that we really use it a lot */ @@ -322,7 +322,14 @@ start_over: * malloc()+free() roundtrip */ if (fnam.len + 1U > w->pool.len) { w->pool.len = ((fnam.len + 64U) / 64U) * 64U; - w->pool.str = realloc(w->pool.str, w->pool.len); + tmp = realloc(w->pool.str, w->pool.len); + if (tmp == NULL) { + archive_set_error( + &a->archive, ENOMEM, + "Out of memory"); + return (ARCHIVE_FATAL); + } + w->pool.str = tmp; } memcpy(w->pool.str, fnam.str, fnam.len); w->pool.str[fnam.len] = '\0'; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c index efed86d2b..3e66ca1b7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c @@ -23,7 +23,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -417,7 +416,7 @@ static void unknowntag_end(struct xar *, const char *); static int xml_start(struct archive_read *, const char *, struct xmlattr_list *); static void xml_end(void *, const char *); -static void xml_data(void *, const char *, int); +static void xml_data(void *, const char *, size_t); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); #if defined(HAVE_LIBXML_XMLREADER_H) @@ -623,8 +622,8 @@ read_toc(struct archive_read *a) (size_t)xar->toc_chksum_size, NULL, 0); __archive_read_consume(a, xar->toc_chksum_size); xar->offset += xar->toc_chksum_size; - if (r != ARCHIVE_OK) #ifndef DONT_FAIL_ON_CRC_ERROR + if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); #endif } @@ -1243,7 +1242,7 @@ heap_add_entry(struct archive_read *a, return (ARCHIVE_FATAL); } new_pending_files = (struct xar_file **) - malloc(new_size * sizeof(new_pending_files[0])); + calloc(new_size, sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); @@ -1617,9 +1616,9 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, switch (xar->rd_encoding) { case GZIP: xar->stream.next_in = (Bytef *)(uintptr_t)b; - xar->stream.avail_in = avail_in; + xar->stream.avail_in = (uInt)avail_in; xar->stream.next_out = (unsigned char *)outbuff; - xar->stream.avail_out = avail_out; + xar->stream.avail_out = (uInt)avail_out; r = inflate(&(xar->stream), 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ @@ -1636,9 +1635,9 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: xar->bzstream.next_in = (char *)(uintptr_t)b; - xar->bzstream.avail_in = avail_in; + xar->bzstream.avail_in = (unsigned int)avail_in; xar->bzstream.next_out = (char *)outbuff; - xar->bzstream.avail_out = avail_out; + xar->bzstream.avail_out = (unsigned int)avail_out; r = BZ2_bzDecompress(&(xar->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ @@ -2056,6 +2055,12 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) attr = attr->next) { if (strcmp(attr->name, "link") != 0) continue; + if (xar->file->hdnext != NULL || xar->file->link != 0 || + xar->file == xar->hdlink_orgs) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "File with multiple link attributes"); + return (ARCHIVE_FATAL); + } if (strcmp(attr->value, "original") == 0) { xar->file->hdnext = xar->hdlink_orgs; xar->hdlink_orgs = xar->file; @@ -2669,7 +2674,7 @@ is_string(const char *known, const char *data, size_t len) } static void -xml_data(void *userData, const char *s, int len) +xml_data(void *userData, const char *s, size_t len) { struct archive_read *a; struct xar *xar; @@ -2702,6 +2707,9 @@ xml_data(void *userData, const char *s, int len) switch (xar->xmlsts) { case FILE_NAME: + if (xar->file->has & HAS_PATHNAME) + break; + if (xar->file->parent != NULL) { archive_string_concat(&(xar->file->pathname), &(xar->file->parent->pathname)); @@ -3185,8 +3193,11 @@ xml2_read_toc(struct archive_read *a) if (r == ARCHIVE_OK) r = xml_start(a, name, &list); xmlattr_cleanup(&list); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + xmlFreeTextReader(reader); + xmlCleanupParser(); return (r); + } if (empty) xml_end(a, name); break; @@ -3252,6 +3263,9 @@ expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts) struct xmlattr_list list; int r; + if (ud->state != ARCHIVE_OK) + return; + r = expat_xmlattr_setup(a, &list, atts); if (r == ARCHIVE_OK) r = xml_start(a, (const char *)name, &list); @@ -3272,7 +3286,7 @@ expat_data_cb(void *userData, const XML_Char *s, int len) { struct expat_userData *ud = (struct expat_userData *)userData; - xml_data(ud->archive, s, len); + xml_data(ud->archive, s, (size_t)len); } static int @@ -3308,14 +3322,16 @@ expat_read_toc(struct archive_read *a) d = NULL; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + XML_ParserFree(parser); return (r); + } xar->toc_remaining -= used; xar->offset += used; xar->toc_total += outbytes; PRINT_TOC(d, outbytes); - xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0); + xr = XML_Parse(parser, d, (int)outbytes, xar->toc_remaining == 0); __archive_read_consume(a, used); if (xr == XML_STATUS_ERROR) { XML_ParserFree(parser); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index e8b20f502..59db86cf2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $"); /* * The definitive documentation of the Zip file format is: @@ -119,7 +118,7 @@ struct trad_enc_ctx { /* Bits used in zip_flags. */ #define ZIP_ENCRYPTED (1 << 0) -#define ZIP_LENGTH_AT_END (1 << 3) +#define ZIP_LENGTH_AT_END (1 << 3) /* Also called "Streaming bit" */ #define ZIP_STRONG_ENCRYPTED (1 << 6) #define ZIP_UTF8_NAME (1 << 11) /* See "7.2 Single Password Symmetric Encryption Method" @@ -166,8 +165,8 @@ struct zip { int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; - /* Running CRC32 of the decompressed data */ - unsigned long entry_crc32; + /* Running CRC32 of the decompressed and decrypted data */ + unsigned long computed_crc32; unsigned long (*crc32func)(unsigned long, const void *, size_t); char ignore_crc32; @@ -945,7 +944,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; - zip->entry_crc32 = zip->crc32func(0, NULL, 0); + zip->computed_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { @@ -1140,7 +1139,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, "Inconsistent CRC32 values"); ret = ARCHIVE_WARN; } - if (zip_entry->compressed_size == 0) { + if (zip_entry->compressed_size == 0 + || zip_entry->compressed_size == 0xffffffff) { zip_entry->compressed_size = zip_entry_central_dir.compressed_size; } else if (zip_entry->compressed_size @@ -1284,7 +1284,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, return ARCHIVE_FATAL; } } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) - || zip_entry->uncompressed_size > 0) { + || (zip_entry->uncompressed_size > 0 + && zip_entry->uncompressed_size != 0xffffffff)) { /* Set the size only if it's meaningful. */ archive_entry_set_size(entry, zip_entry->uncompressed_size); } @@ -1343,25 +1344,267 @@ check_authentication_code(struct archive_read *a, const void *_p) } /* - * Read "uncompressed" data. There are three cases: - * 1) We know the size of the data. This is always true for the - * seeking reader (we've examined the Central Directory already). - * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred. - * Info-ZIP seems to do this; we know the size but have to grab - * the CRC from the data descriptor afterwards. - * 3) We're streaming and ZIP_LENGTH_AT_END was specified and - * we have no size information. In this case, we can do pretty - * well by watching for the data descriptor record. The data - * descriptor is 16 bytes and includes a computed CRC that should - * provide a strong check. + * The Zip end-of-file marker is inherently ambiguous. The specification + * in APPNOTE.TXT allows any of four possible formats, and there is no + * guaranteed-correct way for a reader to know a priori which one the writer + * will have used. The four formats are: + * 1. 32-bit format with an initial PK78 marker + * 2. 32-bit format without that marker + * 3. 64-bit format with the marker + * 4. 64-bit format without the marker * - * TODO: Technically, the PK\007\010 signature is optional. - * In the original spec, the data descriptor contained CRC - * and size fields but had no leading signature. In practice, - * newer writers seem to provide the signature pretty consistently. + * Mark Adler's `sunzip` streaming unzip program solved this ambiguity + * by just looking at every possible combination and accepting the + * longest one that matches the expected values. His approach always + * consumes the longest possible matching EOF marker, based on an + * analysis of all the possible failures and how the values could + * overlap. * - * For uncompressed data, the PK\007\010 marker seems essential - * to be sure we've actually seen the end of the entry. + * For example, suppose both of the first two formats listed + * above match. In that case, we know the next four + * 32-bit words match this pattern: + * ``` + * [PK\07\08] [CRC32] [compressed size] [uncompressed size] + * ``` + * but we know they must also match this pattern: + * ``` + * [CRC32] [compressed size] [uncompressed size] [other PK marker] + * ``` + * + * Since the first word here matches both the PK78 signature in the + * first form and the CRC32 in the second, we know those two values + * are equal, the CRC32 must be exactly 0x08074b50. Similarly, the + * compressed and uncompressed size must also be exactly this value. + * So we know these four words are all 0x08074b50. If we were to + * accept the shorter pattern, it would be immediately followed by + * another PK78 marker, which is not possible in a well-formed ZIP + * archive unless there is garbage between entries. This implies we + * should not accept the shorter form in such a case; we should accept + * the longer form. + * + * If the second and third possibilities above both match, we + * have a slightly different situation. The following words + * must match both the 32-bit format + * ``` + * [CRC32] [compressed size] [uncompressed size] [other PK marker] + * ``` + * and the 64-bit format + * ``` + * [CRC32] [compressed low] [compressed high] [uncompressed low] [uncompressed high] [other PK marker] + * ``` + * Since the 32-bit and 64-bit compressed sizes both match, the + * actual size must fit in 32 bits, which implies the high-order + * word of the compressed size is zero. So we know the uncompressed + * low word is zero, which again implies that if we accept the shorter + * format, there will not be a valid PK marker following it. + * + * Similar considerations rule out the shorter form in every other + * possibly-ambiguous pair. So if two of the four possible formats + * match, we should accept the longer option. + * + * If none of the four formats matches, we know the archive must be + * corrupted in some fashion. In particular, it's possible that the + * length-at-end bit was incorrect and we should not really be looking + * for an EOF marker at all. To allow for this possibility, we + * evaluate the following words to collect data for a later error + * report but do not consume any bytes. We instead rely on the later + * search for a new PK marker to re-sync to the next well-formed + * entry. + */ +static void +consume_end_of_file_marker(struct archive_read *a, struct zip *zip) +{ + const char *marker; + const char *p; + uint64_t compressed32, uncompressed32; + uint64_t compressed64, uncompressed64; + uint64_t compressed_actual, uncompressed_actual; + uint32_t crc32_actual; + const uint32_t PK78 = 0x08074B50ULL; + uint8_t crc32_ignored, crc32_may_be_zero; + + /* If there shouldn't be a marker, don't consume it. */ + if ((zip->entry->zip_flags & ZIP_LENGTH_AT_END) == 0) { + return; + } + + /* The longest Zip end-of-file record is 24 bytes. Since an + * end-of-file record can never appear at the end of the + * archive, we know 24 bytes will be available unless + * the archive is severely truncated. */ + if (NULL == (marker = __archive_read_ahead(a, 24, NULL))) { + return; + } + p = marker; + + /* The end-of-file record comprises: + * = Optional PK\007\010 marker + * = 4-byte CRC32 + * = Compressed size + * = Uncompressed size + * + * The last two fields are either both 32 bits or both 64 + * bits. We check all possible layouts and accept any one + * that gives us a complete match, else we make a best-effort + * attempt to parse out the pieces. + */ + + /* CRC32 checking can be tricky: + * * Test suites sometimes ignore the CRC32 + * * AES AE-2 always writes zero for the CRC32 + * * AES AE-1 sometimes writes zero for the CRC32 + */ + crc32_ignored = zip->ignore_crc32; + crc32_may_be_zero = 0; + crc32_actual = zip->computed_crc32; + if (zip->hctx_valid) { + switch (zip->entry->aes_extra.vendor) { + case AES_VENDOR_AE_2: + crc32_actual = 0; + break; + case AES_VENDOR_AE_1: + default: + crc32_may_be_zero = 1; + break; + } + } + + /* Values computed from the actual data in the archive. */ + compressed_actual = (uint64_t)zip->entry_compressed_bytes_read; + uncompressed_actual = (uint64_t)zip->entry_uncompressed_bytes_read; + + + /* Longest: PK78 marker, all 64-bit fields (24 bytes total) */ + if (archive_le32dec(p) == PK78 + && ((archive_le32dec(p + 4) == crc32_actual) + || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0)) + || crc32_ignored) + && (archive_le64dec(p + 8) == compressed_actual) + && (archive_le64dec(p + 16) == uncompressed_actual)) { + if (!crc32_ignored) { + zip->entry->crc32 = crc32_actual; + } + zip->entry->compressed_size = compressed_actual; + zip->entry->uncompressed_size = uncompressed_actual; + zip->unconsumed += 24; + return; + } + + /* No PK78 marker, 64-bit fields (20 bytes total) */ + if (((archive_le32dec(p) == crc32_actual) + || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0)) + || crc32_ignored) + && (archive_le64dec(p + 4) == compressed_actual) + && (archive_le64dec(p + 12) == uncompressed_actual)) { + if (!crc32_ignored) { + zip->entry->crc32 = crc32_actual; + } + zip->entry->compressed_size = compressed_actual; + zip->entry->uncompressed_size = uncompressed_actual; + zip->unconsumed += 20; + return; + } + + /* PK78 marker and 32-bit fields (16 bytes total) */ + if (archive_le32dec(p) == PK78 + && ((archive_le32dec(p + 4) == crc32_actual) + || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0)) + || crc32_ignored) + && (archive_le32dec(p + 8) == compressed_actual) + && (archive_le32dec(p + 12) == uncompressed_actual)) { + if (!crc32_ignored) { + zip->entry->crc32 = crc32_actual; + } + zip->entry->compressed_size = compressed_actual; + zip->entry->uncompressed_size = uncompressed_actual; + zip->unconsumed += 16; + return; + } + + /* Shortest: No PK78 marker, all 32-bit fields (12 bytes total) */ + if (((archive_le32dec(p) == crc32_actual) + || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0)) + || crc32_ignored) + && (archive_le32dec(p + 4) == compressed_actual) + && (archive_le32dec(p + 8) == uncompressed_actual)) { + if (!crc32_ignored) { + zip->entry->crc32 = crc32_actual; + } + zip->entry->compressed_size = compressed_actual; + zip->entry->uncompressed_size = uncompressed_actual; + zip->unconsumed += 12; + return; + } + + /* If none of the above patterns gives us a full exact match, + * then there's something definitely amiss. The fallback code + * below will parse out some plausible values for error + * reporting purposes. Note that this won't actually + * consume anything: + * + * = If there really is a marker here, the logic to resync to + * the next entry will suffice to skip it. + * + * = There might not really be a marker: Corruption or bugs + * may have set the length-at-end bit without a marker ever + * having actually been written. In this case, we + * explicitly should not consume any bytes, since that would + * prevent us from correctly reading the next entry. + */ + if (archive_le32dec(p) == PK78) { + p += 4; /* Ignore PK78 if it appears to be present */ + } + zip->entry->crc32 = archive_le32dec(p); /* Parse CRC32 */ + p += 4; + + /* Consider both 32- and 64-bit interpretations */ + compressed32 = archive_le32dec(p); + uncompressed32 = archive_le32dec(p + 4); + compressed64 = archive_le64dec(p); + uncompressed64 = archive_le64dec(p + 8); + + /* The earlier patterns may have failed because of CRC32 + * mismatch, so it's still possible that both sizes match. + * Try to match as many as we can... + */ + if (compressed32 == compressed_actual + && uncompressed32 == uncompressed_actual) { + /* Both 32-bit fields match */ + zip->entry->compressed_size = compressed32; + zip->entry->uncompressed_size = uncompressed32; + } else if (compressed64 == compressed_actual + || uncompressed64 == uncompressed_actual) { + /* One or both 64-bit fields match */ + zip->entry->compressed_size = compressed64; + zip->entry->uncompressed_size = uncompressed64; + } else { + /* Zero or one 32-bit fields match */ + zip->entry->compressed_size = compressed32; + zip->entry->uncompressed_size = uncompressed32; + } +} + +/* + * Read "uncompressed" data. + * + * This is straightforward if we know the size of the data. This is + * always true for the seeking reader (we've examined the Central + * Directory already), and will often be true for the streaming reader + * (the writer was writing uncompressed so probably knows the size). + * + * If we don't know the size, then life is more interesting. Note + * that a careful reading of the Zip specification says that a writer + * must use ZIP_LENGTH_AT_END if it cannot write the CRC into the + * local header. And if it uses ZIP_LENGTH_AT_END, then it is + * prohibited from storing the sizes in the local header. This + * prevents fully-compliant streaming writers from providing any size + * clues to a streaming reader. In this case, we have to scan the + * data as we read to try to locate the end-of-file marker. + * + * We assume here that the end-of-file marker always has the + * PK\007\010 signature. Although it's technically optional, newer + * writers seem to provide it pretty consistently, and it's not clear + * how to efficiently recognize an end-of-file marker that lacks it. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * zip->end_of_entry if it consumes all of the data. @@ -1373,18 +1616,18 @@ zip_read_data_none(struct archive_read *a, const void **_buff, struct zip *zip; const char *buff; ssize_t bytes_avail; + ssize_t trailing_extra; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); + trailing_extra = zip->hctx_valid ? AUTH_CODE_SIZE : 0; if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; - ssize_t grabbing_bytes = 24; + ssize_t grabbing_bytes = 24 + trailing_extra; - if (zip->hctx_valid) - grabbing_bytes += AUTH_CODE_SIZE; /* Grab at least 24 bytes. */ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); if (bytes_avail < grabbing_bytes) { @@ -1399,44 +1642,19 @@ zip_read_data_none(struct archive_read *a, const void **_buff, } /* Check for a complete PK\007\010 signature, followed * by the correct 4-byte CRC. */ - p = buff; - if (zip->hctx_valid) - p += AUTH_CODE_SIZE; + p = buff + trailing_extra; if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' - && (archive_le32dec(p + 4) == zip->entry_crc32 + && (archive_le32dec(p + 4) == zip->computed_crc32 || zip->ignore_crc32 || (zip->hctx_valid && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { - if (zip->entry->flags & LA_USED_ZIP64) { - uint64_t compressed, uncompressed; - zip->entry->crc32 = archive_le32dec(p + 4); - compressed = archive_le64dec(p + 8); - uncompressed = archive_le64dec(p + 16); - if (compressed > INT64_MAX || uncompressed > - INT64_MAX) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Overflow of 64-bit file sizes"); - return ARCHIVE_FAILED; - } - zip->entry->compressed_size = compressed; - zip->entry->uncompressed_size = uncompressed; - zip->unconsumed = 24; - } else { - zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = - archive_le32dec(p + 8); - zip->entry->uncompressed_size = - archive_le32dec(p + 12); - zip->unconsumed = 16; - } + zip->end_of_entry = 1; if (zip->hctx_valid) { r = check_authentication_code(a, buff); if (r != ARCHIVE_OK) return (r); } - zip->end_of_entry = 1; return (ARCHIVE_OK); } /* If not at EOF, ensure we consume at least one byte. */ @@ -1452,11 +1670,10 @@ zip_read_data_none(struct archive_read *a, const void **_buff, else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { - if (zip->hctx_valid) - p -= AUTH_CODE_SIZE; break; } else { p += 4; } } + p -= trailing_extra; bytes_avail = p - buff; } else { if (zip->entry_bytes_remaining == 0) { @@ -1499,59 +1716,15 @@ zip_read_data_none(struct archive_read *a, const void **_buff, bytes_avail = dec_size; buff = (const char *)zip->decrypted_buffer; } - *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; zip->unconsumed += bytes_avail; + *size = bytes_avail; *_buff = buff; return (ARCHIVE_OK); } -static int -consume_optional_marker(struct archive_read *a, struct zip *zip) -{ - if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { - const char *p; - - if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP end-of-file record"); - return (ARCHIVE_FATAL); - } - /* Consume the optional PK\007\010 marker. */ - if (p[0] == 'P' && p[1] == 'K' && - p[2] == '\007' && p[3] == '\010') { - p += 4; - zip->unconsumed = 4; - } - if (zip->entry->flags & LA_USED_ZIP64) { - uint64_t compressed, uncompressed; - zip->entry->crc32 = archive_le32dec(p); - compressed = archive_le64dec(p + 4); - uncompressed = archive_le64dec(p + 12); - if (compressed > INT64_MAX || - uncompressed > INT64_MAX) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Overflow of 64-bit file sizes"); - return ARCHIVE_FAILED; - } - zip->entry->compressed_size = compressed; - zip->entry->uncompressed_size = uncompressed; - zip->unconsumed += 20; - } else { - zip->entry->crc32 = archive_le32dec(p); - zip->entry->compressed_size = archive_le32dec(p + 4); - zip->entry->uncompressed_size = archive_le32dec(p + 8); - zip->unconsumed += 12; - } - } - - return (ARCHIVE_OK); -} - #if HAVE_LZMA_H && HAVE_LIBLZMA static int zipx_xz_init(struct archive_read *a, struct zip *zip) @@ -1751,7 +1924,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } - in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; zip->zipx_lzma_stream.total_in = 0; @@ -1793,20 +1966,16 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, break; } - to_consume = zip->zipx_lzma_stream.total_in; + to_consume = (ssize_t)zip->zipx_lzma_stream.total_in; __archive_read_consume(a, to_consume); zip->entry_bytes_remaining -= to_consume; zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; - *size = zip->zipx_lzma_stream.total_out; + *size = (size_t)zip->zipx_lzma_stream.total_out; *buff = zip->uncompressed_buffer; - ret = consume_optional_marker(a, zip); - if (ret != ARCHIVE_OK) - return (ret); - return (ARCHIVE_OK); } @@ -1845,7 +2014,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, } /* Set decompressor parameters. */ - in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); zip->zipx_lzma_stream.next_in = compressed_buf; zip->zipx_lzma_stream.avail_in = in_bytes; @@ -1855,7 +2024,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, /* These lzma_alone streams lack end of stream marker, so let's * make sure the unpacker won't try to unpack more than it's * supposed to. */ - zipmin((int64_t) zip->uncompressed_buffer_size, + (size_t)zipmin((int64_t) zip->uncompressed_buffer_size, zip->entry->uncompressed_size - zip->entry_uncompressed_bytes_read); zip->zipx_lzma_stream.total_out = 0; @@ -1871,8 +2040,6 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, /* This case is optional in lzma alone format. It can happen, * but most of the files don't have it. (GitHub #1257) */ case LZMA_STREAM_END: - lzma_end(&zip->zipx_lzma_stream); - zip->zipx_lzma_valid = 0; if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining) { @@ -1894,7 +2061,7 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } - to_consume = zip->zipx_lzma_stream.total_in; + to_consume = (ssize_t)zip->zipx_lzma_stream.total_in; /* Update pointers. */ __archive_read_consume(a, to_consume); @@ -1906,21 +2073,18 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, zip->end_of_entry = 1; } - /* Return values. */ - *size = zip->zipx_lzma_stream.total_out; - *buff = zip->uncompressed_buffer; - - /* Behave the same way as during deflate decompression. */ - ret = consume_optional_marker(a, zip); - if (ret != ARCHIVE_OK) - return (ret); - /* Free lzma decoder handle because we'll no longer need it. */ + /* This cannot be folded into LZMA_STREAM_END handling above + * because the stream end marker is not required in this format. */ if(zip->end_of_entry) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; } + /* Return values. */ + *size = (size_t)zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + /* If we're here, then we're good! */ return (ARCHIVE_OK); } @@ -2078,10 +2242,6 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, ++consumed_bytes; } while(consumed_bytes < zip->uncompressed_buffer_size); - /* Update pointers for libarchive. */ - *buff = zip->uncompressed_buffer; - *size = consumed_bytes; - /* Update pointers so we can continue decompression in another call. */ zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed; zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed; @@ -2093,10 +2253,9 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, zip->ppmd8_valid = 0; } - /* Seek for optional marker, same way as in each zip entry. */ - ret = consume_optional_marker(a, zip); - if (ret != ARCHIVE_OK) - return ret; + /* Update pointers for libarchive. */ + *buff = zip->uncompressed_buffer; + *size = consumed_bytes; return ARCHIVE_OK; } @@ -2172,7 +2331,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } - in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); if(in_bytes < 1) { /* libbz2 doesn't complain when caller feeds avail_in == 0. * It will actually return success in this case, which is @@ -2235,14 +2394,9 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, zip->entry_uncompressed_bytes_read += total_out; /* Give libarchive its due. */ - *size = total_out; + *size = (size_t)total_out; *buff = zip->uncompressed_buffer; - /* Seek for optional marker, like in other entries. */ - r = consume_optional_marker(a, zip); - if(r != ARCHIVE_OK) - return r; - return ARCHIVE_OK; } @@ -2324,7 +2478,7 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } - in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); if(in_bytes < 1) { /* zstd doesn't complain when caller feeds avail_in == 0. * It will actually return success in this case, which is @@ -2370,14 +2524,9 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, zip->entry_uncompressed_bytes_read += total_out; /* Give libarchive its due. */ - *size = total_out; + *size = (size_t)total_out; *buff = zip->uncompressed_buffer; - /* Seek for optional marker, like in other entries. */ - r = consume_optional_marker(a, zip); - if(r != ARCHIVE_OK) - return r; - return ARCHIVE_OK; } #endif @@ -2413,7 +2562,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip; - ssize_t bytes_avail; + ssize_t bytes_avail, to_consume = 0; const void *compressed_buff, *sp; int r; @@ -2534,34 +2683,33 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, } /* Consume as much as the compressor actually used. */ - bytes_avail = zip->stream.total_in; + to_consume = zip->stream.total_in; + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->stream.total_out; + if (zip->tctx_valid || zip->cctx_valid) { - zip->decrypted_bytes_remaining -= bytes_avail; + zip->decrypted_bytes_remaining -= to_consume; if (zip->decrypted_bytes_remaining == 0) zip->decrypted_ptr = zip->decrypted_buffer; else - zip->decrypted_ptr += bytes_avail; + zip->decrypted_ptr += to_consume; } - /* Calculate compressed data as much as we used.*/ if (zip->hctx_valid) - archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); - __archive_read_consume(a, bytes_avail); - zip->entry_bytes_remaining -= bytes_avail; - zip->entry_compressed_bytes_read += bytes_avail; - - *size = zip->stream.total_out; - zip->entry_uncompressed_bytes_read += zip->stream.total_out; - *buff = zip->uncompressed_buffer; + archive_hmac_sha1_update(&zip->hctx, sp, to_consume); - if (zip->end_of_entry && zip->hctx_valid) { - r = check_authentication_code(a, NULL); - if (r != ARCHIVE_OK) - return (r); + if (zip->end_of_entry) { + if (zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) { + return (r); + } + } } - r = consume_optional_marker(a, zip); - if (r != ARCHIVE_OK) - return (r); + *size = zip->stream.total_out; + *buff = zip->uncompressed_buffer; return (ARCHIVE_OK); } @@ -3029,13 +3177,27 @@ archive_read_format_zip_read_data(struct archive_read *a, } if (r != ARCHIVE_OK) return (r); - /* Update checksum */ - if (*size) - zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff, - (unsigned)*size); - /* If we hit the end, swallow any end-of-data marker. */ + if (*size > 0) { + zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff, + (unsigned)*size); + } + /* If we hit the end, swallow any end-of-data marker and + * verify the final check values. */ if (zip->end_of_entry) { - /* Check file size, CRC against these values. */ + consume_end_of_file_marker(a, zip); + + /* Check computed CRC against header */ + if ((!zip->hctx_valid || + zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && + zip->entry->crc32 != zip->computed_crc32 + && !zip->ignore_crc32) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP bad CRC: 0x%lx should be 0x%lx", + (unsigned long)zip->computed_crc32, + (unsigned long)zip->entry->crc32); + return (ARCHIVE_FAILED); + } + /* Check file size against header. */ if (zip->entry->compressed_size != zip->entry_compressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -3043,7 +3205,7 @@ archive_read_format_zip_read_data(struct archive_read *a, "(read %jd, expected %jd)", (intmax_t)zip->entry_compressed_bytes_read, (intmax_t)zip->entry->compressed_size); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } /* Size field only stores the lower 32 bits of the actual * size. */ @@ -3054,18 +3216,7 @@ archive_read_format_zip_read_data(struct archive_read *a, "(read %jd, expected %jd)\n", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); - return (ARCHIVE_WARN); - } - /* Check computed CRC against header */ - if ((!zip->hctx_valid || - zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && - zip->entry->crc32 != zip->entry_crc32 - && !zip->ignore_crc32) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP bad CRC: 0x%lx should be 0x%lx", - (unsigned long)zip->entry_crc32, - (unsigned long)zip->entry->crc32); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } } @@ -3529,7 +3680,7 @@ read_eocd(struct zip *zip, const char *p, int64_t current_offset) if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond start of EOCD record. */ - if (cd_offset + cd_size > current_offset) + if ((int64_t)cd_offset + cd_size > current_offset) return 0; /* Save the central directory location for later use. */ @@ -3932,6 +4083,17 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, } else { /* Generate resource fork name to find its * resource file at zip->tree_rsrc. */ + + /* If this is an entry ending with slash, + * make the resource for name slash-less + * as the actual resource fork doesn't end with '/'. + */ + size_t tmp_length = filename_length; + if (tmp_length > 0 && name[tmp_length - 1] == '/') { + tmp_length--; + r = rsrc_basename(name, tmp_length); + } + archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); archive_strncat(&(zip_entry->rsrcname), @@ -3939,7 +4101,7 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, archive_strcat(&(zip_entry->rsrcname), "._"); archive_strncat(&(zip_entry->rsrcname), name + (r - name), - filename_length - (r - name)); + tmp_length - (r - name)); /* Register an entry to RB tree to sort it by * file offset. */ __archive_rb_tree_insert_node(&zip->tree, diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index accf52631..41bfe7af1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized @@ -553,6 +552,8 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, } else mbflag = MB_PRECOMPOSED; + mbflag |= MB_ERR_INVALID_CHARS; + buffsize = dest->length + length + 1; do { /* Allocate memory for WCS. */ @@ -1527,7 +1528,7 @@ get_current_codepage(void) p = strrchr(locale, '.'); if (p == NULL) return (GetACP()); - if (strcmp(p+1, "utf8") == 0) + if ((strcmp(p+1, "utf8") == 0) || (strcmp(p+1, "UTF-8") == 0)) return CP_UTF8; cp = my_atoi(p+1); if ((int)cp <= 0) @@ -2639,81 +2640,69 @@ unicode_to_utf16le(char *p, size_t remaining, uint32_t uc) } /* - * Copy UTF-8 string in checking surrogate pair. - * If any surrogate pair are found, it would be canonicalized. + * Append new UTF-8 string to existing UTF-8 string. + * Existing string is assumed to already be in proper form; + * the new string will have invalid sequences replaced and + * surrogate pairs canonicalized. */ static int -strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p, +strncat_from_utf8_to_utf8(struct archive_string *as, const void *_src, size_t len, struct archive_string_conv *sc) { - const char *s; - char *p, *endp; - int n, ret = 0; - + int ret = 0; + const char *src = _src; (void)sc; /* UNUSED */ + /* Pre-extend the destination */ if (archive_string_ensure(as, as->length + len + 1) == NULL) return (-1); - s = (const char *)_p; - p = as->s + as->length; - endp = as->s + as->buffer_length -1; - do { + /* Invariant: src points to the first UTF8 byte that hasn't + * been copied to the destination `as`. */ + for (;;) { + int n; uint32_t uc; - const char *ss = s; - size_t w; + const char *e = src; - /* - * Forward byte sequence until a conversion of that is needed. - */ - while ((n = utf8_to_unicode(&uc, s, len)) > 0) { - s += n; + /* Skip UTF-8 sequences until we reach end-of-string or + * a code point that needs conversion. */ + while ((n = utf8_to_unicode(&uc, e, len)) > 0) { + e += n; len -= n; } - if (ss < s) { - if (p + (s - ss) > endp) { - as->length = p - as->s; - if (archive_string_ensure(as, - as->buffer_length + len + 1) == NULL) - return (-1); - p = as->s + as->length; - endp = as->s + as->buffer_length -1; - } - - memcpy(p, ss, s - ss); - p += s - ss; + /* Copy the part that doesn't need conversion */ + if (e > src) { + if (archive_string_append(as, src, e - src) == NULL) + return (-1); + src = e; } - /* - * If n is negative, current byte sequence needs a replacement. - */ - if (n < 0) { + if (n == 0) { + /* We reached end-of-string */ + return (ret); + } else { + /* Next code point needs conversion */ + char t[4]; + size_t w; + + /* Try decoding a surrogate pair */ if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) { - /* Current byte sequence may be CESU-8. */ - n = cesu8_to_unicode(&uc, s, len); + n = cesu8_to_unicode(&uc, src, len); } + /* Not a (valid) surrogate, so use a replacement char */ if (n < 0) { - ret = -1; - n *= -1;/* Use a replaced unicode character. */ + ret = -1; /* Return -1 if we used any replacement */ + n *= -1; } - - /* Rebuild UTF-8 byte sequence. */ - while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) { - as->length = p - as->s; - if (archive_string_ensure(as, - as->buffer_length + len + 1) == NULL) - return (-1); - p = as->s + as->length; - endp = as->s + as->buffer_length -1; - } - p += w; - s += n; + /* Consume converted code point */ + src += n; len -= n; + /* Convert and append new UTF-8 sequence. */ + w = unicode_to_utf8(t, sizeof(t), uc); + if (archive_string_append(as, t, w) == NULL) + return (-1); } - } while (n > 0); - as->length = p - as->s; - as->s[as->length] = '\0'; - return (ret); + } } static int @@ -3885,6 +3874,30 @@ archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes, } *p = NULL; +#if defined(_WIN32) && !defined(__CYGWIN__) + /* + * On Windows, first try converting from WCS because (1) there's no + * guarantee that the conversion to MBS will succeed, e.g. when using + * CP_ACP, and (2) that's more efficient than converting to MBS, just to + * convert back to WCS again before finally converting to UTF-8 + */ + if ((aes->aes_set & AES_SET_WCS) != 0) { + sc = archive_string_conversion_to_charset(a, "UTF-8", 1); + if (sc == NULL) + return (-1);/* Couldn't allocate memory for sc. */ + archive_string_empty(&(aes->aes_utf8)); + r = archive_string_append_from_wcs_in_codepage(&(aes->aes_utf8), + aes->aes_wcs.s, aes->aes_wcs.length, sc); + if (a == NULL) + free_sconv_object(sc); + if (r == 0) { + aes->aes_set |= AES_SET_UTF8; + *p = aes->aes_utf8.s; + return (0);/* success. */ + } else + return (-1);/* failure. */ + } +#endif /* Try converting WCS to MBS first if MBS does not exist yet. */ if ((aes->aes_set & AES_SET_MBS) == 0) { const char *pm; /* unused */ @@ -3969,6 +3982,32 @@ archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes, } *wp = NULL; +#if defined(_WIN32) && !defined(__CYGWIN__) + /* + * On Windows, prefer converting from UTF-8 directly to WCS because: + * (1) there's no guarantee that the string can be represented in MBS (e.g. + * with CP_ACP), and (2) in order to convert from UTF-8 to MBS, we're going + * to need to convert from UTF-8 to WCS anyway and its wasteful to throw + * away that intermediate result + */ + if (aes->aes_set & AES_SET_UTF8) { + struct archive_string_conv *sc; + + sc = archive_string_conversion_from_charset(a, "UTF-8", 1); + if (sc != NULL) { + archive_wstring_empty((&aes->aes_wcs)); + r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), + aes->aes_utf8.s, aes->aes_utf8.length, sc); + if (a == NULL) + free_sconv_object(sc); + if (r == 0) { + aes->aes_set |= AES_SET_WCS; + *wp = aes->aes_wcs.s; + return (0); + } + } + } +#endif /* Try converting UTF8 to MBS first if MBS does not exist yet. */ if ((aes->aes_set & AES_SET_MBS) == 0) { const char *p; /* unused */ @@ -4222,11 +4261,32 @@ archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ - /* Try converting UTF-8 to MBS, return false on failure. */ sc = archive_string_conversion_from_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ + +#if defined(_WIN32) && !defined(__CYGWIN__) + /* On Windows, there's no good way to convert from UTF8 -> MBS directly, so + * prefer to first convert to WCS as (1) it's wasteful to throw away the + * intermediate result, and (2) WCS will still be set even if we fail to + * convert to MBS (e.g. with ACP that can't represent the characters) */ + r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), + aes->aes_utf8.s, aes->aes_utf8.length, sc); + + if (a == NULL) + free_sconv_object(sc); + if (r != 0) + return (-1); /* This will guarantee we can't convert to MBS */ + aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ + + /* Try converting WCS to MBS, return false on failure. */ + if (archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s, + aes->aes_wcs.length)) + return (-1); +#else + /* Try converting UTF-8 to MBS, return false on failure. */ r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc); + if (a == NULL) free_sconv_object(sc); if (r != 0) @@ -4237,8 +4297,10 @@ archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, aes->aes_mbs.length)) return (-1); - aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; +#endif /* All conversions succeeded. */ + aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; + return (0); } diff --git a/Utilities/cmlibarchive/libarchive/archive_string.h b/Utilities/cmlibarchive/libarchive/archive_string.h index 49d7d3064..e8987867d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.h +++ b/Utilities/cmlibarchive/libarchive/archive_string.h @@ -21,9 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_string.h 201092 2009-12-28 02:26:06Z kientzle $ - * */ #ifndef ARCHIVE_STRING_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_string_composition.h b/Utilities/cmlibarchive/libarchive/archive_string_composition.h index d0ac34096..e917036f1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string_composition.h +++ b/Utilities/cmlibarchive/libarchive/archive_string_composition.h @@ -22,8 +22,6 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ - * */ /* diff --git a/Utilities/cmlibarchive/libarchive/archive_string_sprintf.c b/Utilities/cmlibarchive/libarchive/archive_string_sprintf.c index 969a5603a..c785e12bd 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string_sprintf.c +++ b/Utilities/cmlibarchive/libarchive/archive_string_sprintf.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-06 05:14:55Z kientzle $"); /* * The use of printf()-family functions can be troublesome diff --git a/Utilities/cmlibarchive/libarchive/archive_util.3 b/Utilities/cmlibarchive/libarchive/archive_util.3 index d5d4e7dfd..3aa508f25 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.3 +++ b/Utilities/cmlibarchive/libarchive/archive_util.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_UTIL 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c index 06807113b..148921d40 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.c +++ b/Utilities/cmlibarchive/libarchive/archive_util.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include @@ -256,10 +255,9 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) #endif fd = -1; ws = NULL; + archive_string_init(&temp_name); if (template == NULL) { - archive_string_init(&temp_name); - /* Get a temporary directory. */ if (tmpdir == NULL) { size_t l; @@ -282,7 +280,8 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) goto exit_tmpfile; - if (temp_name.s[temp_name.length-1] != L'/') + if (temp_name.length == 0 || + temp_name.s[temp_name.length-1] != L'/') archive_wstrappend_wchar(&temp_name, L'/'); } @@ -456,7 +455,7 @@ get_tempdir(struct archive_string *temppath) tmp = "/tmp"; #endif archive_strcpy(temppath, tmp); - if (temppath->s[temppath->length-1] != '/') + if (temppath->length == 0 || temppath->s[temppath->length-1] != '/') archive_strappend_char(temppath, '/'); return (ARCHIVE_OK); } @@ -479,7 +478,8 @@ __archive_mktemp(const char *tmpdir) goto exit_tmpfile; } else { archive_strcpy(&temp_name, tmpdir); - if (temp_name.s[temp_name.length-1] != '/') + if (temp_name.length == 0 || + temp_name.s[temp_name.length-1] != '/') archive_strappend_char(&temp_name, '/'); } #ifdef O_TMPFILE @@ -540,7 +540,7 @@ __archive_mktempx(const char *tmpdir, char *template) goto exit_tmpfile; } else archive_strcpy(&temp_name, tmpdir); - if (temp_name.s[temp_name.length-1] == '/') { + if (temp_name.length > 0 && temp_name.s[temp_name.length-1] == '/') { temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } diff --git a/Utilities/cmlibarchive/libarchive/archive_version_details.c b/Utilities/cmlibarchive/libarchive/archive_version_details.c index 5f5a5b743..2d2b6ba51 100644 --- a/Utilities/cmlibarchive/libarchive/archive_version_details.c +++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $"); #ifdef HAVE_STDLIB_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_virtual.c b/Utilities/cmlibarchive/libarchive/archive_virtual.c index f509ee5c6..97e0b8a0d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_virtual.c +++ b/Utilities/cmlibarchive/libarchive/archive_virtual.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $"); #include "archive.h" #include "archive_entry.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.c b/Utilities/cmlibarchive/libarchive/archive_windows.c index ebc5eefb8..bb540da01 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_windows.c @@ -22,8 +22,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ /* diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.h b/Utilities/cmlibarchive/libarchive/archive_windows.h index dda63b874..b8b2ce968 100644 --- a/Utilities/cmlibarchive/libarchive/archive_windows.h +++ b/Utilities/cmlibarchive/libarchive/archive_windows.h @@ -23,8 +23,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -300,12 +298,17 @@ typedef int mbstate_t; size_t wcrtomb(char *, wchar_t, mbstate_t *); #endif -#if defined(_MSC_VER) && _MSC_VER < 1300 +#if !WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) && NTDDI_VERSION < NTDDI_WIN10_VB +// not supported in UWP SDK before 20H1 +#define GetVolumePathNameW(f, v, c) (0) +#elif defined(_MSC_VER) && _MSC_VER < 1300 WINBASEAPI BOOL WINAPI GetVolumePathNameW( LPCWSTR lpszFileName, LPWSTR lpszVolumePathName, DWORD cchBufferLength ); +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 # if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */ typedef struct _FILE_ALLOCATED_RANGE_BUFFER { LARGE_INTEGER FileOffset; diff --git a/Utilities/cmlibarchive/libarchive/archive_write.3 b/Utilities/cmlibarchive/libarchive/archive_write.3 index e7f7f1384..227e4e028 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c index ec3c95c56..e1a4f34bf 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.c +++ b/Utilities/cmlibarchive/libarchive/archive_write.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:00Z kientzle $"); /* * This file contains the "essential" portions of the write API, that @@ -115,7 +114,7 @@ archive_write_new(void) /* Initialize a block of nulls for padding purposes. */ a->null_length = 1024; - nulls = (unsigned char *)calloc(1, a->null_length); + nulls = (unsigned char *)calloc(a->null_length, sizeof(unsigned char)); if (nulls == NULL) { free(a); return (NULL); @@ -133,12 +132,17 @@ archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block) struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block"); + + if (bytes_per_block < 0) { + // Do nothing if the bytes_per_block is negative + return 0; + } a->bytes_per_block = bytes_per_block; return (ARCHIVE_OK); } /* - * Get the current block size. -1 if it has never been set. + * Get the current block size. */ int archive_write_get_bytes_per_block(struct archive *_a) @@ -146,6 +150,10 @@ archive_write_get_bytes_per_block(struct archive *_a) struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block"); + if (a->bytes_per_block < 0) { + // Don't return a negative value + return 1; + } return (a->bytes_per_block); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c index 203f4142b..aa962515a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c index 87fdb73ec..3aca6d81d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_b64encode.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif @@ -85,7 +83,7 @@ archive_write_add_filter_b64encode(struct archive *_a) struct private_b64encode *state; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + ARCHIVE_STATE_NEW, "archive_write_add_filter_b64encode"); state = (struct private_b64encode *)calloc(1, sizeof(*state)); if (state == NULL) { @@ -151,7 +149,7 @@ archive_filter_b64encode_open(struct archive_write_filter *f) size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { - /* Buffer size should be a multiple number of the of bytes + /* Buffer size should be a multiple number of the bytes * per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c index ffa633c96..fc62458c5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c index 9c2144a30..4ee3bcf56 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c @@ -26,8 +26,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $"); - #ifdef HAVE_ERRNO_H #include #endif @@ -170,7 +168,7 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { - /* Buffer size should be a multiple number of the of bytes + /* Buffer size should be a multiple number of the bytes * per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c index 3ed269fce..e547e8872 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c @@ -58,8 +58,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $"); - #ifdef HAVE_ERRNO_H #include #endif @@ -160,7 +158,7 @@ archive_compressor_compress_open(struct archive_write_filter *f) } if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { - /* Buffer size should be a multiple number of the of bytes + /* Buffer size should be a multiple number of the bytes * per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_grzip.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_grzip.c index 371102d74..f8bb88606 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_grzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_grzip.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c index 3e26605ec..a272703a8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $"); - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lrzip.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lrzip.c index e215f8903..fe974c93d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lrzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lrzip.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c index 6ac450357..24061a169 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c index 3bd9062e4..8580e5884 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lzop.c @@ -25,7 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); //#undef HAVE_LZO_LZOCONF_H //#undef HAVE_LZO_LZO1X_H diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_none.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_none.c index 3c06c642e..b7aa6d4bc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_none.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_none.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_none.c 201080 2009-12-28 02:03:54Z kientzle $"); #include "archive.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c index c096e7227..c661cc7f4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $"); #ifdef HAVE_SYS_WAIT_H # include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c index 1ad458921..42dec8def 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_uuencode.c @@ -25,8 +25,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c index 0cc03b1d0..5886f3a1f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c @@ -26,8 +26,6 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $"); - #ifdef HAVE_ERRNO_H #include #endif @@ -312,7 +310,7 @@ archive_compressor_xz_open(struct archive_write_filter *f) if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { - /* Buffer size should be a multiple number of the of bytes + /* Buffer size should be a multiple number of the bytes * per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c index 3d6b3d158..8a0b67ab2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2017 Sean Purcell + * Copyright (c) 2023-2024 Klara, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,12 +26,12 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - - #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDINT_H #include #endif @@ -40,6 +41,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_UNISTD_H +#include +#endif #ifdef HAVE_ZSTD_H #include #endif @@ -55,15 +59,17 @@ struct private_data { int compression_level; int threads; int long_distance; -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream enum { running, finishing, resetting, } state; int frame_per_file; - size_t min_frame_size; - size_t max_frame_size; + size_t min_frame_in; + size_t max_frame_in; + size_t min_frame_out; + size_t max_frame_out; size_t cur_frame; size_t cur_frame_in; size_t cur_frame_out; @@ -96,7 +102,7 @@ static int archive_compressor_zstd_write(struct archive_write_filter *, static int archive_compressor_zstd_flush(struct archive_write_filter *); static int archive_compressor_zstd_close(struct archive_write_filter *); static int archive_compressor_zstd_free(struct archive_write_filter *); -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream static int drive_compressor(struct archive_write_filter *, struct private_data *, int, const void *, size_t); #endif @@ -130,10 +136,12 @@ archive_write_add_filter_zstd(struct archive *_a) data->compression_level = CLEVEL_DEFAULT; data->threads = 0; data->long_distance = 0; -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream data->frame_per_file = 0; - data->min_frame_size = 0; - data->max_frame_size = SIZE_MAX; + data->min_frame_in = 0; + data->max_frame_in = SIZE_MAX; + data->min_frame_out = 0; + data->max_frame_out = SIZE_MAX; data->cur_frame_in = 0; data->cur_frame_out = 0; data->cstream = ZSTD_createCStream(); @@ -162,7 +170,7 @@ static int archive_compressor_zstd_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream ZSTD_freeCStream(data->cstream); free(data->out.dst); #else @@ -173,7 +181,8 @@ archive_compressor_zstd_free(struct archive_write_filter *f) return (ARCHIVE_OK); } -static int string_to_number(const char *string, intmax_t *numberp) +static int +string_to_number(const char *string, intmax_t *numberp) { char *end; @@ -187,6 +196,43 @@ static int string_to_number(const char *string, intmax_t *numberp) return (ARCHIVE_OK); } +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream +static int +string_to_size(const char *string, size_t *numberp) +{ + uintmax_t number; + char *end; + unsigned int shift = 0; + + if (string == NULL || *string == '\0' || *string == '-') + return (ARCHIVE_WARN); + number = strtoumax(string, &end, 10); + if (end > string) { + if (*end == 'K' || *end == 'k') { + shift = 10; + end++; + } else if (*end == 'M' || *end == 'm') { + shift = 20; + end++; + } else if (*end == 'G' || *end == 'g') { + shift = 30; + end++; + } + if (*end == 'B' || *end == 'b') { + end++; + } + } + if (end == string || *end != '\0' || errno == EOVERFLOW) { + return (ARCHIVE_WARN); + } + if (number > (uintmax_t)SIZE_MAX >> shift) { + return (ARCHIVE_WARN); + } + *numberp = (size_t)(number << shift); + return (ARCHIVE_OK); +} +#endif + /* * Set write options. */ @@ -204,7 +250,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, /* If we don't have the library, hard-code the max level */ int minimum = CLEVEL_MIN; int maximum = CLEVEL_MAX; -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream maximum = ZSTD_maxCLevel(); #if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) { @@ -226,34 +272,51 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, if (string_to_number(value, &threads) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - if (threads < 0) { + +#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) + if (threads == 0) { + threads = sysconf(_SC_NPROCESSORS_ONLN); + } +#elif !defined(__CYGWIN__) && defined(_WIN32_WINNT) && \ + _WIN32_WINNT >= 0x0601 /* _WIN32_WINNT_WIN7 */ + if (threads == 0) { + DWORD winCores = GetActiveProcessorCount( + ALL_PROCESSOR_GROUPS); + threads = (intmax_t)winCores; + } +#endif + if (threads < 0 || threads > INT_MAX) { return (ARCHIVE_WARN); } data->threads = (int)threads; return (ARCHIVE_OK); -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream } else if (strcmp(key, "frame-per-file") == 0) { data->frame_per_file = 1; return (ARCHIVE_OK); - } else if (strcmp(key, "min-frame-size") == 0) { - intmax_t min_frame_size; - if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) { + } else if (strcmp(key, "min-frame-in") == 0) { + if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - if (min_frame_size < 0) { + return (ARCHIVE_OK); + } else if (strcmp(key, "min-frame-out") == 0 || + strcmp(key, "min-frame-size") == 0) { + if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - data->min_frame_size = min_frame_size; return (ARCHIVE_OK); - } else if (strcmp(key, "max-frame-size") == 0) { - intmax_t max_frame_size; - if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) { + } else if (strcmp(key, "max-frame-in") == 0 || + strcmp(key, "max-frame-size") == 0) { + if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK || + data->max_frame_in < 1024) { return (ARCHIVE_WARN); } - if (max_frame_size < 1024) { + return (ARCHIVE_OK); + } else if (strcmp(key, "max-frame-out") == 0) { + if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK || + data->max_frame_out < 1024) { return (ARCHIVE_WARN); } - data->max_frame_size = max_frame_size; return (ARCHIVE_OK); #endif } @@ -262,7 +325,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, if (string_to_number(value, &long_distance) != ARCHIVE_OK) { return (ARCHIVE_WARN); } -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR && ZSTD_VERSION_NUMBER >= MINVER_LONG +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream && ZSTD_VERSION_NUMBER >= MINVER_LONG ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog); if (ZSTD_isError(bounds.error)) { int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31)); @@ -287,7 +350,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, return (ARCHIVE_WARN); } -#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream /* * Setup callback. */ @@ -356,9 +419,12 @@ archive_compressor_zstd_flush(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - if (data->frame_per_file && data->state == running && - data->cur_frame_out > data->min_frame_size) - data->state = finishing; + if (data->frame_per_file && data->state == running) { + if (data->cur_frame_in > data->min_frame_in && + data->cur_frame_out > data->min_frame_out) { + data->state = finishing; + } + } return (drive_compressor(f, data, 1, NULL, 0)); } @@ -417,9 +483,11 @@ drive_compressor(struct archive_write_filter *f, data->total_in += in.pos - ipos; data->cur_frame_in += in.pos - ipos; data->cur_frame_out += data->out.pos - opos; - if (data->state == running && - data->cur_frame_in >= data->max_frame_size) { - data->state = finishing; + if (data->state == running) { + if (data->cur_frame_in >= data->max_frame_in || + data->cur_frame_out >= data->max_frame_out) { + data->state = finishing; + } } if (data->out.pos == data->out.size || (flush && data->out.pos > 0)) { @@ -438,7 +506,7 @@ fatal: return (ARCHIVE_FATAL); } -#else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ +#else /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */ static int archive_compressor_zstd_open(struct archive_write_filter *f) @@ -500,4 +568,4 @@ archive_compressor_zstd_close(struct archive_write_filter *f) return __archive_write_program_close(f, data->pdata); } -#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */ +#endif /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 b/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 index 4973f9990..3508851ce 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_blocksize.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE_BLOCKSIZE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_data.3 b/Utilities/cmlibarchive/libarchive/archive_write_data.3 index bc208b45d..9f239f7c9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_data.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_data.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 28, 2017 .Dt ARCHIVE_WRITE_DATA 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk.3 b/Utilities/cmlibarchive/libarchive/archive_write_disk.3 index 97f3fcde9..b046168c3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd January 19, 2020 .Dt ARCHIVE_WRITE_DISK 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index d676ed67a..ad02a6cdc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #if !defined(_WIN32) || defined(__CYGWIN__) @@ -4197,7 +4196,7 @@ copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) } for (xattr_i = 0; xattr_i < xattr_size; xattr_i += strlen(xattr_names + xattr_i) + 1) { - char *xattr_val_saved; + char *p; ssize_t s; int f; @@ -4208,15 +4207,14 @@ copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) ret = ARCHIVE_WARN; goto exit_xattr; } - xattr_val_saved = xattr_val; - xattr_val = realloc(xattr_val, s); - if (xattr_val == NULL) { + p = realloc(xattr_val, s); + if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; - free(xattr_val_saved); goto exit_xattr; } + xattr_val = p; s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, @@ -4362,8 +4360,7 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname, * silly dance of writing the data to disk just so that * copyfile() can read it back in again. */ archive_string_init(&tmp); - archive_strcpy(&tmp, pathname); - archive_strcat(&tmp, ".XXXXXX"); + archive_strcpy(&tmp, "tar.mmd.XXXXXX"); fd = mkstemp(tmp.s); if (fd < 0) { @@ -4428,7 +4425,8 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname) #else la_stat(datafork.s, &st) == -1 || #endif - (st.st_mode & AE_IFMT) != AE_IFREG) + (((st.st_mode & AE_IFMT) != AE_IFREG) && + ((st.st_mode & AE_IFMT) != AE_IFDIR))) goto skip_appledouble; /* diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h b/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h index 557d7e2bf..3efe2bad3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_private.h @@ -22,8 +22,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $ */ #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c index 5fccdb9dc..964169898 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_set_standard_lookup.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c index 7b9ea7493..774151ae2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #if defined(_WIN32) && !defined(__CYGWIN__) @@ -2595,7 +2594,7 @@ set_times(struct archive_write_disk *a, { #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ - + (((nsec)/1000)*10)) + + ((nsec)/100)) HANDLE hw = 0; ULARGE_INTEGER wintm; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 index c83eb77b6..b39cabe04 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd August 14, 2014 .Dt ARCHIVE_WRITE_FILTER 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 b/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 index 5797e16a6..574d60085 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_finish_entry.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 28, 2017 .Dt ARCHIVE_WRITE_FINISH_ENTRY 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_format.3 b/Utilities/cmlibarchive/libarchive/archive_write_format.3 index 653089f77..9e331368a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_format.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_format.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 14, 2013 .Dt ARCHIVE_WRITE_FORMAT 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_free.3 b/Utilities/cmlibarchive/libarchive/archive_write_free.3 index 5210e2a63..f6b84eae9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_free.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_free.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE_FREE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_header.3 b/Utilities/cmlibarchive/libarchive/archive_write_header.3 index 2217b1871..9c6ecec4e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_header.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_header.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE_HEADER 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_new.3 b/Utilities/cmlibarchive/libarchive/archive_write_new.3 index 788cbb855..15a7c4070 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_new.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_new.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd February 2, 2012 .Dt ARCHIVE_WRITE_NEW 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open.3 b/Utilities/cmlibarchive/libarchive/archive_write_open.3 index 6bceb964f..b12d09702 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_open.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd November 12, 2020 .Dt ARCHIVE_WRITE_OPEN 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c b/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c index b8d491faa..a58ae0479 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_fd.c 201093 2009-12-28 02:28:44Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_file.c b/Utilities/cmlibarchive/libarchive/archive_write_open_file.c index bf5b55a67..d787da3af 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_open_file.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c index 9ceefb19b..7dc73d55f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $"); #ifdef HAVE_SYS_STAT_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c index a8a0b817f..609cc47d9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $"); #include #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_private.h b/Utilities/cmlibarchive/libarchive/archive_write_private.h index 6522e6521..f259ccb16 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $ */ #ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED @@ -160,7 +158,7 @@ int __archive_write_program_write(struct archive_write_filter *, struct archive_write_program_data *, const void *, size_t); /* - * Get a encryption passphrase. + * Get an encryption passphrase. */ const char * __archive_write_get_passphrase(struct archive_write *a); #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c index 1f65fa4a7..f636cff74 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-12-29 06:15:32Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c index 1d7249f5a..004e8eea7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c index fc0de1e9f..38689d89b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 2009-12-28 03:28:21Z kientzle $"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_by_name.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_by_name.c index bfb4b3545..09519b123 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_by_name.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_by_name.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_binary.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_binary.c index d6ce35a7b..a22d06ea3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_binary.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_binary.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -578,6 +577,9 @@ archive_write_binary_close(struct archive_write *a) struct archive_entry *trailer; trailer = archive_entry_new2(NULL); + if (trailer == NULL) { + return ARCHIVE_FATAL; + } /* nlink = 1 here for GNU cpio compat. */ archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c index f0f39809d..006736a1f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201160 2009-12-29 05:41:57Z kientzle $"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_odc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_odc.c index 091925a2f..6dce78b45 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_odc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_odc.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -468,6 +467,9 @@ archive_write_odc_close(struct archive_write *a) struct archive_entry *trailer; trailer = archive_entry_new2(NULL); + if (trailer == NULL) { + return ARCHIVE_FATAL; + } /* nlink = 1 here for GNU cpio compat. */ archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_filter_by_ext.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_filter_by_ext.c index 9fe21e454..1bb33b04b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_filter_by_ext.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_filter_by_ext.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c index ec29c5c41..8979078ee 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_gnutar.c @@ -27,8 +27,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $"); - #ifdef HAVE_ERRNO_H #include @@ -298,7 +296,7 @@ archive_write_gnutar_header(struct archive_write *a, /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || - !(archive_entry_filetype(entry) == AE_IFREG)) + archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { @@ -389,7 +387,7 @@ archive_write_gnutar_header(struct archive_write *a, if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Pathame"); + "Can't allocate memory for pathname"); ret = ARCHIVE_FATAL; goto exit_write_header; } @@ -525,7 +523,7 @@ archive_write_gnutar_header(struct archive_write *a, goto exit_write_header; } - if (archive_entry_hardlink(entry) != NULL) { + if (archive_entry_hardlink_is_set(entry)) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c index 1b0317098..fdca748a0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c @@ -2241,7 +2241,7 @@ set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s, int onepad; if (s == NULL) - s = ""; + s = "\0\0"; if (l & 0x01) { onepad = 1; l &= ~1; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c index 619b7714e..6db9d2784 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $"); #ifdef HAVE_SYS_TYPES_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c index 1eb9a9a4b..4aace4682 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_pax.c 201162 2009-12-29 05:47:46Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -609,7 +608,15 @@ archive_write_pax_header(struct archive_write *a, const time_t ustar_max_mtime = get_ustar_max_mtime(); /* Sanity check. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + /* NOTE: If the caller supplied a pathname that fails WCS conversion (e.g. + * if it is invalid UTF-8), we are expected to return ARCHIVE_WARN later on + * in execution, hence the check for both pointers */ + if ((archive_entry_pathname_w(entry_original) == NULL) && + (archive_entry_pathname(entry_original) == NULL)) { +#else if (archive_entry_pathname(entry_original) == NULL) { +#endif archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); @@ -1033,6 +1040,14 @@ archive_write_pax_header(struct archive_write *a, archive_entry_set_symlink(entry_main, "././@LongSymLink"); } + else { + /* Otherwise, has non-ASCII characters; update the paths to + * however they got decoded above */ + if (hardlink != NULL) + archive_entry_set_hardlink(entry_main, linkpath); + else + archive_entry_set_symlink(entry_main, linkpath); + } need_extension = 1; } } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h b/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h index e20022755..ef9dee9d8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_private.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c index 9e4931c95..da2bc0ca3 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_shar.c @@ -25,7 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -210,6 +209,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) if (archive_entry_filetype(entry) != AE_IFDIR) { /* Try to create the dir. */ p = strdup(name); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } pp = strrchr(p, '/'); /* If there is a / character, try to create the dir. */ if (pp != NULL) { @@ -292,6 +295,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) free(shar->last_dir); shar->last_dir = strdup(name); + if (shar->last_dir == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } /* Trim a trailing '/'. */ pp = strrchr(shar->last_dir, '/'); if (pp != NULL && pp[1] == '\0') diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c index d1a06bc4f..9dc6e71f1 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ustar.c @@ -25,8 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 2009-04-27 18:35:03Z kientzle $"); - #ifdef HAVE_ERRNO_H #include @@ -256,7 +254,11 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) sconv = ustar->opt_sconv; /* Sanity check. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + if (archive_entry_pathname_w(entry) == NULL) { +#else if (archive_entry_pathname(entry) == NULL) { +#endif archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); @@ -265,7 +267,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || - !(archive_entry_filetype(entry) == AE_IFREG)) + archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c index 599407144..ffb420f08 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_v7tar.c @@ -25,8 +25,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); - #ifdef HAVE_ERRNO_H #include @@ -243,7 +241,7 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || - !(archive_entry_filetype(entry) == AE_IFREG)) + archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c index 0ef003e2f..3d22e1f4b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c @@ -26,7 +26,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c index 1e82aa219..0fcd31ff0 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include @@ -797,7 +796,7 @@ xar_finish_entry(struct archive_write *a) if (w > 0) xar->bytes_remaining -= w; else - return (w); + return ((int)w); } file = xar->cur_file; checksum_final(&(xar->e_sumwrk), &(file->data.e_sum)); @@ -1164,7 +1163,7 @@ make_file_entry(struct archive_write *a, xmlTextWriterPtr writer, /* * Make a file name entry, "". */ - l = ll = archive_strlen(&(file->basename)); + l = ll = (int)archive_strlen(&(file->basename)); tmp = malloc(l); if (tmp == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -1190,7 +1189,7 @@ make_file_entry(struct archive_write *a, xmlTextWriterPtr writer, return (ARCHIVE_FATAL); } r = xmlTextWriterWriteBase64(writer, file->basename.s, - 0, archive_strlen(&(file->basename))); + 0, (int)archive_strlen(&(file->basename))); if (r < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2232,10 +2231,10 @@ get_path_component(char *name, int n, const char *fn) p = strchr(fn, '/'); if (p == NULL) { - if ((l = strlen(fn)) == 0) + if ((l = (int)strlen(fn)) == 0) return (0); } else - l = p - fn; + l = (int)(p - fn); if (l > n -1) return (-1); memcpy(name, fn, l); @@ -2652,10 +2651,10 @@ compression_init_encoder_gzip(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; if (deflateInit2(strm, level, Z_DEFLATED, (withheader)?15:-15, @@ -2685,10 +2684,10 @@ compression_code_gzip(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; r = deflate(strm, (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH); @@ -2749,11 +2748,11 @@ compression_init_encoder_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (unsigned int)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (unsigned int)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) { @@ -2782,11 +2781,11 @@ compression_code_bzip2(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (unsigned int)lastrm->avail_in; strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff); strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32); strm->next_out = (char *)lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (unsigned int)lastrm->avail_out; strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff); strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32); r = BZ2_bzCompress(strm, diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c index d61030058..40c3b0cac 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c @@ -30,7 +30,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 2009-12-29 06:15:32Z kientzle $"); #ifdef HAVE_ERRNO_H #include @@ -132,7 +131,6 @@ struct zip { enum compression entry_compression; enum encryption entry_encryption; int entry_flags; - int entry_uses_zip64; int experiments; struct trad_enc_ctx tctx; char tctx_valid; @@ -523,6 +521,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) int ret, ret2 = ARCHIVE_OK; mode_t type; int version_needed = 10; +#define MIN_VERSION_NEEDED(x) do { if (version_needed < x) { version_needed = x; } } while (0) /* Ignore types of entries that we don't support. */ type = archive_entry_filetype(entry); @@ -557,12 +556,12 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) /* Reset information from last entry. */ zip->entry_offset = zip->written_bytes; zip->entry_uncompressed_limit = INT64_MAX; + /* Zero size values implies that we're using a trailing data descriptor */ zip->entry_compressed_size = 0; zip->entry_uncompressed_size = 0; zip->entry_compressed_written = 0; zip->entry_uncompressed_written = 0; zip->entry_flags = 0; - zip->entry_uses_zip64 = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); zip->entry_encryption = 0; archive_entry_free(zip->entry); @@ -610,7 +609,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) const char *p; size_t len; - if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) { + if (archive_entry_pathname_l(zip->entry, &p, &len, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); @@ -619,7 +618,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate Pathname '%s' to %s", - archive_entry_pathname(entry), + archive_entry_pathname(zip->entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } @@ -632,7 +631,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) * for filename. */ if (type == AE_IFLNK) { - if (archive_entry_symlink_l(entry, &p, &len, sconv)) { + if (archive_entry_symlink_l(zip->entry, &p, &len, sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory " @@ -672,11 +671,11 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, (const unsigned char *)slink, slink_size); zip->entry_compression = COMPRESSION_STORE; - version_needed = 20; + MIN_VERSION_NEEDED(20); } else if (type != AE_IFREG) { zip->entry_compression = COMPRESSION_STORE; zip->entry_uncompressed_limit = 0; - version_needed = 20; + MIN_VERSION_NEEDED(20); } else if (archive_entry_size_is_set(zip->entry)) { int64_t size = archive_entry_size(zip->entry); int64_t additional_size = 0; @@ -689,27 +688,27 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) if (zip->entry_compression == COMPRESSION_STORE) { zip->entry_compressed_size = size; zip->entry_uncompressed_size = size; - version_needed = 10; + MIN_VERSION_NEEDED(10); } else { zip->entry_uncompressed_size = size; - version_needed = 20; + MIN_VERSION_NEEDED(20); } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: additional_size = TRAD_HEADER_SIZE; - version_needed = 20; + MIN_VERSION_NEEDED(20); break; case ENCRYPTION_WINZIP_AES128: additional_size = WINZIP_AES128_HEADER_SIZE + AUTH_CODE_SIZE; - version_needed = 20; + MIN_VERSION_NEEDED(20); break; case ENCRYPTION_WINZIP_AES256: additional_size = WINZIP_AES256_HEADER_SIZE + AUTH_CODE_SIZE; - version_needed = 20; + MIN_VERSION_NEEDED(20); break; case ENCRYPTION_NONE: default: @@ -733,8 +732,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX) || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED && zip->entry_compression != COMPRESSION_STORE)) { - zip->entry_uses_zip64 = 1; - version_needed = 45; + MIN_VERSION_NEEDED(45); } /* We may know the size, but never the CRC. */ @@ -742,7 +740,6 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) } else { /* We don't know the size. Use the default * compression unless specified otherwise. - * We enable Zip64 extensions unless we're told not to. */ zip->entry_compression = zip->requested_compression; @@ -752,12 +749,12 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END; if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) { - zip->entry_uses_zip64 = 1; - version_needed = 45; + /* We might use zip64 extensions, so require 4.5 */ + MIN_VERSION_NEEDED(45); } else if (zip->entry_compression == COMPRESSION_STORE) { - version_needed = 10; + MIN_VERSION_NEEDED(10); } else { - version_needed = 20; + MIN_VERSION_NEEDED(20); } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { @@ -765,8 +762,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) case ENCRYPTION_TRADITIONAL: case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: - if (version_needed < 20) - version_needed = 20; + MIN_VERSION_NEEDED(20); break; case ENCRYPTION_NONE: default: @@ -787,16 +783,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) archive_le16enc(local_header + 8, zip->entry_compression); archive_le32enc(local_header + 10, dos_time(archive_entry_mtime(zip->entry))); - archive_le32enc(local_header + 14, zip->entry_crc32); - if (zip->entry_uses_zip64) { - /* Zip64 data in the local header "must" include both - * compressed and uncompressed sizes AND those fields - * are included only if these are 0xffffffff; - * THEREFORE these must be set this way, even if we - * know one of them is smaller. */ - archive_le32enc(local_header + 18, ZIP_4GB_MAX); - archive_le32enc(local_header + 22, ZIP_4GB_MAX); - } else { + if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) == 0) { + archive_le32enc(local_header + 14, zip->entry_crc32); archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size); archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size); } @@ -842,42 +830,19 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) * the local file header and the central directory. * We format them once and then duplicate them. */ - /* UT timestamp, length depends on what timestamps are set. */ - memcpy(e, "UT", 2); - archive_le16enc(e + 2, - 1 - + (archive_entry_mtime_is_set(entry) ? 4 : 0) - + (archive_entry_atime_is_set(entry) ? 4 : 0) - + (archive_entry_ctime_is_set(entry) ? 4 : 0)); - e += 4; - *e++ = - (archive_entry_mtime_is_set(entry) ? 1 : 0) - | (archive_entry_atime_is_set(entry) ? 2 : 0) - | (archive_entry_ctime_is_set(entry) ? 4 : 0); - if (archive_entry_mtime_is_set(entry)) { - archive_le32enc(e, (uint32_t)archive_entry_mtime(entry)); - e += 4; - } - if (archive_entry_atime_is_set(entry)) { - archive_le32enc(e, (uint32_t)archive_entry_atime(entry)); + /* ux Unix extra data, length 11, version 1 */ + if (archive_entry_uid_is_set(entry) || archive_entry_gid_is_set(entry)) { + /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */ + memcpy(e, "ux\013\000\001", 5); + e += 5; + *e++ = 4; /* Length of following UID */ + archive_le32enc(e, (uint32_t)archive_entry_uid(entry)); e += 4; - } - if (archive_entry_ctime_is_set(entry)) { - archive_le32enc(e, (uint32_t)archive_entry_ctime(entry)); + *e++ = 4; /* Length of following GID */ + archive_le32enc(e, (uint32_t)archive_entry_gid(entry)); e += 4; } - /* ux Unix extra data, length 11, version 1 */ - /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */ - memcpy(e, "ux\013\000\001", 5); - e += 5; - *e++ = 4; /* Length of following UID */ - archive_le32enc(e, (uint32_t)archive_entry_uid(entry)); - e += 4; - *e++ = 4; /* Length of following GID */ - archive_le32enc(e, (uint32_t)archive_entry_gid(entry)); - e += 4; - /* AES extra data field: WinZIP AES information, ID=0x9901 */ if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 @@ -904,7 +869,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) e += 2; } - /* Copy UT ,ux, and AES-extra into central directory as well. */ + /* Copy ux, AES-extra into central directory as well. */ zip->file_header_extra_offset = zip->central_directory_bytes; cd_extra = cd_alloc(zip, e - local_extra); memcpy(cd_extra, local_extra, e - local_extra); @@ -916,17 +881,50 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) * archive_write_zip_finish_entry() below. */ - /* "[Zip64 entry] in the local header MUST include BOTH - * original [uncompressed] and compressed size fields." */ - if (zip->entry_uses_zip64) { - unsigned char *zip64_start = e; - memcpy(e, "\001\000\020\000", 4); + /* UT timestamp: length depends on what timestamps are set. + * This header appears in the Central Directory also, but + * according to Info-Zip specification, the CD form + * only holds mtime, so we format it separately. */ + if (archive_entry_mtime_is_set(entry) + || archive_entry_atime_is_set(entry) + || archive_entry_ctime_is_set(entry)) { + unsigned char *ut = e; + memcpy(e, "UT\000\000", 4); + e += 4; + *e++ = (archive_entry_mtime_is_set(entry) ? 1 : 0) + | (archive_entry_atime_is_set(entry) ? 2 : 0) + | (archive_entry_ctime_is_set(entry) ? 4 : 0); + if (archive_entry_mtime_is_set(entry)) { + archive_le32enc(e, (uint32_t)archive_entry_mtime(entry)); + e += 4; + } + if (archive_entry_atime_is_set(entry)) { + archive_le32enc(e, (uint32_t)archive_entry_atime(entry)); + e += 4; + } + if (archive_entry_ctime_is_set(entry)) { + archive_le32enc(e, (uint32_t)archive_entry_ctime(entry)); + e += 4; + } + archive_le16enc(ut + 2, (uint16_t)(e - ut - 4)); + } + + /* + * Note about Zip64 Extended Information Extra Field: + * Because libarchive always writes in a streaming + * fashion, we never know the CRC when we're writing + * the local header. So we have to use length-at-end, which + * prevents us from putting size information into a Zip64 + * extra field. However, apparently some readers find it + * a helpful clue to have an empty such field so they + * can expect a 64-bit length-at-end marker. + */ + if (archive_entry_size_is_set(zip->entry) + && (zip->entry_uncompressed_size > ZIP_4GB_MAX + || zip->entry_compressed_size > ZIP_4GB_MAX)) { + /* Header ID 0x0001, size 0 */ + memcpy(e, "\001\000\000\000", 4); e += 4; - archive_le64enc(e, zip->entry_uncompressed_size); - e += 8; - archive_le64enc(e, zip->entry_compressed_size); - e += 8; - archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4))); } if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) { @@ -1205,7 +1203,9 @@ archive_write_zip_finish_entry(struct archive_write *a) archive_le32enc(d + 4, 0);/* no CRC.*/ else archive_le32enc(d + 4, zip->entry_crc32); - if (zip->entry_uses_zip64) { + if (zip->entry_compressed_written > ZIP_4GB_MAX + || zip->entry_uncompressed_written > ZIP_4GB_MAX + || zip->flags & ZIP_FLAG_FORCE_ZIP64) { archive_le64enc(d + 8, (uint64_t)zip->entry_compressed_written); archive_le64enc(d + 16, @@ -1224,23 +1224,60 @@ archive_write_zip_finish_entry(struct archive_write *a) return (ARCHIVE_FATAL); } - /* Append Zip64 extra data to central directory information. */ - if (zip->entry_compressed_written > ZIP_4GB_MAX - || zip->entry_uncompressed_written > ZIP_4GB_MAX + /* UT timestamp: Info-Zip specifies that _only_ the mtime should + * be recorded here; ctime and atime are also included in the + * local file descriptor. */ + if (archive_entry_mtime_is_set(zip->entry)) { + unsigned char ut[9]; + unsigned char *u = ut, *ud; + memcpy(u, "UT\005\000\001", 5); + u += 5; + archive_le32enc(u, (uint32_t)archive_entry_mtime(zip->entry)); + u += 4; + ud = cd_alloc(zip, u - ut); + if (ud == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate zip data"); + return (ARCHIVE_FATAL); + } + memcpy(ud, ut, u - ut); + } + + /* Fill in size information in the central directory entry. */ + /* Fix up central directory file header. */ + if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2) + archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/ + else + archive_le32enc(zip->file_header + 16, zip->entry_crc32); + /* Truncate to 32 bits; we'll fix up below. */ + archive_le32enc(zip->file_header + 20, (uint32_t)zip->entry_compressed_written); + archive_le32enc(zip->file_header + 24, (uint32_t)zip->entry_uncompressed_written); + archive_le16enc(zip->file_header + 30, + (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset)); + archive_le32enc(zip->file_header + 42, (uint32_t)zip->entry_offset); + + /* If any of the values immediately above are too large, we'll + * need to put the corresponding value in a Zip64 extra field + * and set the central directory value to 0xffffffff as a flag. */ + if (zip->entry_compressed_written >= ZIP_4GB_MAX + || zip->entry_uncompressed_written >= ZIP_4GB_MAX || zip->entry_offset > ZIP_4GB_MAX) { unsigned char zip64[32]; unsigned char *z = zip64, *zd; memcpy(z, "\001\000\000\000", 4); z += 4; if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) { + archive_le32enc(zip->file_header + 24, ZIP_4GB_MAX); archive_le64enc(z, zip->entry_uncompressed_written); z += 8; } if (zip->entry_compressed_written >= ZIP_4GB_MAX) { + archive_le32enc(zip->file_header + 20, ZIP_4GB_MAX); archive_le64enc(z, zip->entry_compressed_written); z += 8; } if (zip->entry_offset >= ZIP_4GB_MAX) { + archive_le32enc(zip->file_header + 42, ZIP_4GB_MAX); archive_le64enc(z, zip->entry_offset); z += 8; } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 index f4b5081e2..454c79671 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd January 31, 2020 .Dt ARCHIVE_WRITE_OPTIONS 3 .Os @@ -272,6 +270,7 @@ of physical CPU cores. .It Cm compression The value is one of .Dq store , +.Dq copy , .Dq deflate , .Dq bzip2 , .Dq lzma1 , @@ -279,12 +278,18 @@ The value is one of or .Dq ppmd to indicate how the following entries should be compressed. +The values +.Dq store +and +.Dq copy +are synonyms. Note that this setting is ignored for directories, symbolic links, and other special entries. .It Cm compression-level The value is interpreted as a decimal integer specifying the compression level. -Values between 0 and 9 are supported. +Values between 0 and 9 are supported, with the exception of bzip2 +which only supports values between 1 and 9. The interpretation of the compression level depends on the chosen compression method. .El diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.c b/Utilities/cmlibarchive/libarchive/archive_write_set_options.c index 962309ada..be2a60631 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.c @@ -24,7 +24,6 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #include "archive_write_private.h" #include "archive_options_private.h" diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 index 2db77034c..629e059b2 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd September 21, 2014 .Dt ARCHIVE_WRITE_SET_PASSPHRASE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.c b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.c index 710ecba52..f871c8e2f 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_passphrase.c @@ -24,21 +24,15 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include "archive_write_private.h" -int -archive_write_set_passphrase(struct archive *_a, const char *p) +static int +set_passphrase(struct archive_write *a, const char *p) { - struct archive_write *a = (struct archive_write *)_a; - - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, - "archive_write_set_passphrase"); - if (p == NULL || p[0] == '\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Empty passphrase is unacceptable"); @@ -55,6 +49,18 @@ archive_write_set_passphrase(struct archive *_a, const char *p) } +int +archive_write_set_passphrase(struct archive *_a, const char *p) +{ + struct archive_write *a = (struct archive_write *)_a; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, + "archive_write_set_passphrase"); + + return (set_passphrase(a, p)); +} + + int archive_write_set_passphrase_callback(struct archive *_a, void *client_data, archive_passphrase_callback *cb) @@ -81,15 +87,9 @@ __archive_write_get_passphrase(struct archive_write *a) const char *p; p = a->passphrase_callback(&a->archive, a->passphrase_client_data); - if (p != NULL) { - a->passphrase = strdup(p); - if (a->passphrase == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate data for passphrase"); - return (NULL); - } - return (a->passphrase); - } + set_passphrase(a, p); + a->passphrase_callback = NULL; + a->passphrase_client_data = NULL; } - return (NULL); + return (a->passphrase); } diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h index 669f27246..a1bf0dfe9 100644 --- a/Utilities/cmlibarchive/libarchive/config_freebsd.h +++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ */ #define __LIBARCHIVE_CONFIG_H_INCLUDED 1 @@ -205,7 +203,6 @@ #define HAVE_SYS_MOUNT_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_POLL_H 1 -#define HAVE_SYS_QUEUE_H 1 #define HAVE_SYS_SELECT_H 1 #define HAVE_SYS_STATVFS_H 1 #define HAVE_SYS_STAT_H 1 diff --git a/Utilities/cmlibarchive/libarchive/cpio.5 b/Utilities/cmlibarchive/libarchive/cpio.5 index c71018b19..21c30d78d 100644 --- a/Utilities/cmlibarchive/libarchive/cpio.5 +++ b/Utilities/cmlibarchive/libarchive/cpio.5 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd December 23, 2011 .Dt CPIO 5 .Os diff --git a/Utilities/cmlibarchive/libarchive/filter_fork.h b/Utilities/cmlibarchive/libarchive/filter_fork.h index 2bf290c4d..aeab70ae6 100644 --- a/Utilities/cmlibarchive/libarchive/filter_fork.h +++ b/Utilities/cmlibarchive/libarchive/filter_fork.h @@ -21,8 +21,6 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $ */ #ifndef FILTER_FORK_H diff --git a/Utilities/cmlibarchive/libarchive/filter_fork_posix.c b/Utilities/cmlibarchive/libarchive/filter_fork_posix.c index 62085a709..c895c08e5 100644 --- a/Utilities/cmlibarchive/libarchive/filter_fork_posix.c +++ b/Utilities/cmlibarchive/libarchive/filter_fork_posix.c @@ -30,8 +30,6 @@ #if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \ (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP)) -__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $"); - #if defined(HAVE_SYS_TYPES_H) # include #endif diff --git a/Utilities/cmlibarchive/libarchive/libarchive-formats.5 b/Utilities/cmlibarchive/libarchive/libarchive-formats.5 index 5a118ff5d..fab2f8660 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive-formats.5 +++ b/Utilities/cmlibarchive/libarchive/libarchive-formats.5 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd December 27, 2016 .Dt LIBARCHIVE-FORMATS 5 .Os diff --git a/Utilities/cmlibarchive/libarchive/libarchive.3 b/Utilities/cmlibarchive/libarchive/libarchive.3 index 649056242..c67172bf6 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive.3 +++ b/Utilities/cmlibarchive/libarchive/libarchive.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd March 18, 2012 .Dt LIBARCHIVE 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/libarchive_changes.3 b/Utilities/cmlibarchive/libarchive/libarchive_changes.3 index 6bf8db038..fd0e72105 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive_changes.3 +++ b/Utilities/cmlibarchive/libarchive/libarchive_changes.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd December 23, 2011 .Dt LIBARCHIVE_CHANGES 3 .Os diff --git a/Utilities/cmlibarchive/libarchive/libarchive_internals.3 b/Utilities/cmlibarchive/libarchive/libarchive_internals.3 index d672f3e8a..2978b48c3 100644 --- a/Utilities/cmlibarchive/libarchive/libarchive_internals.3 +++ b/Utilities/cmlibarchive/libarchive/libarchive_internals.3 @@ -22,8 +22,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd January 26, 2011 .Dt LIBARCHIVE_INTERNALS 3 .Os @@ -126,7 +124,7 @@ to read the entire file into memory at once and return the entire file to libarchive as a single block; other clients may begin asynchronous I/O operations for the next block on each request. -.Ss Decompresssion Layer +.Ss Decompression Layer The decompression layer not only handles decompression, it also buffers data so that the format handlers see a much nicer I/O model. diff --git a/Utilities/cmlibarchive/libarchive/mtree.5 b/Utilities/cmlibarchive/libarchive/mtree.5 index 8147796f3..5ea536131 100644 --- a/Utilities/cmlibarchive/libarchive/mtree.5 +++ b/Utilities/cmlibarchive/libarchive/mtree.5 @@ -26,7 +26,6 @@ .\" SUCH DAMAGE. .\" .\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93 -.\" $FreeBSD$ .\" .Dd September 4, 2013 .Dt MTREE 5 diff --git a/Utilities/cmlibarchive/libarchive/tar.5 b/Utilities/cmlibarchive/libarchive/tar.5 index 34ad4f793..725a7d683 100644 --- a/Utilities/cmlibarchive/libarchive/tar.5 +++ b/Utilities/cmlibarchive/libarchive/tar.5 @@ -23,8 +23,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ -.\" .Dd December 27, 2016 .Dt TAR 5 .Os diff --git a/Utilities/cmllpkgc/CMakeLists.txt b/Utilities/cmllpkgc/CMakeLists.txt new file mode 100644 index 000000000..88a382d99 --- /dev/null +++ b/Utilities/cmllpkgc/CMakeLists.txt @@ -0,0 +1,9 @@ +# Disable warnings to avoid changing 3rd party code. +if(CMAKE_C_COMPILER_ID MATCHES + "^(GNU|LCC|Clang|AppleClang|IBMClang|XLClang|XL|VisualAge|SunPro|HP|Intel|IntelLLVM|NVHPC)$") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") +elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall") +endif() + +add_library(cmllpkgc STATIC llpkgc.c llpkgc__internal.c) diff --git a/Utilities/cmllpkgc/README.rst b/Utilities/cmllpkgc/README.rst new file mode 100644 index 000000000..233a5750f --- /dev/null +++ b/Utilities/cmllpkgc/README.rst @@ -0,0 +1,14 @@ +llpkgc +****** + +This code is generated by the upstream llpkgc repository located at: +https://gitlab.kitware.com/utils/llpkgc + +Generally, updates to llpkgc should be made in the upstream utilities library +unless they are exceptionally specific to CMake itself. + +The upstream repository does not vendor a generated copy of the parser, so +the associated update script for this dependency runs the generator and +vendors it appropriately. This requires a reasonably up-to-date version of +npm be available in addition to the normal 3rd-party update tooling +requirements. diff --git a/Utilities/cmllpkgc/llpkgc.c b/Utilities/cmllpkgc/llpkgc.c new file mode 100644 index 000000000..33c9043cb --- /dev/null +++ b/Utilities/cmllpkgc/llpkgc.c @@ -0,0 +1,205 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include +#include + +#include "llpkgc.h" + +#define CALLBACK_MAYBE(PARSER, NAME) \ + do { \ + const llpkgc_settings_t* settings; \ + settings = (const llpkgc_settings_t*) (PARSER)->settings; \ + if(settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER)); \ + } while(0) + +#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \ + do { \ + const llpkgc_settings_t* settings; \ + settings = (const llpkgc_settings_t*) (PARSER)->settings; \ + if(settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME((PARSER), (START), (LEN)); \ + if(err == -1) { \ + err = PCE_USER; \ + llpkgc_set_error_reason((PARSER), "Span callback error in " #NAME); \ + } \ + } while(0) + +void llpkgc_init(llpkgc_t* parser, const llpkgc_settings_t* settings) { + llpkgc__internal_init(parser); + + parser->settings = (void*) settings; +} + +void llpkgc_reset(llpkgc_t* parser) { + llpkgc_settings_t* settings = parser->settings; + void* data = parser->data; + + llpkgc__internal_init(parser); + + parser->settings = settings; + parser->data = data; +} + +void llpkgc_settings_init(llpkgc_settings_t* settings) { + memset(settings, 0, sizeof(*settings)); +} + +llpkgc_errno_t llpkgc_execute(llpkgc_t* parser, const char* data, size_t len) { + return llpkgc__internal_execute(parser, data, data + len); +} + +llpkgc_errno_t llpkgc_finish(llpkgc_t* parser) { + if(parser->error != 0) + return parser->error; + + int err; + // ToDo: Better handling of user callback errors here + if(parser->unfinished_ == 1) { + parser->reason = "Invalid EOF state"; + parser->error = PCE_UNFINISHED; + return PCE_UNFINISHED; + } else if(parser->unfinished_ == 2) { + CALLBACK_MAYBE(parser, on_value_literal_complete); + if(err != PCE_OK) { + parser->error = err; + return err; + } + CALLBACK_MAYBE(parser, on_value_complete); + if(err != PCE_OK) { + parser->error = err; + return err; + } + } else if(parser->unfinished_ == 3) { + CALLBACK_MAYBE(parser, on_value_complete); + if(err != PCE_OK) { + parser->error = err; + return err; + } + } + + CALLBACK_MAYBE(parser, on_pkgc_complete); + return err; +} + +void llpkgc_pause(llpkgc_t* parser) { + if(parser->error != PCE_OK) { + return; + } + + parser->error = PCE_PAUSED; + parser->reason = "Paused"; +} + +void llpkgc_resume(llpkgc_t* parser) { + if(parser->error != PCE_PAUSED) { + return; + } + + parser->error = 0; +} + +llpkgc_errno_t llpkgc_get_errno(const llpkgc_t* parser) { + return parser->error; +} + +const char* llpkgc_get_error_reason(const llpkgc_t* parser) { + return parser->reason; +} + +void llpkgc_set_error_reason(llpkgc_t* parser, const char* reason) { + parser->reason = reason; +} + +const char* llpkgc_get_error_pos(const llpkgc_t* parser) { + return parser->error_pos; +} + +const char* llpkgc_errno_name(llpkgc_errno_t err) { + switch(err) { + case PCE_OK: + return "PCE_OK"; + case PCE_INTERNAL: + return "PCE_INTERNAL"; + case PCE_PAUSED: + return "PCE_PAUSED"; + case PCE_USER: + return "PCE_USER"; + case PCE_UNFINISHED: + return "PCE_UNFINISHED"; + } + return "INVALID_ERRNO"; +} + +int llpkgc__line_begin(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 1; + CALLBACK_MAYBE(s, on_line_begin); + return err; +} + +int llpkgc__key_span(llpkgc_t* s, const char* p, const char* endp) { + int err; + SPAN_CALLBACK_MAYBE(s, on_key, p, endp - p); + return err; +} + +int llpkgc__keyword_complete(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 3; + CALLBACK_MAYBE(s, on_keyword_complete); + return err; +} + +int llpkgc__variable_complete(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 3; + CALLBACK_MAYBE(s, on_variable_complete); + return err; +} + +int llpkgc__vallit_span(llpkgc_t* s, const char* p, const char* endp) { + int err; + if(s->escaped_) { + --endp; + s->escaped_ = 0; + } + s->unfinished_ = 2; + SPAN_CALLBACK_MAYBE(s, on_value_literal, p, endp - p); + return err; +} + +int llpkgc__vallit_complete(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 3; + CALLBACK_MAYBE(s, on_value_literal_complete); + return err; +} + +int llpkgc__valvar_span(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 1; + SPAN_CALLBACK_MAYBE(s, on_value_variable, p, endp - p); + return err; +} + +int llpkgc__valvar_complete(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 3; + CALLBACK_MAYBE(s, on_value_variable_complete); + return err; +} + +int llpkgc__value_complete(llpkgc_t* s, const char* p, const char* endp) { + int err; + s->unfinished_ = 0; + CALLBACK_MAYBE(s, on_value_complete); + return err; +} diff --git a/Utilities/cmllpkgc/llpkgc.h b/Utilities/cmllpkgc/llpkgc.h new file mode 100644 index 000000000..b7d0724e8 --- /dev/null +++ b/Utilities/cmllpkgc/llpkgc.h @@ -0,0 +1,143 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef INCLUDE_LLPKGC_API_H_ +#define INCLUDE_LLPKGC_API_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include "llpkgc__internal.h" + +#if defined(_MSC_VER) +#define LLPKGC_EXPORT __declspec(dllexport) +#else +#define LLPKGC_EXPORT __attribute__((visibility("default"))) +#endif + +typedef llpkgc__internal_t llpkgc_t; +typedef struct llpkgc_settings_s llpkgc_settings_t; + +typedef int (*llpkgc_data_cb)(llpkgc_t*, const char* at, size_t length); +typedef int (*llpkgc_cb)(llpkgc_t*); + +struct llpkgc_settings_s { + /* Possible return values 0, -1, PCE_USER */ + llpkgc_data_cb on_key; + llpkgc_data_cb on_value_literal; + llpkgc_data_cb on_value_variable; + + /* Possible return values 0, -1, `PCE_PAUSED` */ + llpkgc_cb on_line_begin; + llpkgc_cb on_keyword_complete; + llpkgc_cb on_variable_complete; + llpkgc_cb on_value_literal_complete; + llpkgc_cb on_value_variable_complete; + llpkgc_cb on_value_complete; + llpkgc_cb on_pkgc_complete; +}; + +enum llpkgc_errno { + PCE_OK = 0, + PCE_INTERNAL = 1, + PCE_PAUSED = 2, + PCE_USER = 3, + PCE_UNFINISHED = 4, +}; +typedef enum llpkgc_errno llpkgc_errno_t; + +/* Initialize the parser with user settings. + * + * NOTE: lifetime of `settings` has to be at least the same as the lifetime of + * the `parser` here. In practice, `settings` has to be either a static + * variable or be allocated with `malloc`, `new`, etc. + */ +LLPKGC_EXPORT +void llpkgc_init(llpkgc_t* parser, const llpkgc_settings_t* settings); + +/* Reset an already initialized parser back to the start state, preserving the + * existing callback settings and user data. + */ +LLPKGC_EXPORT +void llpkgc_reset(llpkgc_t* parser); + +/* Initialize the settings object */ +LLPKGC_EXPORT +void llpkgc_settings_init(llpkgc_settings_t* settings); + +/* Parse full or partial pc data, invoking user callbacks along the way. + * + * If any of `llpkgc_data_cb` returns errno not equal to `PCE_OK` - the parsing + * interrupts, and such errno is returned from `llpkgc_execute()`. If + * `PCE_PAUSED` was used as an errno, the execution can be resumed with + * `llpkgc_resume()` call. + * + * NOTE: if this function ever returns a non-pause type error, it will continue + * to return the same error upon each successive call up until `llpkgc_init()` + * or `llpkgc_reset()` are called. + */ +LLPKGC_EXPORT +llpkgc_errno_t llpkgc_execute(llpkgc_t* parser, const char* data, size_t len); + +/* This method should be called when the input has reached EOF + * + * This method will invoke `on_pkgc_complete()` callback if the file was + * terminated safely. Otherwise an error code will be returned. + */ +LLPKGC_EXPORT +llpkgc_errno_t llpkgc_finish(llpkgc_t* parser); + +/* Make further calls of `llpkgc_execute()` return `PCE_PAUSED` and set + * appropriate error reason. + * + * Important: do not call this from user callbacks! User callbacks must return + * `PCE_PAUSED` if pausing is required. + */ +LLPKGC_EXPORT +void llpkgc_pause(llpkgc_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llpkgc_execute()` above for details. + * + * Call this only if `llpkgc_execute()` returns `PCE_PAUSED`. + */ +LLPKGC_EXPORT +void llpkgc_resume(llpkgc_t* parser); + +/* Returns the latest return error */ +LLPKGC_EXPORT +llpkgc_errno_t llpkgc_get_errno(const llpkgc_t* parser); + +/* Returns the verbal explanation of the latest returned error. + * + * Note: User callback should set error reason when returning the error. See + * `llpkgc_set_error_reason()` for details. + */ +LLPKGC_EXPORT +const char* llpkgc_get_error_reason(const llpkgc_t* parser); + +/* Assign verbal description to the returned error. Must be called in user + * callbacks right before returning the errno. + * + * Note: `PCE_USER` error code might be useful in user callbacks. + */ +LLPKGC_EXPORT +void llpkgc_set_error_reason(llpkgc_t* parser, const char* reason); + +/* Returns the pointer to the last parsed byte before the returned error. The + * pointer is relative to the `data` argument of `llpkgc_execute()`. + * + * Note: this method might be useful for counting the number of parsed bytes. + */ +LLPKGC_EXPORT +const char* llpkgc_get_error_pos(const llpkgc_t* parser); + +/* Returns textual name of error code */ +LLPKGC_EXPORT +const char* llpkgc_errno_name(llpkgc_errno_t err); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLPKGC_API_H_ */ diff --git a/Utilities/cmllpkgc/llpkgc__internal.c b/Utilities/cmllpkgc/llpkgc__internal.c new file mode 100644 index 000000000..2eaba127c --- /dev/null +++ b/Utilities/cmllpkgc/llpkgc__internal.c @@ -0,0 +1,1069 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +/* This code was generated by llpkgc, do not edit it by hand + See: https://gitlab.kitware.com/utils/llpkgc */ + + +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + +#include "llpkgc__internal.h" + +typedef int (*llpkgc__internal__span_cb)( + llpkgc__internal_t*, const char*, const char*); + +enum llparse_state_e { + s_error, + s_n_llpkgc__internal__n_comment, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_1, + s_n_llpkgc__internal__n_literal_skip_dollar, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_2, + s_n_llpkgc__internal__n_maybe_CR, + s_n_llpkgc__internal__n_skip_escaped_LF, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_3, + s_n_llpkgc__internal__n_maybe_LF, + s_n_llpkgc__internal__n_skip_escaped_CR, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_4, + s_n_llpkgc__internal__n_literal_skip_hash, + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_1, + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_5, + s_n_llpkgc__internal__n_maybe_escaped, + s_n_llpkgc__internal__n_literal, + s_n_llpkgc__internal__n_variable_skip_dollar, + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span, + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2, + s_n_llpkgc__internal__n_variable_close, + s_n_llpkgc__internal__n_variable_skip_curly, + s_n_llpkgc__internal__n_invoke_llpkgc__valvar_complete, + s_n_llpkgc__internal__n_variable, + s_n_llpkgc__internal__n_span_start_llpkgc__valvar_span, + s_n_llpkgc__internal__n_maybe_variable, + s_n_llpkgc__internal__n_expect_value, + s_n_llpkgc__internal__n_expect_sep, + s_n_llpkgc__internal__n_key, + s_n_llpkgc__internal__n_span_start_llpkgc__key_span, + s_n_llpkgc__internal__n_line_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llpkgc__key_span( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__vallit_span( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__valvar_span( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__line_begin( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__keyword_complete( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__value_complete( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__vallit_complete( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__internal__c_update_escaped_( + llpkgc__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->escaped_ = 1; + return 0; +} + +int llpkgc__valvar_complete( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__variable_complete( + llpkgc__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llpkgc__internal_init(llpkgc__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_line_start; + return 0; +} + +static llparse_state_t llpkgc__internal__run( + llpkgc__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llpkgc__internal__n_comment: + s_n_llpkgc__internal__n_comment: { + if (p == endp) { + return s_n_llpkgc__internal__n_comment; + } + switch (*p) { + case 10: { + p++; + goto s_n_llpkgc__internal__n_line_start; + } + case 13: { + p++; + goto s_n_llpkgc__internal__n_line_start; + } + default: { + p++; + goto s_n_llpkgc__internal__n_comment; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete_1; + default: + goto s_n_llpkgc__internal__n_error_3; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_1: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_1: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete_2; + default: + goto s_n_llpkgc__internal__n_error_5; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_literal_skip_dollar: + s_n_llpkgc__internal__n_literal_skip_dollar: { + if (p == endp) { + return s_n_llpkgc__internal__n_literal_skip_dollar; + } + p++; + goto s_n_llpkgc__internal__n_maybe_variable; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_2: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_2: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_literal_skip_dollar; + default: + goto s_n_llpkgc__internal__n_error_7; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_maybe_CR: + s_n_llpkgc__internal__n_maybe_CR: { + if (p == endp) { + return s_n_llpkgc__internal__n_maybe_CR; + } + switch (*p) { + case 13: { + p++; + goto s_n_llpkgc__internal__n_expect_value; + } + default: { + goto s_n_llpkgc__internal__n_expect_value; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_skip_escaped_LF: + s_n_llpkgc__internal__n_skip_escaped_LF: { + if (p == endp) { + return s_n_llpkgc__internal__n_skip_escaped_LF; + } + p++; + goto s_n_llpkgc__internal__n_maybe_CR; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_3: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_3: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_skip_escaped_LF; + default: + goto s_n_llpkgc__internal__n_error_8; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_maybe_LF: + s_n_llpkgc__internal__n_maybe_LF: { + if (p == endp) { + return s_n_llpkgc__internal__n_maybe_LF; + } + switch (*p) { + case 10: { + p++; + goto s_n_llpkgc__internal__n_expect_value; + } + default: { + goto s_n_llpkgc__internal__n_expect_value; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_skip_escaped_CR: + s_n_llpkgc__internal__n_skip_escaped_CR: { + if (p == endp) { + return s_n_llpkgc__internal__n_skip_escaped_CR; + } + p++; + goto s_n_llpkgc__internal__n_maybe_LF; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_4: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_4: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_skip_escaped_CR; + default: + goto s_n_llpkgc__internal__n_error_9; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_literal_skip_hash: + s_n_llpkgc__internal__n_literal_skip_hash: { + if (p == endp) { + return s_n_llpkgc__internal__n_literal_skip_hash; + } + p++; + goto s_n_llpkgc__internal__n_literal; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_1: + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_1: { + if (p == endp) { + return s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llpkgc__vallit_span; + goto s_n_llpkgc__internal__n_literal_skip_hash; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_5: + s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_5: { + switch (llpkgc__vallit_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_1; + default: + goto s_n_llpkgc__internal__n_error_10; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_maybe_escaped: + s_n_llpkgc__internal__n_maybe_escaped: { + if (p == endp) { + return s_n_llpkgc__internal__n_maybe_escaped; + } + switch (*p) { + case 10: { + goto s_n_llpkgc__internal__n_invoke_update_escaped_; + } + case 13: { + goto s_n_llpkgc__internal__n_invoke_update_escaped__1; + } + case '#': { + goto s_n_llpkgc__internal__n_invoke_update_escaped__2; + } + default: { + goto s_n_llpkgc__internal__n_literal; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_literal: + s_n_llpkgc__internal__n_literal: { + if (p == endp) { + return s_n_llpkgc__internal__n_literal; + } + switch (*p) { + case 10: { + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span; + } + case 13: { + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span; + } + case '#': { + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_1; + } + case '$': { + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_2; + } + case 92: { + p++; + goto s_n_llpkgc__internal__n_maybe_escaped; + } + default: { + p++; + goto s_n_llpkgc__internal__n_literal; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_variable_skip_dollar: + s_n_llpkgc__internal__n_variable_skip_dollar: { + if (p == endp) { + return s_n_llpkgc__internal__n_variable_skip_dollar; + } + p++; + goto s_n_llpkgc__internal__n_literal; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span: + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span: { + if (p == endp) { + return s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llpkgc__vallit_span; + goto s_n_llpkgc__internal__n_variable_skip_dollar; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2: + s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2: { + if (p == endp) { + return s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llpkgc__vallit_span; + goto s_n_llpkgc__internal__n_literal; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_variable_close: + s_n_llpkgc__internal__n_variable_close: { + if (p == endp) { + return s_n_llpkgc__internal__n_variable_close; + } + switch (*p) { + case 10: { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case 13: { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case '#': { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case '$': { + p++; + goto s_n_llpkgc__internal__n_maybe_variable; + } + default: { + goto s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_variable_skip_curly: + s_n_llpkgc__internal__n_variable_skip_curly: { + if (p == endp) { + return s_n_llpkgc__internal__n_variable_skip_curly; + } + p++; + goto s_n_llpkgc__internal__n_variable_close; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_invoke_llpkgc__valvar_complete: + s_n_llpkgc__internal__n_invoke_llpkgc__valvar_complete: { + switch (llpkgc__valvar_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_variable_skip_curly; + default: + goto s_n_llpkgc__internal__n_error_11; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_variable: + s_n_llpkgc__internal__n_variable: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 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, 0, 0, 0, 0, 1, + 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, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llpkgc__internal__n_variable; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llpkgc__internal__n_variable; + } + case 2: { + goto s_n_llpkgc__internal__n_span_end_llpkgc__valvar_span; + } + default: { + goto s_n_llpkgc__internal__n_error_12; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_span_start_llpkgc__valvar_span: + s_n_llpkgc__internal__n_span_start_llpkgc__valvar_span: { + if (p == endp) { + return s_n_llpkgc__internal__n_span_start_llpkgc__valvar_span; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llpkgc__valvar_span; + goto s_n_llpkgc__internal__n_variable; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_maybe_variable: + s_n_llpkgc__internal__n_maybe_variable: { + if (p == endp) { + return s_n_llpkgc__internal__n_maybe_variable; + } + switch (*p) { + case '$': { + goto s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span; + } + case '{': { + p++; + goto s_n_llpkgc__internal__n_span_start_llpkgc__valvar_span; + } + default: { + goto s_n_llpkgc__internal__n_error_13; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_expect_value: + s_n_llpkgc__internal__n_expect_value: { + if (p == endp) { + return s_n_llpkgc__internal__n_expect_value; + } + switch (*p) { + case 9: { + p++; + goto s_n_llpkgc__internal__n_expect_value; + } + case 10: { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case 13: { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case ' ': { + p++; + goto s_n_llpkgc__internal__n_expect_value; + } + case '#': { + goto s_n_llpkgc__internal__n_invoke_llpkgc__value_complete; + } + case '$': { + p++; + goto s_n_llpkgc__internal__n_maybe_variable; + } + default: { + goto s_n_llpkgc__internal__n_span_start_llpkgc__vallit_span_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_expect_sep: + s_n_llpkgc__internal__n_expect_sep: { + if (p == endp) { + return s_n_llpkgc__internal__n_expect_sep; + } + switch (*p) { + case 9: { + p++; + goto s_n_llpkgc__internal__n_expect_sep; + } + case ' ': { + p++; + goto s_n_llpkgc__internal__n_expect_sep; + } + case ':': { + p++; + goto s_n_llpkgc__internal__n_invoke_llpkgc__keyword_complete; + } + case '=': { + p++; + goto s_n_llpkgc__internal__n_invoke_llpkgc__variable_complete; + } + default: { + goto s_n_llpkgc__internal__n_error_15; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_key: + s_n_llpkgc__internal__n_key: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 1, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llpkgc__internal__n_key; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llpkgc__internal__n_span_end_llpkgc__key_span; + } + case 2: { + p++; + goto s_n_llpkgc__internal__n_key; + } + default: { + goto s_n_llpkgc__internal__n_error_16; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_span_start_llpkgc__key_span: + s_n_llpkgc__internal__n_span_start_llpkgc__key_span: { + if (p == endp) { + return s_n_llpkgc__internal__n_span_start_llpkgc__key_span; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llpkgc__key_span; + goto s_n_llpkgc__internal__n_key; + /* UNREACHABLE */; + abort(); + } + case s_n_llpkgc__internal__n_line_start: + s_n_llpkgc__internal__n_line_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llpkgc__internal__n_line_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llpkgc__internal__n_line_start; + } + case 2: { + p++; + goto s_n_llpkgc__internal__n_comment; + } + case 3: { + goto s_n_llpkgc__internal__n_invoke_llpkgc__line_begin; + } + default: { + goto s_n_llpkgc__internal__n_error_17; + } + } + /* UNREACHABLE */; + abort(); + } + default: + /* UNREACHABLE */ + abort(); + } + s_n_llpkgc__internal__n_error_2: { + state->error = 0xb; + state->reason = "Value complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__value_complete: { + switch (llpkgc__value_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_line_start; + default: + goto s_n_llpkgc__internal__n_error_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_4: { + state->error = 0xb; + state->reason = "Value complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__value_complete_1: { + switch (llpkgc__value_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_line_start; + default: + goto s_n_llpkgc__internal__n_error_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_3: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_6: { + state->error = 0xb; + state->reason = "Value complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__value_complete_2: { + switch (llpkgc__value_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_comment; + default: + goto s_n_llpkgc__internal__n_error_6; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_5: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_1; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_1; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_7: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_2; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_8: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_3; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_3; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_update_escaped_: { + switch (llpkgc__internal__c_update_escaped_(state, p, endp)) { + default: + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_9: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_4; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_4; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_update_escaped__1: { + switch (llpkgc__internal__c_update_escaped_(state, p, endp)) { + default: + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_10: { + state->error = 0xa; + state->reason = "Literal complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__vallit_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_5; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__vallit_complete_5; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_update_escaped__2: { + switch (llpkgc__internal__c_update_escaped_(state, p, endp)) { + default: + goto s_n_llpkgc__internal__n_span_end_llpkgc__vallit_span_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_11: { + state->error = 0xc; + state->reason = "Variable complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__valvar_span: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__valvar_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_invoke_llpkgc__valvar_complete; + return s_error; + } + goto s_n_llpkgc__internal__n_invoke_llpkgc__valvar_complete; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_12: { + state->error = 0xd; + state->reason = "Invalid variable character"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_13: { + state->error = 0x9; + state->reason = "Unexpected `$`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_1: { + state->error = 0x5; + state->reason = "Keyword complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__keyword_complete: { + switch (llpkgc__keyword_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_expect_value; + default: + goto s_n_llpkgc__internal__n_error_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_14: { + state->error = 0x6; + state->reason = "Variable complete error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__variable_complete: { + switch (llpkgc__variable_complete(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_expect_value; + default: + goto s_n_llpkgc__internal__n_error_14; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_15: { + state->error = 0x7; + state->reason = "Expected seperator"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_span_end_llpkgc__key_span: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llpkgc__key_span(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llpkgc__internal__n_expect_sep; + return s_error; + } + goto s_n_llpkgc__internal__n_expect_sep; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_16: { + state->error = 0x4; + state->reason = "Invalid key character"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error: { + state->error = 0x63; + state->reason = "Line start error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_invoke_llpkgc__line_begin: { + switch (llpkgc__line_begin(state, p, endp)) { + case 0: + goto s_n_llpkgc__internal__n_span_start_llpkgc__key_span; + default: + goto s_n_llpkgc__internal__n_error; + } + /* UNREACHABLE */; + abort(); + } + s_n_llpkgc__internal__n_error_17: { + state->error = 0x2; + state->reason = "Expected key"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } +} + +int llpkgc__internal_execute(llpkgc__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llpkgc__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llpkgc__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} diff --git a/Utilities/cmllpkgc/llpkgc__internal.h b/Utilities/cmllpkgc/llpkgc__internal.h new file mode 100644 index 000000000..34ec1d254 --- /dev/null +++ b/Utilities/cmllpkgc/llpkgc__internal.h @@ -0,0 +1,37 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +/* This code was generated by llpkgc, do not edit it by hand + See: https://gitlab.kitware.com/utils/llpkgc */ + + +#ifndef INCLUDE_LLPKGC__INTERNAL_H_ +#define INCLUDE_LLPKGC__INTERNAL_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct llpkgc__internal_s llpkgc__internal_t; +struct llpkgc__internal_s { + int32_t _index; + void* _span_pos0; + void* _span_cb0; + int32_t error; + const char* reason; + const char* error_pos; + void* data; + void* _current; + void* settings; + uint8_t unfinished_; + uint8_t escaped_; +}; + +int llpkgc__internal_init(llpkgc__internal_t* s); +int llpkgc__internal_execute(llpkgc__internal_t* s, const char* p, const char* endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLPKGC__INTERNAL_H_ */ diff --git a/Utilities/std/cmext/type_traits b/Utilities/std/cmext/type_traits index 4468e3106..57e1e0140 100644 --- a/Utilities/std/cmext/type_traits +++ b/Utilities/std/cmext/type_traits @@ -82,4 +82,30 @@ using is_sequence_container = !cm::is_associative_container::value && !cm::is_unordered_associative_container::value>; +template +struct remove_member_pointer +{ + typedef T type; +}; +template +struct remove_member_pointer +{ + typedef T type; +}; +template +using remove_member_pointer_t = typename remove_member_pointer::type; + +template +struct member_pointer_class +{ + typedef T type; +}; +template +struct member_pointer_class +{ + typedef T type; +}; +template +using member_pointer_class_t = typename member_pointer_class::type; + } // namespace cm diff --git a/bootstrap b/bootstrap index 88878fed1..53358d547 100755 --- a/bootstrap +++ b/bootstrap @@ -306,6 +306,7 @@ CMAKE_CXX_SOURCES="\ cmBlockCommand \ cmBreakCommand \ cmBuildCommand \ + cmBuildDatabase \ cmCMakeLanguageCommand \ cmCMakeMinimumRequired \ cmList \ @@ -345,8 +346,11 @@ CMAKE_CXX_SOURCES="\ cmExecuteProcessCommand \ cmExpandedCommandArgument \ cmExperimental \ + cmExportBuildCMakeConfigGenerator \ cmExportBuildFileGenerator \ + cmExportCMakeConfigGenerator \ cmExportFileGenerator \ + cmExportInstallCMakeConfigGenerator \ cmExportInstallFileGenerator \ cmExportSet \ cmExportTryCompileFileGenerator \ @@ -408,6 +412,7 @@ CMAKE_CXX_SOURCES="\ cmIncludeGuardCommand \ cmIncludeDirectoryCommand \ cmIncludeRegularExpressionCommand \ + cmInstallCMakeConfigExportGenerator \ cmInstallCommand \ cmInstallCommandArguments \ cmInstallCxxModuleBmiGenerator \ @@ -1763,7 +1768,7 @@ libs="" uv_c_flags="" if ${cmake_system_mingw}; then uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600" - libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32" + libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32 -luuid" else case "${cmake_system}" in *AIX*)