diff options
781 files changed, 11621 insertions, 10419 deletions
@@ -3,6 +3,10 @@ # Common build directories /build*/ +# CI jobs that run in symlinked trees produce these artifacts. +/real_work/ +/work + # MacOS Finder files. .DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7a66494..c13ba72 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,6 +18,10 @@ stages: - test-ext - upload +variables: + # Some jobs that place their artifacts in a different directory will override this. + CMAKE_CI_BUILD_DIR: build + ################################################################################ # Job declarations # @@ -195,6 +199,18 @@ t:debian12-makefiles-clang: variables: CMAKE_CI_JOB_NIGHTLY: "true" +t:debian12-ninja-multi-symlinked: + extends: + - .debian12_ninja_multi_symlinked + - .cmake_test_linux_release + - .linux_x86_64_tags + - .cmake_junit_artifacts + - .run_dependent + - .needs_centos7_x86_64 + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + CMAKE_CI_JOB_NIGHTLY_NINJA: "true" + t:debian12-hip-radeon: extends: - .debian12_hip_radeon @@ -334,6 +350,16 @@ t:cuda11.8-minimal-ninja: variables: CMAKE_CI_NO_MR: "true" +t:hip5.5-nvidia: + extends: + - .hip5.5_nvidia + - .cmake_test_linux_release + - .linux_x86_64_tags_cuda + - .run_dependent + - .needs_centos7_x86_64 + variables: + CMAKE_CI_NO_MR: "true" + t:hip5.5-radeon: extends: - .hip5.5_radeon @@ -385,6 +411,16 @@ b:fedora38-ninja: variables: CMAKE_CI_JOB_CONTINUOUS: "true" +b:fedora38-makefiles-symlinked: + extends: + - .fedora38_makefiles_symlinked + - .cmake_build_linux + - .cmake_build_artifacts + - .linux_x86_64_tags + - .run_manually + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:debian12-makefiles-inplace: extends: - .debian12_makefiles_inplace @@ -435,6 +471,20 @@ t:fedora38-ninja: variables: CMAKE_CI_JOB_CONTINUOUS: "true" +t:fedora38-makefiles-symlinked: + extends: + - .fedora38_makefiles_symlinked + - .cmake_test_linux + - .linux_x86_64_tags_x11 + - .cmake_test_artifacts + - .run_dependent + dependencies: + - b:fedora38-makefiles-symlinked + needs: + - b:fedora38-makefiles-symlinked + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + t:fedora38-ninja-multi: extends: - .fedora38_ninja_multi @@ -857,6 +907,16 @@ b:macos-arm64-ninja: variables: CMAKE_CI_JOB_CONTINUOUS: "true" +b:macos-arm64-ninja-symlinked: + extends: + - .macos_arm64_ninja_symlinked + - .cmake_build_macos + - .cmake_build_artifacts + - .macos_arm64_tags + - .run_manually + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + b:macos-arm64-pch: extends: - .macos_arm64_pch @@ -895,6 +955,21 @@ t:macos-arm64-ninja: CMAKE_CI_JOB_CONTINUOUS: "true" CMAKE_CI_JOB_NIGHTLY_NINJA: "true" +t:macos-arm64-ninja-symlinked: + extends: + - .macos_arm64_ninja_symlinked + - .cmake_test_macos + - .cmake_test_artifacts + - .macos_arm64_tags + - .run_dependent + dependencies: + - b:macos-arm64-ninja-symlinked + needs: + - b:macos-arm64-ninja-symlinked + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + CMAKE_CI_JOB_NIGHTLY_NINJA: "true" + b:macos-x86_64-makefiles: extends: - .macos_x86_64_makefiles @@ -954,6 +1029,20 @@ t:macos-arm64-xcode: needs: - t:macos-arm64-ninja +t:macos-arm64-xcode-symlinked: + extends: + - .macos_arm64_xcode_symlinked + - .cmake_test_macos_external + - .macos_arm64_tags_ext + - .cmake_junit_artifacts + - .run_dependent + dependencies: + - t:macos-arm64-ninja + needs: + - t:macos-arm64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + t:macos-x86_64-ninja-ub: extends: - .macos_x86_64_ninja_ub @@ -1157,7 +1246,7 @@ t:windows-borland5.8: variables: CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang16.0-cl-ninja: +t:windows-clang17.0-cl-ninja: extends: - .windows_clang_ninja - .cmake_test_windows_external @@ -1169,10 +1258,10 @@ t:windows-clang16.0-cl-ninja: needs: - t:windows-vs2022-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_ninja + CMAKE_CI_BUILD_NAME: windows_clang17.0_cl_ninja CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang16.0-cl-nmake: +t:windows-clang17.0-cl-nmake: extends: - .windows_clang_nmake - .cmake_test_windows_external @@ -1184,10 +1273,10 @@ t:windows-clang16.0-cl-nmake: needs: - t:windows-vs2022-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_nmake + CMAKE_CI_BUILD_NAME: windows_clang17.0_cl_nmake CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang16.0-gnu-ninja: +t:windows-clang17.0-gnu-ninja: extends: - .windows_clang_ninja - .cmake_test_windows_external @@ -1199,10 +1288,10 @@ t:windows-clang16.0-gnu-ninja: needs: - t:windows-vs2022-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_ninja + CMAKE_CI_BUILD_NAME: windows_clang17.0_gnu_ninja CMAKE_CI_JOB_NIGHTLY: "true" -t:windows-clang16.0-gnu-nmake: +t:windows-clang17.0-gnu-nmake: extends: - .windows_clang_nmake - .cmake_test_windows_external @@ -1214,7 +1303,7 @@ t:windows-clang16.0-gnu-nmake: needs: - t:windows-vs2022-x64-ninja variables: - CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_nmake + CMAKE_CI_BUILD_NAME: windows_clang17.0_gnu_nmake CMAKE_CI_JOB_NIGHTLY: "true" t:windows-intel2021.9.0-ninja: @@ -1303,6 +1392,20 @@ t:windows-openwatcom1.9: variables: CMAKE_CI_JOB_NIGHTLY: "true" +t:windows-orangec6.73.1: + extends: + - .windows_orangec6.73.1 + - .cmake_test_windows_external + - .windows_x86_64_tags_concurrent + - .cmake_junit_artifacts + - .run_dependent + dependencies: + - t:windows-vs2022-x64-ninja + needs: + - t:windows-vs2022-x64-ninja + variables: + CMAKE_CI_JOB_NIGHTLY: "true" + # Windows arm64 jobs b:windows-arm64-vs2022-ninja: diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore index 852dfa6..19796ef 100644 --- a/.gitlab/.gitignore +++ b/.gitlab/.gitignore @@ -12,6 +12,7 @@ /ninja* /openmp /open-watcom* +/orangec /python* /qt* /sccache* diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 6c4cc0d..d9943b8 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -5,63 +5,63 @@ expire_in: 1d paths: # Test specifications. - - build/**/CTestTestfile.cmake + - ${CMAKE_CI_BUILD_DIR}/**/CTestTestfile.cmake # Allow CMake to find CMAKE_ROOT. - - build/CMakeFiles/CMakeSourceDir.txt + - ${CMAKE_CI_BUILD_DIR}/CMakeFiles/CMakeSourceDir.txt # Take the install tree. - - build/install/ + - ${CMAKE_CI_BUILD_DIR}/install/ # We need the main binaries. - - build/bin/ + - ${CMAKE_CI_BUILD_DIR}/bin/ # The cache is needed for the installation test. - - build/CMakeCache.txt + - ${CMAKE_CI_BUILD_DIR}/CMakeCache.txt # Test binaries. Eventually these might be better under # `Source/Tests` or the like. - - build/Tests/EnforceConfig.cmake - - build/Tests/CMakeBuildTest.cmake - - build/Tests/CMakeBuildDoubleProjectTest.cmake - - build/Tests/CMake*/runcompilecommands - - build/Tests/CMake*/runcompilecommands.exe - - build/Tests/CMake*/test* - - build/Tests/CMake*/PseudoMemcheck/valgrind - - build/Tests/CMake*/PseudoMemcheck/purify - - build/Tests/CMake*/PseudoMemcheck/memcheck_fail - - build/Tests/CMake*/PseudoMemcheck/BC - - build/Tests/CMake*/PseudoMemcheck/cuda-memcheck - - build/Tests/CMake*/PseudoMemcheck/valgrind.exe - - build/Tests/CMake*/PseudoMemcheck/purify.exe - - build/Tests/CMake*/PseudoMemcheck/memcheck_fail.exe - - build/Tests/CMake*/PseudoMemcheck/BC.exe - - build/Tests/CMake*/PseudoMemcheck/cuda-memcheck.exe - - build/Tests/CMake*/PseudoMemcheck/NoLog - - build/Tests/CMake*Lib/*LibTests - - build/Tests/CMake*Lib/*LibTests.exe - - build/Source/kwsys/cmsysTest* - - build/Source/kwsys/testConsoleBufChild.exe - - build/Utilities/cmcurl/curltest - - build/Utilities/cmcurl/curltest.exe - - build/Utilities/KWIML/test/kwiml_test - - build/Utilities/KWIML/test/kwiml_test.exe - - build/Source/kwsys/*cmsysTestDynload.* - - build/Source/kwsys/dynloaddir/cmsysTestDynloadImpl.dll - - build/Source/kwsys/dynloaddir/cmsysTestDynloadUse.dll + - ${CMAKE_CI_BUILD_DIR}/Tests/EnforceConfig.cmake + - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeBuildTest.cmake + - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeBuildDoubleProjectTest.cmake + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/runcompilecommands + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/runcompilecommands.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/test* + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/valgrind + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/purify + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/memcheck_fail + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/BC + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/cuda-memcheck + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/valgrind.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/purify.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/memcheck_fail.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/BC.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/cuda-memcheck.exe + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/NoLog + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests + - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests.exe + - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/cmsysTest* + - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/testConsoleBufChild.exe + - ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest + - ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest.exe + - ${CMAKE_CI_BUILD_DIR}/Utilities/KWIML/test/kwiml_test + - ${CMAKE_CI_BUILD_DIR}/Utilities/KWIML/test/kwiml_test.exe + - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/*cmsysTestDynload.* + - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/dynloaddir/cmsysTestDynloadImpl.dll + - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/dynloaddir/cmsysTestDynloadUse.dll # Test directories. - - build/Tests/CTest* - - build/Tests/Find* - - build/Tests/Qt* - - build/Tests/RunCMake/ - - build/Tests/CMakeOnly/ - - build/Tests/CMakeTests/ - - build/Tests/CMakeGUI/ - - build/Tests/FortranC/ + - ${CMAKE_CI_BUILD_DIR}/Tests/CTest* + - ${CMAKE_CI_BUILD_DIR}/Tests/Find* + - ${CMAKE_CI_BUILD_DIR}/Tests/Qt* + - ${CMAKE_CI_BUILD_DIR}/Tests/RunCMake/ + - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeOnly/ + - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeTests/ + - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeGUI/ + - ${CMAKE_CI_BUILD_DIR}/Tests/FortranC/ # CTest/CDash information. - - build/Testing/ - - build/DartConfiguation.tcl - - build/CTestCustom.cmake + - ${CMAKE_CI_BUILD_DIR}/Testing/ + - ${CMAKE_CI_BUILD_DIR}/DartConfiguation.tcl + - ${CMAKE_CI_BUILD_DIR}/CTestCustom.cmake .cmake_release_artifacts: artifacts: @@ -71,17 +71,17 @@ when: always paths: # Any packages made. - - build/cmake-*-linux-x86_64.* - - build/cmake-*-linux-aarch64.* - - build/cmake-*-macos*-universal.* - - build/cmake-*-windows-x86_64.* - - build/cmake-*-windows-i386.* - - build/cmake-*-windows-arm64.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-linux-x86_64.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-linux-aarch64.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-macos*-universal.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-x86_64.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-i386.* + - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-arm64.* # Any source packages made. - - build/cmake-*.tar.gz - - build/cmake-*.zip + - ${CMAKE_CI_BUILD_DIR}/cmake-*.tar.gz + - ${CMAKE_CI_BUILD_DIR}/cmake-*.zip # Any unsigned packages made. - - build/unsigned/cmake-* + - ${CMAKE_CI_BUILD_DIR}/unsigned/cmake-* .cmake_junit_artifacts: artifacts: @@ -89,7 +89,7 @@ when: always reports: junit: - - build/junit.xml + - ${CMAKE_CI_BUILD_DIR}/junit.xml .cmake_sphinx_artifacts: artifacts: @@ -97,8 +97,8 @@ when: always paths: # Take the sphinx logs. - - build/build-*.log - - build/linkcheck/output.* + - ${CMAKE_CI_BUILD_DIR}/build-*.log + - ${CMAKE_CI_BUILD_DIR}/linkcheck/output.* .cmake_test_artifacts: artifacts: @@ -107,22 +107,22 @@ when: always reports: junit: - - build/junit.xml + - ${CMAKE_CI_BUILD_DIR}/junit.xml paths: # Take the install tree. - - build/install/ + - ${CMAKE_CI_BUILD_DIR}/install/ .cmake_doc_artifacts: artifacts: expire_in: 1d paths: # Take the install tree. - - build/install-doc/ + - ${CMAKE_CI_BUILD_DIR}/install-doc/ .cmake_org_help_artifacts: artifacts: expire_in: 1d paths: - - build/html + - ${CMAKE_CI_BUILD_DIR}/html exclude: - - build/html/.buildinfo + - ${CMAKE_CI_BUILD_DIR}/html/.buildinfo diff --git a/.gitlab/ci/clang.ps1 b/.gitlab/ci/clang.ps1 index 1fc8d8e..e455ebc 100755 --- a/.gitlab/ci/clang.ps1 +++ b/.gitlab/ci/clang.ps1 @@ -1,10 +1,10 @@ $erroractionpreference = "stop" -if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang16.0")) { - # LLVM/Clang 16.0 - # https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0 - $filename = "llvm-16.0.0-win-x86_64-1" - $sha256sum = "13F48356BA5892A82E8BB25EB283FDDAA8F23A0F209B6BF6525D2C5E1285B950" +if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang17.0")) { + # LLVM/Clang 17.0.1 + # https://github.com/llvm/llvm-project/releases/tag/llvmorg-17.0.1 + $filename = "llvm-17.0.1-win-x86_64-1" + $sha256sum = "803F5D7291219BE60D2EE69CE8882341F94A8707A214DED190614895B6996F55" } else { throw ('unknown CMAKE_CI_BUILD_NAME: ' + "$env:CMAKE_CI_BUILD_NAME") } diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 index 3efb67a..f6b5cc7 100755 --- a/.gitlab/ci/cmake.ps1 +++ b/.gitlab/ci/cmake.ps1 @@ -1,12 +1,12 @@ $erroractionpreference = "stop" -$version = "3.24.1" +$version = "3.27.6" if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { - $sha256sum = "C1B17431A16337D517F7BA78C7067B6F143A12686CB8087F3DD32F3FA45F5AAE" + $sha256sum = "F013A0CCA091AA953F9A60A99292EC7A20AE3F9CEB05CB5C08EBE164097C526F" $platform = "windows-x86_64" } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { - $sha256sum = "D94683F3B0E63F6EF194C621194F6E26F3735EDA70750395E0F2BBEE4023FB95" + $sha256sum = "980B7E532D77BFB4E5814C76403242C503019F1C0699440220CF2D527C8FF350" $platform = "windows-arm64" } else { throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE") diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh index 137da06..21da466b 100755 --- a/.gitlab/ci/cmake.sh +++ b/.gitlab/ci/cmake.sh @@ -2,22 +2,22 @@ set -e -readonly version="3.24.1" +readonly version="3.27.6" case "$(uname -s)-$(uname -m)" in Linux-x86_64) shatool="sha256sum" - sha256sum="827bf068cfaa23a9fb95f990c9f8a7ed8f2caeb3af62b5c0a2fed7a8dd6dde3e" + sha256sum="26373a283daa8490d772dc8a179450cd6d391cb2a9db8d4242fe09e361efc42e" platform="linux-x86_64" ;; Linux-aarch64) shatool="sha256sum" - sha256sum="d50c40135df667ed659f8e4eb7cf7d53421250304f7b3e1a70af9cf3d0f2ab18" + sha256sum="811e5040ad7f3fb4924a875373d2a1a174a01400233a81a638a989157438a5e3" platform="linux-aarch64" ;; Darwin-*) shatool="shasum -a 256" - sha256sum="71bb8db69826d74c395a3c3bbf8b773dbe9f54a2c7331266ba70da303e9c97a1" + sha256sum="a66b497289ab8c769b601d93833448eaae985beb762993837a51a79916d12f23" platform="macos-universal" ;; *) diff --git a/.gitlab/ci/configure_debian12_hip_radeon.cmake b/.gitlab/ci/configure_debian12_hip_radeon.cmake index 58036b0..c7d7004 100644 --- a/.gitlab/ci/configure_debian12_hip_radeon.cmake +++ b/.gitlab/ci/configure_debian12_hip_radeon.cmake @@ -1,3 +1,3 @@ -set(CMake_TEST_HIP "ON" CACHE BOOL "") +set(CMake_TEST_HIP "amd" CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_debian12_ninja.cmake b/.gitlab/ci/configure_debian12_ninja.cmake index f70e3d5..89c1108 100644 --- a/.gitlab/ci/configure_debian12_ninja.cmake +++ b/.gitlab/ci/configure_debian12_ninja.cmake @@ -1,110 +1,2 @@ -set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "") -set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "") -set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "") -set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "") -set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "") -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "") -endif() - -set(CMake_TEST_FindALSA "ON" CACHE BOOL "") -set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "") -set(CMake_TEST_FindBoost "ON" CACHE BOOL "") -set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "") -set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "") -set(CMake_TEST_FindCups "ON" CACHE BOOL "") -set(CMake_TEST_FindCURL "ON" CACHE BOOL "") -set(CMake_TEST_FindDevIL "ON" CACHE BOOL "") -set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "") -set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "") -set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "") -set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "") -set(CMake_TEST_FindFreetype "ON" CACHE BOOL "") -set(CMake_TEST_FindGDAL "ON" CACHE BOOL "") -set(CMake_TEST_FindGIF "ON" CACHE BOOL "") -set(CMake_TEST_FindGit "ON" CACHE BOOL "") -set(CMake_TEST_FindGLEW "ON" CACHE BOOL "") -set(CMake_TEST_FindGLUT "ON" CACHE BOOL "") -set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "") -set(CMake_TEST_FindGSL "ON" CACHE BOOL "") -set(CMake_TEST_FindGTest "ON" CACHE BOOL "") -set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "") -set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "") -set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "") -set(CMake_TEST_FindIconv "ON" CACHE BOOL "") -set(CMake_TEST_FindICU "ON" CACHE BOOL "") -set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "") -set(CMake_TEST_FindIntl "ON" CACHE BOOL "") -set(CMake_TEST_FindJNI "ON" CACHE BOOL "") -set(CMake_TEST_FindJPEG "ON" CACHE BOOL "") -set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "") -set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "") -set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "") -set(CMake_TEST_FindLibinput "ON" CACHE BOOL "") -set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "") -set(CMake_TEST_FindLibUV "ON" CACHE BOOL "") -set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "") -set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "") -set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "") -set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "") -set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "") -set(CMake_TEST_FindMPI "ON" CACHE BOOL "") -set(CMake_TEST_FindODBC "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "") -set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "") -set(CMake_TEST_FindPatch "ON" CACHE BOOL "") -set(CMake_TEST_FindPNG "ON" CACHE BOOL "") -set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "") -set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "") -set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "") -set(CMake_TEST_FindPython3 "ON" CACHE BOOL "") -set(CMake_TEST_FindPython3_IronPython "ON" CACHE BOOL "") -set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "") -set(CMake_TEST_FindRuby "ON" CACHE BOOL "") -set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "") -set(CMake_TEST_FindSDL "ON" CACHE BOOL "") -set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "") -set(CMake_TEST_FindTIFF "ON" CACHE BOOL "") -set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "") -set(CMake_TEST_FindX11 "ON" CACHE BOOL "") -set(CMake_TEST_FindXalanC "ON" CACHE BOOL "") -set(CMake_TEST_FindXercesC "ON" CACHE BOOL "") -set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "") -set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "") -set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "") -set(CMake_TEST_Qt5 "ON" CACHE BOOL "") -set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_debian12_ninja_common.cmake") set(CMake_TEST_UseSWIG "ON" CACHE BOOL "") - -if (NOT "$ENV{SWIFTC}" STREQUAL "") - set(CMAKE_Swift_COMPILER "$ENV{SWIFTC}" CACHE FILEPATH "") -endif() - -if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") - set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "") -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_debian12_ninja_common.cmake b/.gitlab/ci/configure_debian12_ninja_common.cmake new file mode 100644 index 0000000..d4d117b --- /dev/null +++ b/.gitlab/ci/configure_debian12_ninja_common.cmake @@ -0,0 +1,109 @@ +set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "") +set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "") +set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "") +set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "") +set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "") +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "") +endif() + +set(CMake_TEST_FindALSA "ON" CACHE BOOL "") +set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "") +set(CMake_TEST_FindBoost "ON" CACHE BOOL "") +set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "") +set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "") +set(CMake_TEST_FindCups "ON" CACHE BOOL "") +set(CMake_TEST_FindCURL "ON" CACHE BOOL "") +set(CMake_TEST_FindDevIL "ON" CACHE BOOL "") +set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "") +set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "") +set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "") +set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "") +set(CMake_TEST_FindFreetype "ON" CACHE BOOL "") +set(CMake_TEST_FindGDAL "ON" CACHE BOOL "") +set(CMake_TEST_FindGIF "ON" CACHE BOOL "") +set(CMake_TEST_FindGit "ON" CACHE BOOL "") +set(CMake_TEST_FindGLEW "ON" CACHE BOOL "") +set(CMake_TEST_FindGLUT "ON" CACHE BOOL "") +set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "") +set(CMake_TEST_FindGSL "ON" CACHE BOOL "") +set(CMake_TEST_FindGTest "ON" CACHE BOOL "") +set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "") +set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "") +set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "") +set(CMake_TEST_FindIconv "ON" CACHE BOOL "") +set(CMake_TEST_FindICU "ON" CACHE BOOL "") +set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "") +set(CMake_TEST_FindIntl "ON" CACHE BOOL "") +set(CMake_TEST_FindJNI "ON" CACHE BOOL "") +set(CMake_TEST_FindJPEG "ON" CACHE BOOL "") +set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "") +set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "") +set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "") +set(CMake_TEST_FindLibinput "ON" CACHE BOOL "") +set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "") +set(CMake_TEST_FindLibUV "ON" CACHE BOOL "") +set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "") +set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "") +set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "") +set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "") +set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "") +set(CMake_TEST_FindMPI "ON" CACHE BOOL "") +set(CMake_TEST_FindODBC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "") +set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "") +set(CMake_TEST_FindPatch "ON" CACHE BOOL "") +set(CMake_TEST_FindPNG "ON" CACHE BOOL "") +set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "") +set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "") +set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "") +set(CMake_TEST_FindPython3 "ON" CACHE BOOL "") +set(CMake_TEST_FindPython3_IronPython "ON" CACHE BOOL "") +set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "") +set(CMake_TEST_FindRuby "ON" CACHE BOOL "") +set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "") +set(CMake_TEST_FindSDL "ON" CACHE BOOL "") +set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "") +set(CMake_TEST_FindTIFF "ON" CACHE BOOL "") +set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "") +set(CMake_TEST_FindX11 "ON" CACHE BOOL "") +set(CMake_TEST_FindXalanC "ON" CACHE BOOL "") +set(CMake_TEST_FindXercesC "ON" CACHE BOOL "") +set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "") +set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "") +set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "") +set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "") +set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "") +set(CMake_TEST_Qt5 "ON" CACHE BOOL "") +set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "") + +if (NOT "$ENV{SWIFTC}" STREQUAL "") + set(CMAKE_Swift_COMPILER "$ENV{SWIFTC}" CACHE FILEPATH "") +endif() + +if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "") + set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "") +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake b/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake new file mode 100644 index 0000000..cf9202d --- /dev/null +++ b/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake @@ -0,0 +1,3 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_debian12_ninja_common.cmake") +set(CMake_TEST_UseSWIG "OFF" CACHE BOOL "") diff --git a/.gitlab/ci/configure_fedora38_hip_radeon.cmake b/.gitlab/ci/configure_fedora38_hip_radeon.cmake index 58036b0..c7d7004 100644 --- a/.gitlab/ci/configure_fedora38_hip_radeon.cmake +++ b/.gitlab/ci/configure_fedora38_hip_radeon.cmake @@ -1,3 +1,3 @@ -set(CMake_TEST_HIP "ON" CACHE BOOL "") +set(CMake_TEST_HIP "amd" CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake new file mode 100644 index 0000000..177bbcf --- /dev/null +++ b/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common.cmake") diff --git a/.gitlab/ci/configure_hip5.5_nvidia.cmake b/.gitlab/ci/configure_hip5.5_nvidia.cmake new file mode 100644 index 0000000..4b3511a --- /dev/null +++ b/.gitlab/ci/configure_hip5.5_nvidia.cmake @@ -0,0 +1,3 @@ +set(CMake_TEST_HIP "nvidia" CACHE BOOL "") + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_hip5.5_radeon.cmake b/.gitlab/ci/configure_hip5.5_radeon.cmake index 58036b0..c7d7004 100644 --- a/.gitlab/ci/configure_hip5.5_radeon.cmake +++ b/.gitlab/ci/configure_hip5.5_radeon.cmake @@ -1,3 +1,3 @@ -set(CMake_TEST_HIP "ON" CACHE BOOL "") +set(CMake_TEST_HIP "amd" CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake b/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake new file mode 100644 index 0000000..63d0dc7 --- /dev/null +++ b/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_arm64_ninja.cmake") diff --git a/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake b/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake new file mode 100644 index 0000000..66b18e1 --- /dev/null +++ b/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake @@ -0,0 +1,2 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_arm64_xcode.cmake") diff --git a/.gitlab/ci/configure_mingw_osdn_io_common.cmake b/.gitlab/ci/configure_mingw_osdn_io_common.cmake index 55dce1d..d316233 100644 --- a/.gitlab/ci/configure_mingw_osdn_io_common.cmake +++ b/.gitlab/ci/configure_mingw_osdn_io_common.cmake @@ -1,5 +1,8 @@ set(CMake_TEST_Java OFF CACHE BOOL "") +get_filename_component(mingw_dir "${CMAKE_CURRENT_LIST_DIR}/../mingw" ABSOLUTE) +set(CMake_TEST_MSYSTEM_PREFIX "${mingw_dir}" CACHE STRING "") + set(configure_no_sccache 1) include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/configure_symlinked_common.cmake b/.gitlab/ci/configure_symlinked_common.cmake new file mode 100644 index 0000000..a5e427e --- /dev/null +++ b/.gitlab/ci/configure_symlinked_common.cmake @@ -0,0 +1,7 @@ +# Ensure that the symlink tree is set up correctly. +if(NOT CMAKE_SOURCE_DIR STREQUAL "$ENV{CI_PROJECT_DIR}/work/cmake") + message(FATAL_ERROR "Expected value of CMAKE_SOURCE_DIR:\n $ENV{CI_PROJECT_DIR}/work/cmake\nActual value:\n ${CMAKE_SOURCE_DIR}") +endif() +if(NOT CMAKE_BINARY_DIR STREQUAL "$ENV{CI_PROJECT_DIR}/work/build") + message(FATAL_ERROR "Expected value of CMAKE_BINARY_DIR:\n $ENV{CI_PROJECT_DIR}/work/build\nActual value:\n ${CMAKE_BINARY_DIR}") +endif() diff --git a/.gitlab/ci/configure_windows_orangec6.73.1.cmake b/.gitlab/ci/configure_windows_orangec6.73.1.cmake new file mode 100644 index 0000000..e667b94 --- /dev/null +++ b/.gitlab/ci/configure_windows_orangec6.73.1.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_orangec_common.cmake") diff --git a/.gitlab/ci/configure_windows_orangec_common.cmake b/.gitlab/ci/configure_windows_orangec_common.cmake new file mode 100644 index 0000000..55dce1d --- /dev/null +++ b/.gitlab/ci/configure_windows_orangec_common.cmake @@ -0,0 +1,5 @@ +set(CMake_TEST_Java OFF CACHE BOOL "") + +set(configure_no_sccache 1) + +include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake") diff --git a/.gitlab/ci/cxx_modules_rules_gcc.cmake b/.gitlab/ci/cxx_modules_rules_gcc.cmake index 020cb1f..2b09b0e 100644 --- a/.gitlab/ci/cxx_modules_rules_gcc.cmake +++ b/.gitlab/ci/cxx_modules_rules_gcc.cmake @@ -1,20 +1 @@ set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3") - -string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE - "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>" - " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>" - " -fmodules-ts -fdeps-file=<DYNDEP_FILE> -fdeps-target=<OBJECT> -fdeps-format=p1689r5" - " -o <PREPROCESSED_SOURCE>") -set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc") -string(CONCAT CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG - # Turn on modules. - "-fmodules-ts" - # Read the module mapper file. - " -fmodule-mapper=<MODULE_MAP_FILE>" - # Make sure dependency tracking is enabled (missing from `try_*`). - " -MD" - # Suppress `CXX_MODULES +=` from generated depfile snippets. - " -fdeps-format=p1689r5" - # Force C++ as a language. - " -x c++") -set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG "-fmodule-only") diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh index 529626e..15cfe39 100755 --- a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh +++ b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh @@ -2,8 +2,8 @@ set -e -readonly revision="p1689r5-cmake-ci-20230814" # 9fd54ccc390ab4eb3c48186b7bf15e02632cc76c -readonly tarball="https://github.com/mathstuf/gcc/archive/$revision.tar.gz" +readonly revision="29862e21f6d656eca59284c927d0c4c0698eb99c" # master as of 21 Sep 2023 +readonly tarball="git://gcc.gnu.org/git/gcc.git" readonly workdir="$HOME/gcc" readonly srcdir="$workdir/gcc" @@ -12,9 +12,8 @@ readonly njobs="$( nproc )" mkdir -p "$workdir" cd "$workdir" -curl -L "$tarball" > "gcc-$revision.tar.gz" -tar xf "gcc-$revision.tar.gz" -mv "gcc-$revision" "$srcdir" +git clone "$tarball" "$srcdir" +git -C "$srcdir" checkout "$revision" mkdir -p "$builddir" cd "$builddir" "$srcdir/configure" \ diff --git a/.gitlab/ci/docker/hip5.5/Dockerfile b/.gitlab/ci/docker/hip5.5/Dockerfile index 2deb3c6..3a4aa53 100644 --- a/.gitlab/ci/docker/hip5.5/Dockerfile +++ b/.gitlab/ci/docker/hip5.5/Dockerfile @@ -2,7 +2,13 @@ ARG BASE_IMAGE=rocm/dev-ubuntu-22.04:5.5 -FROM ${BASE_IMAGE} AS apt-cache +FROM ${BASE_IMAGE} AS cuda-keyring +ADD https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb /root/ +RUN --mount=type=tmpfs,target=/var/log \ + dpkg -i /root/cuda-keyring_1.1-1_all.deb \ + && rm /root/cuda-keyring_1.1-1_all.deb + +FROM cuda-keyring AS apt-cache # Populate APT cache w/ the fresh metadata and prefetch packages. # Use an empty `docker-clean` file to "hide" the image-provided # file to disallow removing packages after `apt-get` operations. @@ -12,9 +18,12 @@ RUN --mount=type=tmpfs,target=/var/log \ apt-get update \ && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst) -FROM ${BASE_IMAGE} +FROM cuda-keyring MAINTAINER Brad King <brad.king@kitware.com> +ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility +ENV NVIDIA_REQUIRE_CUDA=cuda>=11.8 +ENV NVIDIA_VISIBLE_DEVICES=all ENV PATH="/opt/rocm/bin:$PATH" RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \ diff --git a/.gitlab/ci/docker/hip5.5/deps_packages.lst b/.gitlab/ci/docker/hip5.5/deps_packages.lst index 9847925..3276055 100644 --- a/.gitlab/ci/docker/hip5.5/deps_packages.lst +++ b/.gitlab/ci/docker/hip5.5/deps_packages.lst @@ -2,3 +2,20 @@ g++ curl git + +# NVIDIA CUDA Compiler +cuda-keyring +cuda-nvcc-11-8 +cuda-profiler-api-11-8 + +# NVIDIA CUDA Toolkit +# These are not needed for HIP, but having them in +# the environment allows us to run CUDA tests too. +cuda-nvrtc-dev-11-8 +cuda-nvtx-11-8 +libcublas-dev-11-8 +libcufft-dev-11-8 +libcurand-dev-11-8 +libcusolver-dev-11-8 +libcusparse-dev-11-8 +libnpp-dev-11-8 diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake index b02ceb0..28a3e27 100644 --- a/.gitlab/ci/download_qt.cmake +++ b/.gitlab/ci/download_qt.cmake @@ -15,11 +15,11 @@ if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows.*package") set(qt_url_root "https://cmake.org/files/dependencies") set(qt_url_path "") if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_x86_64_package") - list(APPEND qt_files "qt-5.12.1-win-x86_64-msvc_v142-1.zip") - set(qt_subdir "qt-5.12.1-win-x86_64-msvc_v142-1") + list(APPEND qt_files "qt-5.15.10-win-x86_64-msvc_v142-1.zip") + set(qt_subdir "qt-5.15.10-win-x86_64-msvc_v142-1") elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_i386_package") - list(APPEND qt_files "qt-5.12.1-win-i386-msvc_v142-1.zip") - set(qt_subdir "qt-5.12.1-win-i386-msvc_v142-1") + list(APPEND qt_files "qt-5.15.10-win-i386-msvc_v142-1.zip") + set(qt_subdir "qt-5.15.10-win-i386-msvc_v142-1") elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_arm64_package") list(APPEND qt_files "qt-6.3.0-win-arm64-msvc_v143-1.zip") set(qt_subdir "qt-6.3.0-win-arm64-msvc_v143-1") diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake index 4c48a47..1b9cf14 100644 --- a/.gitlab/ci/download_qt_hashes.cmake +++ b/.gitlab/ci/download_qt_hashes.cmake @@ -13,8 +13,8 @@ set("5.15.1-0-202009071110qtbase-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_6 set("qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz_hash" d4449771afa0bc6a809c14f1e6d939e7732494cf059503ae451e2bfe8fc60cc1) set("qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz_hash" 7b9463a01c8beeee5bf8d01c70deff2d08561cd20aaf6f7a2f41cf8b68ce8a6b) -set("qt-5.12.1-win-i386-msvc_v142-1.zip_hash" aa78711fdaa5d9b146bf7ddcf15983f9fbb3f995462f2d043f8cca74b40ddd11) -set("qt-5.12.1-win-x86_64-msvc_v142-1.zip_hash" c2fc068b9dac40bb420e28e1ee15ce4f2ccfc866d767f3b99b6bb435b7c4f44b) +set("qt-5.15.10-win-i386-msvc_v142-1.zip_hash" c158cebc054d3f4f09733772a8a04789e2884912d45782e8c0c5e6a0b2773e92) +set("qt-5.15.10-win-x86_64-msvc_v142-1.zip_hash" d55c017aef359f6aa8c592b18ba13cc120c749417b55671548970690126cd139) set("qt-6.3.0-win-arm64-msvc_v143-1.zip_hash" f794c035fd4ff9f04468e1787a60d93d7496119c0060c3173a76d24a6b551b14) set("qt-6.3.0-win-i386-msvc_v143-1.zip_hash" 972bc707f78d11b44f360643ca4d0c898e761f7add43b96117d958c70d84a443) diff --git a/.gitlab/ci/env.sh b/.gitlab/ci/env.sh index 7634f5d..aa709a8 100644 --- a/.gitlab/ci/env.sh +++ b/.gitlab/ci/env.sh @@ -9,6 +9,16 @@ quietly() { rm -f "$log" } +if test -n "$CMAKE_CI_IN_SYMLINK_TREE"; then + mkdir -p "$CI_PROJECT_DIR/real_work/work/build" + ln -s real_work/work "$CI_PROJECT_DIR/work" + git worktree prune + git worktree add "$CI_PROJECT_DIR/work/cmake" HEAD + + # Assert that the hash matches. + test "$(git -C "$CI_PROJECT_DIR/work/cmake" rev-parse HEAD)" = "$(git -C "$CI_PROJECT_DIR" rev-parse HEAD)" +fi + if test -r ".gitlab/ci/env_${CMAKE_CONFIGURATION}.sh"; then source ".gitlab/ci/env_${CMAKE_CONFIGURATION}.sh" fi diff --git a/.gitlab/ci/env_debian12_ninja.sh b/.gitlab/ci/env_debian12_ninja.sh index b2b471d..2b8ff2a 100644 --- a/.gitlab/ci/env_debian12_ninja.sh +++ b/.gitlab/ci/env_debian12_ninja.sh @@ -1,11 +1,5 @@ export MY_RUBY_HOME="/usr/local/rvm/rubies/ruby-3.2.2" if test -z "$CI_MERGE_REQUEST_ID"; then - curl -L -O "https://download.swift.org/swift-5.7.1-release/ubuntu1804/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu18.04.tar.gz" - echo '2b30f9efc969d9e96f0836d0871130dffb369822a3823ee6f3db44c29c1698e3 swift-5.7.1-RELEASE-ubuntu18.04.tar.gz' > swift.sha256sum - sha256sum --check swift.sha256sum - mkdir /opt/swift - tar xzf swift-5.7.1-RELEASE-ubuntu18.04.tar.gz -C /opt/swift --strip-components=2 - rm swift-5.7.1-RELEASE-ubuntu18.04.tar.gz swift.sha256sum - export SWIFTC="/opt/swift/bin/swiftc" + source .gitlab/ci/swift-env.sh fi diff --git a/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh b/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh new file mode 100644 index 0000000..2b8ff2a --- /dev/null +++ b/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh @@ -0,0 +1,5 @@ +export MY_RUBY_HOME="/usr/local/rvm/rubies/ruby-3.2.2" + +if test -z "$CI_MERGE_REQUEST_ID"; then + source .gitlab/ci/swift-env.sh +fi diff --git a/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake new file mode 100644 index 0000000..2accabf --- /dev/null +++ b/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/env_fedora38_makefiles.cmake") diff --git a/.gitlab/ci/env_fedora38_makefiles_symlinked.sh b/.gitlab/ci/env_fedora38_makefiles_symlinked.sh new file mode 100644 index 0000000..452d3785 --- /dev/null +++ b/.gitlab/ci/env_fedora38_makefiles_symlinked.sh @@ -0,0 +1 @@ +. .gitlab/ci/env_fedora38_makefiles.sh diff --git a/.gitlab/ci/env_hip5.5_nvidia.sh b/.gitlab/ci/env_hip5.5_nvidia.sh new file mode 100644 index 0000000..67d1ef2 --- /dev/null +++ b/.gitlab/ci/env_hip5.5_nvidia.sh @@ -0,0 +1,4 @@ +export HIP_PLATFORM=nvidia +export CUDA_PATH=/usr/local/cuda-11.8 +export PATH=/usr/local/cuda-11.8/bin:$PATH +export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64 diff --git a/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh b/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh new file mode 100644 index 0000000..b78cefc --- /dev/null +++ b/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh @@ -0,0 +1 @@ +. .gitlab/ci/env_macos_arm64_ninja.sh diff --git a/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 index e2d573e..f6e1f0a 100755 --- a/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 +++ b/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 @@ -1,3 +1 @@ -$pwdpath = $pwd.Path -& "$pwsh" -File ".gitlab/ci/mingw.ps1" -Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\bin;$env:PATH" +. .gitlab/ci/mingw-env.ps1 diff --git a/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 index 6eccb72..f6e1f0a 100755 --- a/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 +++ b/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 @@ -1,5 +1 @@ -$pwdpath = $pwd.Path -& "$pwsh" -File ".gitlab/ci/mingw.ps1" -Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\msys\1.0\bin;$pwdpath\.gitlab\mingw\bin;$env:PATH" -$env:MSYSTEM = 'MINGW32' -$env:MAKE_MODE = 'unix' +. .gitlab/ci/mingw-env.ps1 diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec6.73.1.ps1 new file mode 100755 index 0000000..96e36a1 --- /dev/null +++ b/.gitlab/ci/env_windows_orangec6.73.1.ps1 @@ -0,0 +1,2 @@ +. .gitlab/ci/ninja-env.ps1 +. .gitlab/ci/orangec-env.ps1 diff --git a/.gitlab/ci/env_windows_vs2022_x64.ps1 b/.gitlab/ci/env_windows_vs2022_x64.ps1 new file mode 100755 index 0000000..42aec11 --- /dev/null +++ b/.gitlab/ci/env_windows_vs2022_x64.ps1 @@ -0,0 +1,4 @@ +if ("$env:CMAKE_CI_NIGHTLY" -eq "true") { + $cmake = "build\install\bin\cmake" + . ".gitlab/ci/qt-env.ps1" +} diff --git a/.gitlab/ci/gitlab_ci.cmake b/.gitlab/ci/gitlab_ci.cmake index 080c93b..d0d4a59 100644 --- a/.gitlab/ci/gitlab_ci.cmake +++ b/.gitlab/ci/gitlab_ci.cmake @@ -5,8 +5,13 @@ endif () # Set up the source and build paths. set(CTEST_SOURCE_DIRECTORY "$ENV{CI_PROJECT_DIR}") +if("$ENV{CMAKE_CI_IN_SYMLINK_TREE}") + set(CTEST_SOURCE_DIRECTORY "$ENV{CI_PROJECT_DIR}/work/cmake") +endif() if("$ENV{CMAKE_CI_INPLACE}") set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}") +elseif("$ENV{CMAKE_CI_IN_SYMLINK_TREE}") + set(CTEST_BINARY_DIRECTORY "$ENV{CI_PROJECT_DIR}/work/build") else() set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}/build") endif() diff --git a/.gitlab/ci/mingw-env.ps1 b/.gitlab/ci/mingw-env.ps1 new file mode 100755 index 0000000..d68a7f7 --- /dev/null +++ b/.gitlab/ci/mingw-env.ps1 @@ -0,0 +1,5 @@ +$pwdpath = $pwd.Path +& "$pwsh" -File ".gitlab/ci/mingw.ps1" +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\bin;$pwdpath\.gitlab\mingw\msys\1.0\bin;$env:PATH" +$env:MSYSTEM = 'MINGW32' +$env:MAKE_MODE = 'unix' diff --git a/.gitlab/ci/ninja-nightly.sh b/.gitlab/ci/ninja-nightly.sh index b78b64e..739d1d5 100755 --- a/.gitlab/ci/ninja-nightly.sh +++ b/.gitlab/ci/ninja-nightly.sh @@ -5,7 +5,7 @@ set -e cd .gitlab git clone https://github.com/ninja-build/ninja.git ninja-src -cmake -S ninja-src -B ninja-src/build -DCMAKE_BUILD_TYPE=Release +cmake -S ninja-src -B ninja-src/build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release cmake --build ninja-src/build --parallel --target ninja mv ninja-src/build/ninja . rm -rf ninja-src diff --git a/.gitlab/ci/orangec-env.ps1 b/.gitlab/ci/orangec-env.ps1 new file mode 100755 index 0000000..3a5d232 --- /dev/null +++ b/.gitlab/ci/orangec-env.ps1 @@ -0,0 +1,8 @@ +Invoke-Expression -Command .gitlab/ci/orangec.ps1 +$pwdpath = $pwd.Path +Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\orangec\bin;$env:PATH" +Set-Item -Force -Path "env:ORANGEC" -Value "$pwdpath\.gitlab\orangec" + +$env:CC = "occ" +$env:CXX = "occ" +occ --version diff --git a/.gitlab/ci/orangec.ps1 b/.gitlab/ci/orangec.ps1 new file mode 100755 index 0000000..2201e12 --- /dev/null +++ b/.gitlab/ci/orangec.ps1 @@ -0,0 +1,24 @@ +$erroractionpreference = "stop" + +if ("$env:CMAKE_CONFIGURATION".Contains("orangec6.73.1")) { + # OrangeC 6.73.1 + $archive = "ZippedBinaries6738.zip" + $release = "Orange-C-v6.73.1" + $sha256sum = "29BC506AB105B2BF1002129C37826B2153DF1C8D0F22B9A2C38ACA3FB72A5B89" +} else { + throw ('unknown CMAKE_CONFIGURATION: ' + "$env:CMAKE_CONFIGURATION") +} + +$outdir = $pwd.Path +$outdir = "$outdir\.gitlab" +$ProgressPreference = 'SilentlyContinue' +Invoke-WebRequest -Uri "https://github.com/LADSoft/OrangeC/releases/download/$release/$archive" -OutFile "$outdir\$archive" +$hash = Get-FileHash "$outdir\$archive" -Algorithm SHA256 +if ($hash.Hash -ne $sha256sum) { + exit 1 +} + +Add-Type -AssemblyName System.IO.Compression.FileSystem +[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$archive", "$outdir") +# The archive contains directory 'orangec', placed at '$outdir\orangec'. +Remove-Item "$outdir\$archive" diff --git a/.gitlab/ci/qt-env.ps1 b/.gitlab/ci/qt-env.ps1 index 7eff55f..22b1099 100755 --- a/.gitlab/ci/qt-env.ps1 +++ b/.gitlab/ci/qt-env.ps1 @@ -1,6 +1,9 @@ +if ($cmake -eq $null) { + throw ('$cmake powershell variable not set ') +} if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { $pwdpath = $pwd.Path - cmake -P .gitlab/ci/download_qt.cmake + & $cmake -P .gitlab/ci/download_qt.cmake Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\qt\bin;$env:PATH" qmake -v } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { diff --git a/.gitlab/ci/swift-env.sh b/.gitlab/ci/swift-env.sh new file mode 100644 index 0000000..fa0c81e --- /dev/null +++ b/.gitlab/ci/swift-env.sh @@ -0,0 +1,7 @@ +curl -L -O "https://download.swift.org/swift-5.7.1-release/ubuntu1804/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu18.04.tar.gz" +echo '2b30f9efc969d9e96f0836d0871130dffb369822a3823ee6f3db44c29c1698e3 swift-5.7.1-RELEASE-ubuntu18.04.tar.gz' > swift.sha256sum +sha256sum --check swift.sha256sum +mkdir /opt/swift +tar xzf swift-5.7.1-RELEASE-ubuntu18.04.tar.gz -C /opt/swift --strip-components=2 +rm swift-5.7.1-RELEASE-ubuntu18.04.tar.gz swift.sha256sum +export SWIFTC="/opt/swift/bin/swiftc" diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 8398108..e70dc07 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -137,6 +137,16 @@ CMAKE_CI_NO_INSTALL: 1 CTEST_NO_WARNINGS_ALLOWED: 1 +.debian12_ninja_multi_symlinked: + extends: .debian12 + + variables: + CMAKE_CONFIGURATION: debian12_ninja_multi_symlinked + CMAKE_GENERATOR: "Ninja Multi-Config" + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_CI_IN_SYMLINK_TREE: 1 + CMAKE_CI_BUILD_DIR: "real_work/work/build" + .debian12_extdeps: extends: .debian12 @@ -185,6 +195,16 @@ CTEST_NO_WARNINGS_ALLOWED: 1 CMAKE_GENERATOR: "Unix Makefiles" +.fedora38_makefiles_symlinked: + extends: .fedora38 + + variables: + CMAKE_CONFIGURATION: fedora38_makefiles_symlinked + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_GENERATOR: "Unix Makefiles" + CMAKE_CI_IN_SYMLINK_TREE: 1 + CMAKE_CI_BUILD_DIR: "real_work/work/build" + ### Clang Compiler .debian12_makefiles_clang: @@ -345,7 +365,7 @@ ### HIP builds .hip5.5: - image: "kitware/cmake:ci-hip5.5-x86_64-2023-06-01" + image: "kitware/cmake:ci-hip5.5-x86_64-2023-09-18" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -373,10 +393,17 @@ CMAKE_CONFIGURATION: fedora38_hip_radeon CTEST_LABELS: "HIP" +.hip5.5_nvidia: + extends: .hip5.5 + + variables: + CMAKE_CONFIGURATION: hip5.5_nvidia + CTEST_LABELS: "HIP" + ### C++ modules .gcc_cxx_modules_x86_64: - image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2023-08-15" + image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2023-09-21" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index fbba288..b3165fa 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -7,7 +7,7 @@ GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID" # TODO: Factor this out so that each job selects the Xcode version to # use so that different versions can be tested in a single pipeline. - DEVELOPER_DIR: "/Applications/Xcode-14.3.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode-15.0.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4227 @@ -38,6 +38,15 @@ CMAKE_CONFIGURATION: macos_arm64_ninja CTEST_NO_WARNINGS_ALLOWED: 1 +.macos_arm64_ninja_symlinked: + extends: .macos_build + + variables: + CMAKE_CONFIGURATION: macos_arm64_ninja_symlinked + CTEST_NO_WARNINGS_ALLOWED: 1 + CMAKE_CI_IN_SYMLINK_TREE: 1 + CMAKE_CI_BUILD_DIR: "real_work/work/build" + .macos_arm64_pch: extends: .macos_arm64_ninja @@ -86,6 +95,16 @@ CMAKE_GENERATOR: Xcode CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" +.macos_arm64_xcode_symlinked: + extends: .macos + + variables: + CMAKE_CONFIGURATION: macos_arm64_xcode_symlinked + CMAKE_GENERATOR: Xcode + CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" + CMAKE_CI_IN_SYMLINK_TREE: 1 + CMAKE_CI_BUILD_DIR: "real_work/work/build" + .macos_arm64_xcode_ub: extends: .macos @@ -116,7 +135,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-x86_64 - shell - - xcode-14.3 + - xcode-15.0 - nonconcurrent .macos_x86_64_tags_ext: @@ -124,7 +143,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-x86_64 - shell - - xcode-14.3 + - xcode-15.0 - concurrent .macos_arm64_tags: @@ -132,7 +151,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-14.3 + - xcode-15.0 - nonconcurrent .macos_arm64_tags_ext: @@ -140,7 +159,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-14.3 + - xcode-15.0 - concurrent .macos_arm64_tags_package: @@ -148,7 +167,7 @@ - cmake # Since this is a bare runner, pin to a project. - macos-arm64 - shell - - xcode-14.3 + - xcode-15.0 - nonconcurrent - finder diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index 422e147..c449ab8 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -261,6 +261,20 @@ variables: CMAKE_CONFIGURATION: windows_openwatcom1.9 +.windows_orangec: + extends: .windows + + variables: + CMAKE_GENERATOR: "Ninja" + CMAKE_CI_BUILD_TYPE: Release + CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true" + +.windows_orangec6.73.1: + extends: .windows_orangec + + variables: + CMAKE_CONFIGURATION: windows_orangec6.73.1 + .windows_arm64_vs2022: extends: .windows @@ -366,6 +380,7 @@ - Set-Item -Force -Path "env:WIX" -Value "$pwdpath\.gitlab\wix" - (& "$pwsh" -File ".gitlab/ci/cmake.ps1") - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\cmake\bin;$env:PATH" + - $cmake = "cmake" - . .gitlab/ci/ninja-env.ps1 - (& "$env:WIX\bin\light.exe" -help) | Select -First 1 - cmake --version diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt index fe26d2b..56c77c1 100644 --- a/Help/command/FIND_XXX.txt +++ b/Help/command/FIND_XXX.txt @@ -211,7 +211,8 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: setting the :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` to ``FALSE``. * |SYSTEM_ENVIRONMENT_PATH_XXX| - * |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| + + |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| 6. Search cmake variables defined in the Platform files for the current system. The searching of ``CMAKE_INSTALL_PREFIX`` and diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 1fcf06c..5fe4326 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -87,6 +87,11 @@ The options are: :ref:`Target-dependent expressions <Target-Dependent Queries>` are not permitted. + .. versionchanged:: 3.28 + In targets using :ref:`file sets`, custom command byproducts are now + considered private unless they are listed in a non-private file set. + See policy :policy:`CMP0154`. + ``COMMAND`` Specify the command-line(s) to execute at build time. If more than one ``COMMAND`` is specified they will be executed in order, @@ -270,6 +275,11 @@ The options are: :ref:`Target-dependent expressions <Target-Dependent Queries>` are not permitted. + .. versionchanged:: 3.28 + In targets using :ref:`file sets`, custom command outputs are now + considered private unless they are listed in a non-private file set. + See policy :policy:`CMP0154`. + ``USES_TERMINAL`` .. versionadded:: 3.2 diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst index ef0c8d9..0385a93 100644 --- a/Help/command/add_custom_target.rst +++ b/Help/command/add_custom_target.rst @@ -63,6 +63,11 @@ The options are: :ref:`Target-dependent expressions <Target-Dependent Queries>` are not permitted. + .. versionchanged:: 3.28 + In custom targets using :ref:`file sets`, byproducts are now + considered private unless they are listed in a non-private file set. + See policy :policy:`CMP0154`. + ``COMMAND`` Specify the command-line(s) to execute at build time. If more than one ``COMMAND`` is specified they will be executed in order, diff --git a/Help/command/block.rst b/Help/command/block.rst index a352e83..4c6e111 100644 --- a/Help/command/block.rst +++ b/Help/command/block.rst @@ -21,7 +21,8 @@ scopes created by the ``block()`` command are removed. ``POLICIES`` Create a new policy scope. This is equivalent to - :command:`cmake_policy(PUSH)`. + :command:`cmake_policy(PUSH)` with an automatic + :command:`cmake_policy(POP)` when leaving the block scope. ``VARIABLES`` Create a new variable scope. diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst index 76824ef..dad0833 100644 --- a/Help/command/cmake_host_system_information.rst +++ b/Help/command/cmake_host_system_information.rst @@ -146,6 +146,13 @@ queried. The list of queried values is stored in ``<variable>``. See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR` +``MSYSTEM_PREFIX`` + .. versionadded:: 3.28 + + Available only on Windows hosts. In a MSYS or MinGW development + environment that sets the ``MSYSTEM`` environment variable, this + is its installation prefix. Otherwise, this is the empty string. + ``DISTRIB_INFO`` .. versionadded:: 3.22 diff --git a/Help/command/cmake_policy.rst b/Help/command/cmake_policy.rst index d7880bc..4a08c01 100644 --- a/Help/command/cmake_policy.rst +++ b/Help/command/cmake_policy.rst @@ -24,9 +24,8 @@ The ``cmake_policy`` command is used to set policies to ``OLD`` or ``NEW`` behavior. While setting policies individually is supported, we encourage projects to set policies based on CMake versions: -.. code-block:: cmake - - cmake_policy(VERSION <min>[...<max>]) +.. signature:: cmake_policy(VERSION <min>[...<max>]) + :target: VERSION .. versionadded:: 3.12 The optional ``<max>`` version. @@ -57,10 +56,8 @@ command implicitly calls ``cmake_policy(VERSION)`` too. Setting Policies Explicitly ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - - cmake_policy(SET CMP<NNNN> NEW) - cmake_policy(SET CMP<NNNN> OLD) +.. signature:: cmake_policy(SET CMP<NNNN> NEW|OLD) + :target: SET Tell CMake to use the ``OLD`` or ``NEW`` behavior for a given policy. Projects depending on the old behavior of a given policy may silence a @@ -73,9 +70,8 @@ policy state to ``NEW``. Checking Policy Settings ^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: cmake - - cmake_policy(GET CMP<NNNN> <variable>) +.. signature:: cmake_policy(GET CMP<NNNN> <variable>) + :target: GET Check whether a given policy is set to ``OLD`` or ``NEW`` behavior. The output ``<variable>`` value will be ``OLD`` or ``NEW`` if the @@ -94,23 +90,27 @@ except when invoked with the ``NO_POLICY_SCOPE`` option The ``cmake_policy`` command provides an interface to manage custom entries on the policy stack: -.. code-block:: cmake +.. signature:: cmake_policy(PUSH) + :target: PUSH + + Create a new entry on the policy stack. + +.. signature:: cmake_policy(POP) + :target: POP - cmake_policy(PUSH) - cmake_policy(POP) + Remove the last policy stack entry created with ``cmake_policy(PUSH)``. Each ``PUSH`` must have a matching ``POP`` to erase any changes. This is useful to make temporary changes to policy settings. Calls to the :command:`cmake_minimum_required(VERSION)`, -``cmake_policy(VERSION)``, or ``cmake_policy(SET)`` commands +:command:`cmake_policy(VERSION)`, or :command:`cmake_policy(SET)` commands influence only the current top of the policy stack. .. versionadded:: 3.25 - The :command:`block` and :command:`endblock` commands offer a more flexible + The :command:`block(SCOPE_FOR POLICIES)` command offers a more flexible and more secure way to manage the policy stack. The pop action is done - automatically when the :command:`endblock` command is executed, so it avoid - to call the :command:`cmake_policy(POP)` command before each - :command:`return` command. + automatically when leaving the block scope, so there is no need to + precede each :command:`return` with a call to :command:`cmake_policy(POP)`. .. code-block:: cmake diff --git a/Help/command/find_file.rst b/Help/command/find_file.rst index 9f89f52..9ef8a6d 100644 --- a/Help/command/find_file.rst +++ b/Help/command/find_file.rst @@ -28,9 +28,11 @@ find_file .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``INCLUDE`` and ``PATH``. -.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: - ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` - is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: + On Windows hosts, CMake 3.3 through 3.27 searched additional paths: + ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. + This behavior was removed by CMake 3.28. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` diff --git a/Help/command/find_library.rst b/Help/command/find_library.rst index fb2c2f1..ba046fa 100644 --- a/Help/command/find_library.rst +++ b/Help/command/find_library.rst @@ -27,9 +27,11 @@ find_library .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``LIB`` and ``PATH``. -.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: - ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` - is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: + On Windows hosts, CMake 3.3 through 3.27 searched additional paths: + ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. + This behavior was removed by CMake 3.28. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set, diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index b0b6fe1..9a007fa 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst @@ -323,18 +323,27 @@ containing a configuration file: In all cases the ``<name>`` is treated as case-insensitive and corresponds to any of the names specified (``<PackageName>`` or names given by ``NAMES``). -Paths with ``lib/<arch>`` are enabled if the -:variable:`CMAKE_LIBRARY_ARCHITECTURE` variable is set. ``lib*`` includes one -or more of the values ``lib64``, ``lib32``, ``libx32`` or ``lib`` (searched in -that order). +If at least one compiled language has been enabled, the architecture-specific +``lib/<arch>`` and ``lib*`` directories may be searched based on the compiler's +target architecture, in the following order: -* Paths with ``lib64`` are searched on 64 bit platforms if the +``lib/<arch>`` + Searched if the :variable:`CMAKE_LIBRARY_ARCHITECTURE` variable is set. + +``lib64`` + Searched on 64 bit platforms (:variable:`CMAKE_SIZEOF_VOID_P` is 8) and the :prop_gbl:`FIND_LIBRARY_USE_LIB64_PATHS` property is set to ``TRUE``. -* Paths with ``lib32`` are searched on 32 bit platforms if the + +``lib32`` + Searched on 32 bit platforms (:variable:`CMAKE_SIZEOF_VOID_P` is 4) and the :prop_gbl:`FIND_LIBRARY_USE_LIB32_PATHS` property is set to ``TRUE``. -* Paths with ``libx32`` are searched on platforms using the x32 ABI + +``libx32`` + Searched on platforms using the x32 ABI if the :prop_gbl:`FIND_LIBRARY_USE_LIBX32_PATHS` property is set to ``TRUE``. -* The ``lib`` path is always searched. + +``lib`` + Always searched. .. versionchanged:: 3.24 On ``Windows`` platform, it is possible to include registry queries as part @@ -346,7 +355,7 @@ that order). ``REGISTRY_VIEW`` can be specified to manage ``Windows`` registry queries specified as part of ``PATHS`` and ``HINTS``. -.. include:: FIND_XXX_REGISTRY_VIEW.txt + .. include:: FIND_XXX_REGISTRY_VIEW.txt If ``PATH_SUFFIXES`` is specified, the suffixes are appended to each (``W``) or (``U``) directory entry one-by-one. diff --git a/Help/command/find_path.rst b/Help/command/find_path.rst index f0522f6..080c231 100644 --- a/Help/command/find_path.rst +++ b/Help/command/find_path.rst @@ -27,9 +27,11 @@ find_path .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``INCLUDE`` and ``PATH``. -.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: - ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` - is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: + On Windows hosts, CMake 3.3 through 3.27 searched additional paths: + ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. + This behavior was removed by CMake 3.28. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` diff --git a/Help/command/find_program.rst b/Help/command/find_program.rst index fe95a9a..b6b2508 100644 --- a/Help/command/find_program.rst +++ b/Help/command/find_program.rst @@ -23,7 +23,7 @@ find_program .. |ENV_CMAKE_XXX_MAC_PATH| replace:: :envvar:`CMAKE_APPBUNDLE_PATH` .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` itself. -.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts no extra search paths are included +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: \ .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: |CMAKE_SYSTEM_PREFIX_PATH_XXX_SUBDIR| diff --git a/Help/command/if.rst b/Help/command/if.rst index c47c71b..5d85a1f 100644 --- a/Help/command/if.rst +++ b/Help/command/if.rst @@ -228,36 +228,36 @@ Comparisons .. signature:: if(<variable|string> LESS <variable|string>) :target: LESS - True if the given string or variable's value is a valid number and less - than that on the right. + True if the given string or variable's value parses as a real number + (like a C ``double``) and less than that on the right. .. signature:: if(<variable|string> GREATER <variable|string>) :target: GREATER - True if the given string or variable's value is a valid number and greater - than that on the right. + True if the given string or variable's value parses as a real number + (like a C ``double``) and greater than that on the right. .. signature:: if(<variable|string> EQUAL <variable|string>) :target: EQUAL - True if the given string or variable's value is a valid number and equal - to that on the right. + True if the given string or variable's value parses as a real number + (like a C ``double``) and equal to that on the right. .. signature:: if(<variable|string> LESS_EQUAL <variable|string>) :target: LESS_EQUAL .. versionadded:: 3.7 - True if the given string or variable's value is a valid number and less - than or equal to that on the right. + True if the given string or variable's value parses as a real number + (like a C ``double``) and less than or equal to that on the right. .. signature:: if(<variable|string> GREATER_EQUAL <variable|string>) :target: GREATER_EQUAL .. versionadded:: 3.7 - True if the given string or variable's value is a valid number and greater - than or equal to that on the right. + True if the given string or variable's value parses as a real number + (like a C ``double``) and greater than or equal to that on the right. .. signature:: if(<variable|string> STRLESS <variable|string>) :target: STRLESS diff --git a/Help/command/set.rst b/Help/command/set.rst index aeb88b3..fa635c6 100644 --- a/Help/command/set.rst +++ b/Help/command/set.rst @@ -27,11 +27,12 @@ Set Normal Variable If the ``PARENT_SCOPE`` option is given the variable will be set in the scope above the current scope. Each new directory or :command:`function` command creates a new scope. A scope can also be created with the - :command:`block` command. This command will set the value of a variable into - the parent directory, calling function or encompassing scope (whichever is - applicable to the case at hand). The previous state of the variable's value - stays the same in the current scope (e.g., if it was undefined before, it is - still undefined and if it had a value, it is still that value). + :command:`block` command. ``set(PARENT_SCOPE)`` will set the value + of a variable into the parent directory, calling function, or + encompassing scope (whichever is applicable to the case at hand). + The previous state of the variable's value stays the same in the + current scope (e.g., if it was undefined before, it is still undefined + and if it had a value, it is still that value). The :command:`block(PROPAGATE)` and :command:`return(PROPAGATE)` commands can be used as an alternate method to the :command:`set(PARENT_SCOPE)` diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst index 4a8eda2..86bf7fb 100644 --- a/Help/command/target_sources.rst +++ b/Help/command/target_sources.rst @@ -60,6 +60,8 @@ expressions to ensure the sources are correctly assigned to the target. See the :manual:`cmake-buildsystem(7)` manual for more on defining buildsystem properties. +.. _`File Sets`: + File Sets ^^^^^^^^^ diff --git a/Help/envvar/CUDAHOSTCXX.rst b/Help/envvar/CUDAHOSTCXX.rst index 74f5d48..4e78afa 100644 --- a/Help/envvar/CUDAHOSTCXX.rst +++ b/Help/envvar/CUDAHOSTCXX.rst @@ -8,8 +8,10 @@ CUDAHOSTCXX Preferred executable for compiling host code when compiling ``CUDA`` language files. Will only be used by CMake on the first configuration to determine ``CUDA`` host compiler, after which the value for ``CUDAHOSTCXX`` is -stored in the cache as :variable:`CMAKE_CUDA_HOST_COMPILER`. This environment -variable is preferred over :variable:`CMAKE_CUDA_HOST_COMPILER`. +stored in the cache as +:variable:`CMAKE_CUDA_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`. +This environment variable is preferred over +:variable:`CMAKE_CUDA_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`. This environment variable is primarily meant for use with projects that enable ``CUDA`` as a first-class language. diff --git a/Help/envvar/HIPHOSTCXX.rst b/Help/envvar/HIPHOSTCXX.rst new file mode 100644 index 0000000..871fbfb --- /dev/null +++ b/Help/envvar/HIPHOSTCXX.rst @@ -0,0 +1,19 @@ +HIPHOSTCXX +----------- + +.. versionadded:: 3.28 + +.. include:: ENV_VAR.txt + +Preferred executable for compiling host code when compiling ``HIP`` +language files with the NVIDIA CUDA Compiler. Will only be used by CMake +on the first configuration to determine ``HIP`` host compiler, after which +the value for ``HIPHOSTCXX`` is stored in the cache as +:variable:`CMAKE_HIP_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`. + +This environment variable is primarily meant for use with projects that +enable ``HIP`` as a first-class language. + +.. note:: + + Ignored when using :ref:`Visual Studio Generators`. diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst index 5e803f5..e7aff9c 100644 --- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst +++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst @@ -245,10 +245,9 @@ then use :command:`target_compile_features` to add the compiler feature </details> Finally, with our interface library set up, we need to link our -executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary`` -library to our new -``tutorial_compiler_flags`` library. Respectively, the code will look like -this: +executable ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions`` +library to our new ``tutorial_compiler_flags`` library. Respectively, the code +will look like this: .. raw:: html @@ -275,7 +274,7 @@ this: :caption: TODO 6: MathFunctions/CMakeLists.txt :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4 :language: cmake - :start-after: # link our compiler flags interface library + :start-after: # link SqrtLibrary to tutorial_compiler_flags :end-before: target_link_libraries(MathFunctions .. raw:: html @@ -292,8 +291,7 @@ and this: :caption: TODO 7: MathFunctions/CMakeLists.txt :name: MathFunctions-SqrtLibrary-target_link_libraries-step4 :language: cmake - :start-after: # link our compiler flags interface library - :end-before: target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + :start-after: # link MathFunctions to tutorial_compiler_flags .. raw:: html diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst index 178334a..18ced97 100644 --- a/Help/guide/tutorial/Adding a Library.rst +++ b/Help/guide/tutorial/Adding a Library.rst @@ -184,7 +184,7 @@ Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``: </details> -Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``. +Lastly, replace ``sqrt`` with the wrapper function ``mathfunctions::sqrt``. .. raw:: html diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt index b221506..1654564 100644 --- a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt @@ -33,10 +33,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt index 36b3fe1..210563a 100644 --- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt @@ -26,10 +26,13 @@ if(USE_MYMATH) ${CMAKE_CURRENT_BINARY_DIR} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt index 813bf90..eacc538 100644 --- a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt @@ -31,10 +31,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt index 38694dd..8aa5904 100644 --- a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt @@ -33,10 +33,13 @@ if(USE_MYMATH) POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # define the symbol stating we are using the declspec(dllexport) when diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt index 0ffb9e1..ffb2f35 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt @@ -16,7 +16,7 @@ if (USE_MYMATH) # TODO 7: Link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() # TODO 6: Link MathFunctions to tutorial_compiler_flags diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt index 48561eb..6931898 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt @@ -17,10 +17,11 @@ if (USE_MYMATH) mysqrt.cxx ) - # link our compiler flags interface library + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - target_link_libraries(MathFunctions PUBLIC SqrtLibrary) + + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt index 0c688f2..61b3899 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt @@ -16,12 +16,13 @@ if (USE_MYMATH) mysqrt.cxx ) - # link our compiler flags interface library + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # TODO 1: Create a variable called installable_libs that is a list of all diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt index b1b925e..8499a51 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -16,11 +16,13 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt index 897ec0e..a0b3037 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -16,6 +16,7 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) # TODO 1: Include CheckCXXSourceCompiles @@ -41,7 +42,7 @@ if (USE_MYMATH) target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt index 872a24a..b14d180 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt @@ -10,6 +10,7 @@ if (USE_MYMATH) mysqrt.cxx ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) # does this system provide the log and exp functions? @@ -45,7 +46,7 @@ target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) -# link our compiler flags interface library +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt index 54cecf8..5addc6d 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt @@ -25,10 +25,13 @@ if (USE_MYMATH) ${CMAKE_CURRENT_BINARY_DIR} ) + # link SqrtLibrary to tutorial_compiler_flags target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) endif() +# link MathFunctions to tutorial_compiler_flags target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) # install libs diff --git a/Help/manual/OPTIONS_BUILD.txt b/Help/manual/OPTIONS_BUILD.txt index 94adac8..fb1e7d4 100644 --- a/Help/manual/OPTIONS_BUILD.txt +++ b/Help/manual/OPTIONS_BUILD.txt @@ -105,11 +105,15 @@ .. option:: --toolchain <path-to-file> + .. versionadded:: 3.21 + Specify the cross compiling toolchain file, equivalent to setting :variable:`CMAKE_TOOLCHAIN_FILE` variable. .. option:: --install-prefix <directory> + .. versionadded:: 3.21 + Specify the installation directory, used by the :variable:`CMAKE_INSTALL_PREFIX` variable. Must be an absolute path. diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index 356e73d..55f07b7 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst @@ -84,6 +84,7 @@ Environment Variables for Languages /envvar/FFLAGS /envvar/HIPCXX /envvar/HIPFLAGS + /envvar/HIPHOSTCXX /envvar/ISPC /envvar/ISPCFLAGS /envvar/OBJC diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 86ae0a4..00d1aa4 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -195,6 +195,12 @@ Two forms of conditional generator expressions are supported: if ``condition`` is ``0``. Any other value for ``condition`` results in an error. + .. versionadded:: 3.28 + + This generator expression short-circuits such that generator expressions in + ``false_string`` will not evaluate when ``condition`` is ``1``, and generator + expressions in ``true_string`` will not evaluate when condition is ``0``. + Typically, the ``condition`` is itself a generator expression. For instance, the following expression expands to ``DEBUG_MODE`` when the ``Debug`` configuration is used, and the empty string for all other configurations: @@ -252,6 +258,11 @@ The common boolean logic operators are supported: ``condition`` must be ``0`` or ``1``. The result of the expression is ``0`` if ``condition`` is ``1``, else ``1``. + .. versionadded:: 3.28 + + Logical operators short-circuit such that generator expressions in the + arguments list will not be evaluated once a return value can be determined. + .. _`Comparison Expressions`: Primary Comparison Expressions diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index a018aff..dff4f34 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.28 .. toctree:: :maxdepth: 1 + CMP0154: Generated files are private by default in targets using file sets. </policy/CMP0154> CMP0153: The exec_program command should not be called. </policy/CMP0153> CMP0152: file(REAL_PATH) resolves symlinks before collapsing ../ components. </policy/CMP0152> diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index e2366da..46b5fc7 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst @@ -41,6 +41,15 @@ The files are a JSON document with an object as the root: The root object recognizes the following fields: +``$schema`` + An optional string that provides a URI to the JSON schema that describes the + structure of this JSON document. This field is used for validation and + autocompletion in editors that support JSON schema. It doesn't affect the + behavior of the document itself. If this field is not specified, the JSON + document will still be valid, but tools that use JSON schema for validation + and autocompletion may not function correctly. + This is allowed in preset files specifying version ``8`` or above. + ``version`` A required integer representing the version of the JSON schema. The supported versions are: diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index fa7a90f..d9df773 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -590,6 +590,7 @@ Variables for Languages /variable/CMAKE_Fortran_MODOUT_FLAG /variable/CMAKE_HIP_ARCHITECTURES /variable/CMAKE_HIP_EXTENSIONS + /variable/CMAKE_HIP_PLATFORM /variable/CMAKE_HIP_STANDARD /variable/CMAKE_HIP_STANDARD_REQUIRED /variable/CMAKE_ISPC_HEADER_DIRECTORY @@ -627,6 +628,7 @@ Variables for Languages /variable/CMAKE_LANG_FLAGS_RELEASE_INIT /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT + /variable/CMAKE_LANG_HOST_COMPILER /variable/CMAKE_LANG_IGNORE_EXTENSIONS /variable/CMAKE_LANG_IMPLICIT_INCLUDE_DIRECTORIES /variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index b5848f7..5223acb 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -309,6 +309,8 @@ Options .. option:: --debug-find + .. versionadded:: 3.17 + Put cmake find commands in a debug mode. Print extra find call information during the cmake run to standard @@ -318,6 +320,8 @@ Options .. option:: --debug-find-pkg=<pkg>[,...] + .. versionadded:: 3.23 + Put cmake find commands in a debug mode when running under calls to :command:`find_package(\<pkg\>) <find_package>`, where ``<pkg>`` is an entry in the given comma-separated list of case-sensitive package @@ -328,6 +332,8 @@ Options .. option:: --debug-find-var=<var>[,...] + .. versionadded:: 3.23 + Put cmake find commands in a debug mode when called with ``<var>`` as the result variable, where ``<var>`` is an entry in the given comma-separated list. @@ -349,6 +355,8 @@ Options .. option:: --trace-format=<format> + .. versionadded:: 3.17 + Put cmake in trace mode and sets the trace output format. ``<format>`` can be one of the following values. @@ -471,12 +479,16 @@ Options .. option:: --compile-no-warning-as-error + .. versionadded:: 3.24 + Ignore target property :prop_tgt:`COMPILE_WARNING_AS_ERROR` and variable :variable:`CMAKE_COMPILE_WARNING_AS_ERROR`, preventing warnings from being treated as errors on compile. .. option:: --profiling-output=<path> + .. versionadded:: 3.18 + Used in conjunction with :option:`--profiling-format <cmake --profiling-format>` to output to a given path. @@ -1325,6 +1337,8 @@ autoconf-based projects (via ``share/aclocal/cmake.m4``). Run a Workflow Preset ===================== +.. versionadded:: 3.25 + .. program:: cmake :manual:`CMake Presets <cmake-presets(7)>` provides a way to execute multiple diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index adfb1cb..d27faa1 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json @@ -9,9 +9,9 @@ "const": 1, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV1"} + "configurePresets": { "$ref": "#/definitions/configurePresetsV1" } }, "additionalProperties": false }, @@ -21,11 +21,11 @@ "const": 2, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV1"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV2"}, - "testPresets": { "$ref": "#/definitions/testPresetsV2"} + "configurePresets": { "$ref": "#/definitions/configurePresetsV1" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV2" }, + "testPresets": { "$ref": "#/definitions/testPresetsV2" } }, "additionalProperties": false }, @@ -35,11 +35,11 @@ "const": 3, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV3"}, - "testPresets": { "$ref": "#/definitions/testPresetsV3"} + "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV3" }, + "testPresets": { "$ref": "#/definitions/testPresetsV3" } }, "additionalProperties": false }, @@ -49,12 +49,12 @@ "const": 4, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4"}, - "testPresets": { "$ref": "#/definitions/testPresetsV3"}, - "include": { "$ref": "#/definitions/include"} + "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV3" }, + "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false }, @@ -64,12 +64,12 @@ "const": 5, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4"}, - "testPresets": { "$ref": "#/definitions/testPresetsV5"}, - "include": { "$ref": "#/definitions/include"} + "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV5" }, + "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false }, @@ -79,14 +79,14 @@ "const": 6, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV3"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4"}, - "testPresets": { "$ref": "#/definitions/testPresetsV6"}, - "packagePresets": { "$ref": "#/definitions/packagePresetsV6"}, + "configurePresets": { "$ref": "#/definitions/configurePresetsV3" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV6" }, + "packagePresets": { "$ref": "#/definitions/packagePresetsV6" }, "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" }, - "include": { "$ref": "#/definitions/include"} + "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false }, @@ -96,14 +96,32 @@ "const": 7, "description": "A required integer representing the version of the JSON schema." }, - "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"}, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, "vendor": { "$ref": "#/definitions/vendor" }, - "configurePresets": { "$ref": "#/definitions/configurePresetsV7"}, - "buildPresets": { "$ref": "#/definitions/buildPresetsV4"}, - "testPresets": { "$ref": "#/definitions/testPresetsV6"}, - "packagePresets": { "$ref": "#/definitions/packagePresetsV6"}, + "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV6" }, + "packagePresets": { "$ref": "#/definitions/packagePresetsV6" }, "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" }, - "include": { "$ref": "#/definitions/include"} + "include": { "$ref": "#/definitions/include" } + }, + "additionalProperties": false + }, + { + "properties": { + "$schema": { "$ref": "#/definitions/$schema" }, + "version": { + "const": 8, + "description": "A required integer representing the version of the JSON schema." + }, + "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" }, + "vendor": { "$ref": "#/definitions/vendor" }, + "configurePresets": { "$ref": "#/definitions/configurePresetsV7" }, + "buildPresets": { "$ref": "#/definitions/buildPresetsV4" }, + "testPresets": { "$ref": "#/definitions/testPresetsV6" }, + "packagePresets": { "$ref": "#/definitions/packagePresetsV6" }, + "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" }, + "include": { "$ref": "#/definitions/include" } }, "additionalProperties": false } @@ -112,6 +130,11 @@ "version" ], "definitions": { + "$schema": { + "type": "string", + "description": "The schema against which to verify this document.", + "format": "uri-reference" + }, "cmakeMinimumRequired": { "type": "object", "description": "An optional object representing the minimum version of CMake needed to build this project.", diff --git a/Help/policy/CMP0154.rst b/Help/policy/CMP0154.rst new file mode 100644 index 0000000..cb93d41 --- /dev/null +++ b/Help/policy/CMP0154.rst @@ -0,0 +1,53 @@ +CMP0154 +------- + +.. versionadded:: 3.28 + +Generated files are private by default in targets using :ref:`file sets`. + +CMake 3.27 and below assume that any file generated as an output or byproduct +of :command:`add_custom_command` or :command:`add_custom_target` may be a +public header file meant for inclusion by dependents' source files. This +requires :ref:`Ninja Generators` to add conservative order-only dependencies +that prevent a target's source files from compiling before custom commands +from the target's dependencies are finished, even if those custom commands +only produce sources private to their own target. + +:ref:`File Sets`, introduced by CMake 3.23, provide a way to express the +visibility of generated header files. CMake 3.28 and above prefer to +assume that, in targets using file sets, generated files are private to +their own target by default. Generated public headers must be specified +as members of a ``PUBLIC`` (or ``INTERFACE``) ``FILE_SET``, typically of +type ``HEADERS``. With this information, :ref:`Ninja Generators` may omit +the above-mentioned conservative dependencies and produce more efficient +build graphs. + +This policy provides compatibility for projects using file sets in targets +with generated header files that have not been updated. Such projects +should be updated to express generated public headers in a file set. +For example: + +.. code-block:: cmake + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h + ... + ) + target_sources(foo + PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} + FILES ${CMAKE_CURRENT_BINARY_DIR}/foo.h + ) + +The ``OLD`` behavior for this policy is to assume generated files are +public, even in targets using file sets, and for :ref:`Ninja Generators` +to produce conservative build graphs. The ``NEW`` behavior for this +policy is to assume generated files are private in targets using file sets, +and for :ref:`Ninja Generators` to produce more efficient build graphs. + +This policy was introduced in CMake version 3.28. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. +Unlike many policies, CMake version |release| does *not* warn +when this policy is not set and simply uses ``OLD`` behavior. + +.. include:: DEPRECATED.txt diff --git a/Help/prop_tgt/HIP_ARCHITECTURES.rst b/Help/prop_tgt/HIP_ARCHITECTURES.rst index 06f956b..58a813d 100644 --- a/Help/prop_tgt/HIP_ARCHITECTURES.rst +++ b/Help/prop_tgt/HIP_ARCHITECTURES.rst @@ -3,7 +3,8 @@ HIP_ARCHITECTURES .. versionadded:: 3.21 -List of AMD GPU architectures to generate device code for. +List of GPU architectures to for which to generate device code. +Architecture names are interpreted based on :variable:`CMAKE_HIP_PLATFORM`. A non-empty false value (e.g. ``OFF``) disables adding architectures. This is intended to support packagers and rare cases where full control diff --git a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst index 0c7845c..7e08b48 100644 --- a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst +++ b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst @@ -21,6 +21,8 @@ support per-configuration specification. For example, the code: selects for the target ``foo`` the program database debug information format for the Debug configuration. +This property is initialized from the value of the +:variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable, if it is set. If this property is not set, CMake selects a debug information format using the default value ``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the compiler, and otherwise diff --git a/Help/release/3.27.rst b/Help/release/3.27.rst index 7bcfc9a..e0b72cd 100644 --- a/Help/release/3.27.rst +++ b/Help/release/3.27.rst @@ -285,8 +285,8 @@ Changes made since CMake 3.27.0 include the following. to select the Windows 8.1 SDK. In CMake 3.27.[0-1] the ``version=`` field was limited to selecting Windows 10 SDKs. -3.27.3, 3.27.4 --------------- +3.27.3, 3.27.4, 3.27.5, 3.27.6 +------------------------------ * These versions made no changes to documented features or interfaces. Some implementation updates were made to support ecosystem changes diff --git a/Help/release/dev/CrayClang-compiler.rst b/Help/release/dev/CrayClang-compiler.rst new file mode 100644 index 0000000..ea26b50 --- /dev/null +++ b/Help/release/dev/CrayClang-compiler.rst @@ -0,0 +1,5 @@ +CrayClang-compiler +------------------ + +* Cray Clang-based compilers are now supported with + :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``CrayClang``. diff --git a/Help/release/dev/FetchContent-exclude-from-all.rst b/Help/release/dev/FetchContent-exclude-from-all.rst new file mode 100644 index 0000000..267ce72 --- /dev/null +++ b/Help/release/dev/FetchContent-exclude-from-all.rst @@ -0,0 +1,7 @@ +FetchContent-exclude-from-all +----------------------------- + +* The :module:`FetchContent` module's :command:`FetchContent_Declare` command + gained an ``EXCLUDE_FROM_ALL`` option, which propagates through to the + :command:`add_subdirectory` call made by + :command:`FetchContent_MakeAvailable` for the dependency. diff --git a/Help/release/dev/FindPkgConfig-override.rst b/Help/release/dev/FindPkgConfig-override.rst new file mode 100644 index 0000000..3f58065 --- /dev/null +++ b/Help/release/dev/FindPkgConfig-override.rst @@ -0,0 +1,5 @@ +FindPkgConfig-override +---------------------- + +* The :module:`FindPkgConfig` module :command:`pkg_get_variable` function + gained a ``DEFINE_VARIABLES`` option to pass variables to ``pkg-config``. diff --git a/Help/release/dev/fileset-private-dep.rst b/Help/release/dev/fileset-private-dep.rst new file mode 100644 index 0000000..5ffb036 --- /dev/null +++ b/Help/release/dev/fileset-private-dep.rst @@ -0,0 +1,7 @@ +fileset-private-dep +------------------- + +* Generated files, in targets using :ref:`file sets`, are now considered + private by default. Generated public headers must be specified using + file sets. This allows :ref:`Ninja Generators` to produce more + efficient build graphs. See policy :policy:`CMP0154`. diff --git a/Help/release/dev/find-windows-no-PATH-prefixes.rst b/Help/release/dev/find-windows-no-PATH-prefixes.rst new file mode 100644 index 0000000..b65c00b --- /dev/null +++ b/Help/release/dev/find-windows-no-PATH-prefixes.rst @@ -0,0 +1,17 @@ +find-windows-no-PATH-prefixes +----------------------------- + +* The :command:`find_library`, :command:`find_path`, and :command:`find_file` + commands no longer search in installation prefixes derived from the ``PATH`` + environment variable. This behavior was added in CMake 3.3 to support + MSYS and MinGW (``MSYSTEM``) development environments on Windows, but + it can search undesired prefixes that happen to be in the ``PATH`` for + unrelated reasons. Users that keep some ``<prefix>/bin`` directories in + the ``PATH`` just for their tools do not necessarily want any corresponding + ``<prefix>/lib`` or ``<prefix>/include`` directories searched. + The behavior was reverted for non-Windows platforms by CMake 3.6. + Now it has been reverted on Windows platforms too. + + One may set the ``CMAKE_PREFIX_PATH`` environment variable with a + :ref:`semicolon-separated list <CMake Language Lists>` of prefixes + that are to be searched. diff --git a/Help/release/dev/genexp-no-eval.rst b/Help/release/dev/genexp-no-eval.rst new file mode 100644 index 0000000..42ff1aa --- /dev/null +++ b/Help/release/dev/genexp-no-eval.rst @@ -0,0 +1,5 @@ +genexp-no-eval +-------------- + +* :manual:`generator expressions <cmake-generator-expressions(7)>` + short-circuit to avoid unnecessary evaluation of parameters. diff --git a/Help/release/dev/hip-nvidia.rst b/Help/release/dev/hip-nvidia.rst new file mode 100644 index 0000000..1d9814e --- /dev/null +++ b/Help/release/dev/hip-nvidia.rst @@ -0,0 +1,9 @@ +hip-nvidia +---------- + +* ``HIP`` language code may now be compiled for NVIDIA GPUs + using the NVIDIA CUDA Compiler (NVCC). + +* The :variable:`CMAKE_HIP_PLATFORM` variable was added to specify + the GPU platform for which HIP language sources are to be compiled + (``amd`` or ``nvidia``). diff --git a/Help/release/dev/host-msystem-prefix.rst b/Help/release/dev/host-msystem-prefix.rst new file mode 100644 index 0000000..4377144 --- /dev/null +++ b/Help/release/dev/host-msystem-prefix.rst @@ -0,0 +1,6 @@ +host-msystem-prefix +------------------- + +* The :command:`cmake_host_system_information` command gained a + ``MSYSTEM_PREFIX`` query for the installation prefix of a MSYS + or MinGW development environment on Windows hosts. diff --git a/Help/release/dev/mingw-search-prefixes.rst b/Help/release/dev/mingw-search-prefixes.rst new file mode 100644 index 0000000..1b5c3c7 --- /dev/null +++ b/Help/release/dev/mingw-search-prefixes.rst @@ -0,0 +1,6 @@ +mingw-search-prefixes +--------------------- + +* When using MinGW tools in a ``MSYSTEM`` environment on Windows, + the ``$MSYSTEM_PREFIX/local`` and ``$MSYSTEM_PREFIX`` prefixes are + now added to :variable:`CMAKE_SYSTEM_PREFIX_PATH`. diff --git a/Help/release/dev/orangec-compiler.rst b/Help/release/dev/orangec-compiler.rst new file mode 100644 index 0000000..9068699 --- /dev/null +++ b/Help/release/dev/orangec-compiler.rst @@ -0,0 +1,5 @@ +orangec-compiler +---------------- + +* The OrangeC compiler is now supported with + :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``OrangeC``. diff --git a/Help/release/dev/presets-schema.rst b/Help/release/dev/presets-schema.rst new file mode 100644 index 0000000..964373c --- /dev/null +++ b/Help/release/dev/presets-schema.rst @@ -0,0 +1,5 @@ +presets-schema +-------------- + +* :manual:`cmake-presets(7)` files now support schema version ``8``. + It adds support for a ``$schema`` field. diff --git a/Help/release/dev/xcode-no-legacy-buildsystem.rst b/Help/release/dev/xcode-no-legacy-buildsystem.rst new file mode 100644 index 0000000..f3d1f67 --- /dev/null +++ b/Help/release/dev/xcode-no-legacy-buildsystem.rst @@ -0,0 +1,8 @@ +xcode-no-legacy-buildsystem +--------------------------- + +* The :generator:`Xcode` generator will now issue a fatal error if + the Legacy Build System has been selected for Xcode 14 and + newer. Those Xcode versions dropped support for the Legacy Build + System and expect the project being set-up for their current + Build System. diff --git a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst index 6e2d756..498e2e5 100644 --- a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst +++ b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst @@ -3,34 +3,6 @@ CMAKE_CUDA_HOST_COMPILER .. versionadded:: 3.10 -When :variable:`CMAKE_CUDA_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` is -``NVIDIA``, ``CMAKE_CUDA_HOST_COMPILER`` selects the compiler executable to use -when compiling host code for ``CUDA`` language files. -This maps to the ``nvcc -ccbin`` option. - -The ``CMAKE_CUDA_HOST_COMPILER`` variable may be set explicitly before CUDA is -first enabled by a :command:`project` or :command:`enable_language` command. -This can be done via ``-DCMAKE_CUDA_HOST_COMPILER=...`` on the command line -or in a :ref:`toolchain file <Cross Compiling Toolchain>`. Or, one may set -the :envvar:`CUDAHOSTCXX` environment variable to provide a default value. - -Once the CUDA language is enabled, the ``CMAKE_CUDA_HOST_COMPILER`` variable -is read-only and changes to it are undefined behavior. - -.. note:: - - Since ``CMAKE_CUDA_HOST_COMPILER`` is meaningful only when the - :variable:`CMAKE_CUDA_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` is ``NVIDIA``, - it does not make sense to set ``CMAKE_CUDA_HOST_COMPILER`` without also - setting ``CMAKE_CUDA_COMPILER`` to NVCC. - -.. note:: - - Projects should not try to set ``CMAKE_CUDA_HOST_COMPILER`` to match - :variable:`CMAKE_CXX_COMPILER <CMAKE_<LANG>_COMPILER>` themselves. - It is the end-user's responsibility, not the project's, to ensure that - the C++ and CUDA compilers target the same ABI. - -.. note:: - - Ignored when using :ref:`Visual Studio Generators`. +This is the original CUDA-specific name for the more general +:variable:`CMAKE_<LANG>_HOST_COMPILER` variable. See the latter +for details. diff --git a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst index a7e9029..2e7f7c4 100644 --- a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst +++ b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst @@ -15,7 +15,8 @@ form. The format of the JSON file looks like: { "directory": "/home/user/development/project", "command": "/usr/bin/c++ ... -c ../foo/foo.cc", - "file": "../foo/foo.cc" + "file": "../foo/foo.cc", + "output": "../foo.dir/foo.cc.o" }, ... @@ -23,7 +24,8 @@ form. The format of the JSON file looks like: { "directory": "/home/user/development/project", "command": "/usr/bin/c++ ... -c ../foo/bar.cc", - "file": "../foo/bar.cc" + "file": "../foo/bar.cc", + "output": "../foo.dir/bar.cc.o" } ] diff --git a/Help/variable/CMAKE_HIP_ARCHITECTURES.rst b/Help/variable/CMAKE_HIP_ARCHITECTURES.rst index bcc6b35..3f17983 100644 --- a/Help/variable/CMAKE_HIP_ARCHITECTURES.rst +++ b/Help/variable/CMAKE_HIP_ARCHITECTURES.rst @@ -3,10 +3,14 @@ CMAKE_HIP_ARCHITECTURES .. versionadded:: 3.21 -Default value for :prop_tgt:`HIP_ARCHITECTURES` property of targets. +List of GPU architectures to for which to generate device code. +Architecture names are interpreted based on :variable:`CMAKE_HIP_PLATFORM`. -This is initialized to the architectures reported by ``rocm_agent_enumerator``, -if available, and otherwise to the default chosen by the compiler. +This is initialized based on the value of :variable:`CMAKE_HIP_PLATFORM`: + +``amd`` + Uses architectures reported by ``rocm_agent_enumerator``, if available, + and otherwise to a default chosen by the compiler. This variable is used to initialize the :prop_tgt:`HIP_ARCHITECTURES` property on all targets. See the target property for additional information. diff --git a/Help/variable/CMAKE_HIP_PLATFORM.rst b/Help/variable/CMAKE_HIP_PLATFORM.rst new file mode 100644 index 0000000..5e3a2b7 --- /dev/null +++ b/Help/variable/CMAKE_HIP_PLATFORM.rst @@ -0,0 +1,22 @@ +CMAKE_HIP_PLATFORM +------------------ + +.. versionadded:: 3.28 + +GPU platform for which HIP language sources are to be compiled. + +The value must be one of: + +``amd`` + AMD GPUs + +``nvidia`` + NVIDIA GPUs + +If not specified, a default is computed via ``hipconfig --platform``. + +:variable:`CMAKE_HIP_ARCHITECTURES` entries are interpreted with +as architectures of the GPU platform. + +:variable:`CMAKE_HIP_COMPILER <CMAKE_<LANG>_COMPILER>` must target +the same GPU platform. diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst index 5eb86c6..6893eea 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst @@ -18,6 +18,7 @@ Value Name ``CCur`` Concurrent Fortran ``Clang`` `LLVM Clang`_ ``Cray`` Cray Compiler +``CrayClang`` Cray Clang-based Compiler ``Embarcadero``, ``Borland`` `Embarcadero`_ ``Flang`` `Classic Flang Fortran Compiler`_ ``LLVMFlang`` `LLVM Flang Fortran Compiler`_ @@ -34,6 +35,7 @@ Value Name ``MSVC`` `Microsoft Visual Studio`_ ``NVHPC`` `NVIDIA HPC Compiler`_ ``NVIDIA`` `NVIDIA CUDA Compiler`_ +``OrangeC`` `OrangeC Compiler`_ ``OpenWatcom`` `Open Watcom`_ ``PGI`` The Portland Group ``PathScale`` PathScale @@ -62,6 +64,7 @@ languages. .. _NVIDIA HPC Compiler: https://developer.nvidia.com/hpc-compilers .. _NVIDIA CUDA Compiler: https://developer.nvidia.com/cuda-llvm-compiler .. _Open Watcom: https://open-watcom.github.io +.. _OrangeC Compiler: https://github.com/LADSoft/OrangeC .. _Small Device C Compiler: https://sdcc.sourceforge.net .. _Tiny C Compiler: https://bellard.org/tcc .. _Tasking Compiler Toolsets: https://www.tasking.com diff --git a/Help/variable/CMAKE_LANG_HOST_COMPILER.rst b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst new file mode 100644 index 0000000..cf3ba62 --- /dev/null +++ b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst @@ -0,0 +1,44 @@ +CMAKE_<LANG>_HOST_COMPILER +-------------------------- + +.. versionadded:: 3.10 + ``CMAKE_CUDA_HOST_COMPILER`` + +.. versionadded:: 3.28 + ``CMAKE_HIP_HOST_COMPILER`` + +This variable is available when ``<LANG>`` is ``CUDA`` or ``HIP``. + +When :variable:`CMAKE_<LANG>_COMPILER_ID` is +``NVIDIA``, ``CMAKE_<LANG>_HOST_COMPILER`` selects the compiler executable +to use when compiling host code for ``CUDA`` or ``HIP`` language files. +This maps to the ``nvcc -ccbin`` option. + +The ``CMAKE_<LANG>_HOST_COMPILER`` variable may be set explicitly before CUDA +or HIP is first enabled by a :command:`project` or :command:`enable_language` +command. +This can be done via ``-DCMAKE_<LANG>_HOST_COMPILER=...`` on the command line +or in a :ref:`toolchain file <Cross Compiling Toolchain>`. Or, one may set +the :envvar:`CUDAHOSTCXX` or :envvar:`HIPHOSTCXX` environment variable to +provide a default value. + +Once the CUDA or HIP language is enabled, the ``CMAKE_<LANG>_HOST_COMPILER`` +variable is read-only and changes to it are undefined behavior. + +.. note:: + + Since ``CMAKE_<LANG>_HOST_COMPILER`` is meaningful only when the + :variable:`CMAKE_<LANG>_COMPILER_ID` is ``NVIDIA``, + it does not make sense to set ``CMAKE_<LANG>_HOST_COMPILER`` without also + setting ``CMAKE_<LANG>_COMPILER`` to NVCC. + +.. note:: + + Projects should not try to set ``CMAKE_<LANG>_HOST_COMPILER`` to match + :variable:`CMAKE_CXX_COMPILER <CMAKE_<LANG>_COMPILER>` themselves. + It is the end-user's responsibility, not the project's, to ensure that + NVCC targets the same ABI as the C++ compiler. + +.. note:: + + Ignored when using :ref:`Visual Studio Generators`. diff --git a/Help/variable/CMAKE_SIZEOF_VOID_P.rst b/Help/variable/CMAKE_SIZEOF_VOID_P.rst index f5464d1..0c1fd7a 100644 --- a/Help/variable/CMAKE_SIZEOF_VOID_P.rst +++ b/Help/variable/CMAKE_SIZEOF_VOID_P.rst @@ -4,5 +4,5 @@ CMAKE_SIZEOF_VOID_P Size of a ``void`` pointer. This is set to the size of a pointer on the target machine, and is determined -by a try compile. If a 64-bit size is found, then the library search -path is modified to look for 64-bit libraries first. +when a compiled language is enabled. If a 64-bit size is found, then the +library search path is modified to look for 64-bit libraries first. diff --git a/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst b/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst index c8b5815..8dfcdc5 100644 --- a/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst +++ b/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst @@ -38,6 +38,12 @@ OpenBSD: Unix: * ``ENV{CONDA_PREFIX}`` when using a conda compiler +MSYSTEM environment with MinGW toolchain: + .. versionadded:: 3.28 + + * ``ENV{MSYSTEM_PREFIX}/local`` + * ``ENV{MSYSTEM_PREFIX}`` + Windows: * ``ENV{ProgramW6432}`` * ``ENV{ProgramFiles}`` diff --git a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst index d153061..f3c213c 100644 --- a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst +++ b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst @@ -12,7 +12,8 @@ mature enough for use by CMake. The possible values are: ``1`` The original Xcode build system. - This is the default when using Xcode 11.x or below. + This is the default when using Xcode 11.x or below and supported + up to Xcode 13.x. ``12`` The Xcode "new build system" introduced by Xcode 10. diff --git a/Modules/CMakeCUDACompilerABI.cu b/Modules/CMakeCUDACompilerABI.cu index 8463e86..b04d0ec 100644 --- a/Modules/CMakeCUDACompilerABI.cu +++ b/Modules/CMakeCUDACompilerABI.cu @@ -2,11 +2,8 @@ # error "A C or C++ compiler has been selected for CUDA" #endif -#include <cstdio> - -#include <cuda_runtime.h> - #include "CMakeCompilerABI.h" +#include "CMakeCompilerCUDAArch.h" int main(int argc, char* argv[]) { @@ -19,25 +16,7 @@ int main(int argc, char* argv[]) #endif static_cast<void>(argv); - int count = 0; - if (cudaGetDeviceCount(&count) != cudaSuccess || count == 0) { - std::fprintf(stderr, "No CUDA devices found.\n"); - return -1; - } - - int found = 0; - const char* sep = ""; - for (int device = 0; device < count; ++device) { - cudaDeviceProp prop; - if (cudaGetDeviceProperties(&prop, device) == cudaSuccess) { - std::printf("%s%d%d", sep, prop.major, prop.minor); - sep = ";"; - found = 1; - } - } - - if (!found) { - std::fprintf(stderr, "No CUDA architecture detected from any devices.\n"); + if (!cmakeCompilerCUDAArch()) { // Convince the compiler that the non-zero return value depends // on the info strings so they are not optimized out. return require ? -1 : 1; diff --git a/Modules/CMakeCompilerCUDAArch.h b/Modules/CMakeCompilerCUDAArch.h new file mode 100644 index 0000000..dcb030f --- /dev/null +++ b/Modules/CMakeCompilerCUDAArch.h @@ -0,0 +1,29 @@ +#include <cstdio> + +#include <cuda_runtime.h> + +static bool cmakeCompilerCUDAArch() +{ + int count = 0; + if (cudaGetDeviceCount(&count) != cudaSuccess || count == 0) { + std::fprintf(stderr, "No CUDA devices found.\n"); + return -1; + } + + bool found = false; + const char* sep = ""; + for (int device = 0; device < count; ++device) { + cudaDeviceProp prop; + if (cudaGetDeviceProperties(&prop, device) == cudaSuccess) { + std::printf("%s%d%d", sep, prop.major, prop.minor); + sep = ";"; + found = true; + } + } + + if (!found) { + std::fprintf(stderr, "No CUDA architecture detected from any devices.\n"); + } + + return found; +} diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake index 5fec06f..7eb93e2 100644 --- a/Modules/CMakeCompilerIdDetection.cmake +++ b/Modules/CMakeCompilerIdDetection.cmake @@ -65,12 +65,14 @@ function(compiler_id_detection outvar lang) VisualAge NVHPC PGI + CrayClang Cray TI FujitsuClang Fujitsu GHS Tasking + OrangeC ) if ("x${lang}" STREQUAL "xC") list(APPEND ordered_compilers @@ -103,7 +105,7 @@ function(compiler_id_detection outvar lang) endif() if("x${lang}" STREQUAL "xHIP") - set(ordered_compilers Clang) + set(ordered_compilers NVIDIA Clang) endif() if(CID_ID_DEFINE) diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index 8ec6c66..6d7d17e 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake @@ -70,6 +70,10 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_ARMClang "--version") set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_ARMClang "armclang") + list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS OrangeC ) + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_OrangeC "--version") + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_OrangeC "occ \\(OrangeC\\) Version") + list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS HP ) set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_HP "-V") set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_HP "HP C") diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake index 53c5f78..585b2aa 100644 --- a/Modules/CMakeDetermineCUDACompiler.cmake +++ b/Modules/CMakeDetermineCUDACompiler.cmake @@ -4,13 +4,13 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake) -if( NOT ( ("${CMAKE_GENERATOR}" MATCHES "Make") OR - ("${CMAKE_GENERATOR}" MATCHES "Ninja") OR - ("${CMAKE_GENERATOR}" MATCHES "Visual Studio (1|[9][0-9])") ) ) +if(NOT ((CMAKE_GENERATOR MATCHES "Make") OR + (CMAKE_GENERATOR MATCHES "Ninja") OR + (CMAKE_GENERATOR MATCHES "Visual Studio (1|[9][0-9])"))) message(FATAL_ERROR "CUDA language not currently supported by \"${CMAKE_GENERATOR}\" generator") endif() -if(${CMAKE_GENERATOR} MATCHES "Visual Studio") +if(CMAKE_GENERATOR MATCHES "Visual Studio") if(DEFINED ENV{CUDAHOSTCXX} OR DEFINED CMAKE_CUDA_HOST_COMPILER) message(WARNING "Visual Studio does not support specifying CUDAHOSTCXX or CMAKE_CUDA_HOST_COMPILER. Using the C++ compiler provided by Visual Studio.") endif() @@ -62,7 +62,7 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) - if(${CMAKE_GENERATOR} MATCHES "Visual Studio") + if(CMAKE_GENERATOR MATCHES "Visual Studio") # We will not know CMAKE_CUDA_COMPILER until the main compiler id step # below extracts it, but we do know that the compiler id will be NVIDIA. set(CMAKE_CUDA_COMPILER_ID "NVIDIA") @@ -78,176 +78,18 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) message(FATAL_ERROR "Clang with CUDA is not yet supported on Windows. See CMake issue #20776.") endif() - # Find the CUDA toolkit. We store the CMAKE_CUDA_COMPILER_TOOLKIT_ROOT, CMAKE_CUDA_COMPILER_TOOLKIT_VERSION and - # CMAKE_CUDA_COMPILER_LIBRARY_ROOT in CMakeCUDACompiler.cmake so FindCUDAToolkit can avoid searching on future - # runs and the toolkit is the same. - # This is very similar to FindCUDAToolkit, but somewhat simplified since we can issue fatal errors - # if we fail and we don't need to account for searching the libraries. - - # For NVCC we can easily deduce the SDK binary directory from the compiler path. - if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") - set(_CUDA_NVCC_EXECUTABLE "${CMAKE_CUDA_COMPILER}") - else() - # Search using CUDAToolkit_ROOT and then CUDA_PATH for equivalence with FindCUDAToolkit. - # In FindCUDAToolkit CUDAToolkit_ROOT is searched automatically due to being in a find_package(). - # First we search candidate non-default paths to give them priority. - find_program(_CUDA_NVCC_EXECUTABLE - NAMES nvcc nvcc.exe - PATHS ${CUDAToolkit_ROOT} - ENV CUDAToolkit_ROOT - ENV CUDA_PATH - PATH_SUFFIXES bin - NO_DEFAULT_PATH - ) - - # If we didn't find NVCC, then try the default paths. - find_program(_CUDA_NVCC_EXECUTABLE - NAMES nvcc nvcc.exe - PATH_SUFFIXES bin - ) - - # If the user specified CUDAToolkit_ROOT but nvcc could not be found, this is an error. - if(NOT _CUDA_NVCC_EXECUTABLE AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT})) - set(fail_base "Could not find nvcc executable in path specified by") - - if(DEFINED CUDAToolkit_ROOT) - message(FATAL_ERROR "${fail_base} CUDAToolkit_ROOT=${CUDAToolkit_ROOT}") - elseif(DEFINED ENV{CUDAToolkit_ROOT}) - message(FATAL_ERROR "${fail_base} environment variable CUDAToolkit_ROOT=$ENV{CUDAToolkit_ROOT}") - endif() - endif() - - # CUDAToolkit_ROOT cmake/env variable not specified, try platform defaults. - # - # - Linux: /usr/local/cuda-X.Y - # - macOS: /Developer/NVIDIA/CUDA-X.Y - # - Windows: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\vX.Y - # - # We will also search the default symlink location /usr/local/cuda first since - # if CUDAToolkit_ROOT is not specified, it is assumed that the symlinked - # directory is the desired location. - if(NOT _CUDA_NVCC_EXECUTABLE) - if(UNIX) - if(NOT APPLE) - set(platform_base "/usr/local/cuda-") - else() - set(platform_base "/Developer/NVIDIA/CUDA-") - endif() - else() - set(platform_base "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v") - endif() - - # Build out a descending list of possible cuda installations, e.g. - file(GLOB possible_paths "${platform_base}*") - # Iterate the glob results and create a descending list. - set(versions) - foreach(p ${possible_paths}) - # Extract version number from end of string - string(REGEX MATCH "[0-9][0-9]?\\.[0-9]$" p_version ${p}) - if(IS_DIRECTORY ${p} AND p_version) - list(APPEND versions ${p_version}) - endif() - endforeach() - - # Sort numerically in descending order, so we try the newest versions first. - list(SORT versions COMPARE NATURAL ORDER DESCENDING) - - # With a descending list of versions, populate possible paths to search. - set(search_paths) - foreach(v ${versions}) - list(APPEND search_paths "${platform_base}${v}") - endforeach() - - # Force the global default /usr/local/cuda to the front on Unix. - if(UNIX) - list(INSERT search_paths 0 "/usr/local/cuda") - endif() - - # Now search for nvcc again using the platform default search paths. - find_program(_CUDA_NVCC_EXECUTABLE - NAMES nvcc nvcc.exe - PATHS ${search_paths} - PATH_SUFFIXES bin - ) - - # We are done with these variables now, cleanup. - unset(platform_base) - unset(possible_paths) - unset(versions) - unset(search_paths) - - if(NOT _CUDA_NVCC_EXECUTABLE) - message(FATAL_ERROR "Failed to find nvcc.\nCompiler ${CMAKE_CUDA_COMPILER_ID} requires the CUDA toolkit. Please set the CUDAToolkit_ROOT variable.") - endif() - endif() - endif() - - # Given that NVCC can be provided by multiple different sources (NVIDIA HPC SDK, CUDA Toolkit, distro) - # each of which has a different layout, we need to extract the CUDA toolkit root from the compiler - # itself, allowing us to support numerous different scattered toolkit layouts - execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" - OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT) - if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)") - get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_MATCH_1}" ABSOLUTE) - else() - get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY) - get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY) - endif() - - if(_CUDA_NVCC_OUT MATCHES "\\#\\$ NVVMIR_LIBRARY_DIR=([^\r\n]*)") - get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${CMAKE_MATCH_1}" ABSOLUTE) - - #We require the path to end in `/nvvm/libdevice' - if(_CUDA_NVVMIR_LIBRARY_DIR MATCHES "nvvm/libdevice$") - get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}/../.." ABSOLUTE) - set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}") - endif() - - unset(_CUDA_NVVMIR_LIBRARY_DIR) - unset(_cuda_nvvmir_dir_name) - endif() - unset(_CUDA_NVCC_OUT) + # Find the CUDA toolkit to get: + # - CMAKE_CUDA_COMPILER_TOOLKIT_VERSION + # - CMAKE_CUDA_COMPILER_TOOLKIT_ROOT + # - CMAKE_CUDA_COMPILER_LIBRARY_ROOT + # We save them in CMakeCUDACompiler.cmake so FindCUDAToolkit can + # avoid searching on future runs and the toolkit is the same. + # Match arguments with cmake_cuda_architectures_all call. + include(Internal/CMakeCUDAFindToolkit) + cmake_cuda_find_toolkit(CUDA CMAKE_CUDA_COMPILER_) set(CMAKE_CUDA_DEVICE_LINKER "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/nvlink${CMAKE_EXECUTABLE_SUFFIX}") set(CMAKE_CUDA_FATBINARY "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/fatbinary${CMAKE_EXECUTABLE_SUFFIX}") - - # In a non-scattered installation the following are equivalent to CMAKE_CUDA_COMPILER_TOOLKIT_ROOT. - # We first check for a non-scattered installation to prefer it over a scattered installation. - - # CMAKE_CUDA_COMPILER_LIBRARY_ROOT contains the device library. - if(DEFINED CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR) - set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR}") - elseif(EXISTS "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvvm/libdevice") - set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}") - elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/nvvm/libdevice") - set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda") - elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/nvvm/libdevice") - set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/cuda") - else() - message(FATAL_ERROR "Couldn't find CUDA library root.") - endif() - unset(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR) - - # CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT contains the linking stubs necessary for device linking and other low-level library files. - if(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub") - set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit") - elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub") - set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit") - else() - set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}") - endif() - endif() - - # For regular nvcc we the toolkit version is the same as the compiler version and we can parse it from the vendor test output. - # For Clang we need to invoke nvcc to get version output. - if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") - if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang") - execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE CMAKE_CUDA_COMPILER_ID_OUTPUT) - endif() - - if(CMAKE_CUDA_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=]) - set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION "${CMAKE_MATCH_1}") - endif() endif() set(CMAKE_CUDA_COMPILER_ID_FLAGS_ALWAYS "-v") @@ -269,23 +111,9 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) endif() endif() - # Rest of the code treats an empty value as equivalent to "use the defaults". - # Error out early to prevent confusing errors as a result of this. - # Note that this also catches invalid non-numerical values such as "a". - if(DEFINED CMAKE_CUDA_ARCHITECTURES) - if(CMAKE_CUDA_ARCHITECTURES STREQUAL "") - message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES must be non-empty if set.") - elseif(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^([0-9]+a?(-real|-virtual)?(;[0-9]+a?(-real|-virtual)?|;)*|all|all-major|native)$") - message(FATAL_ERROR - "CMAKE_CUDA_ARCHITECTURES:\n" - " ${CMAKE_CUDA_ARCHITECTURES}\n" - "is not one of the following:\n" - " * a semicolon-separated list of integers, each optionally\n" - " followed by '-real' or '-virtual'\n" - " * a special value: all, all-major, native\n" - ) - endif() - endif() + # If the user set CMAKE_CUDA_ARCHITECTURES, validate its value. + include(Internal/CMakeCUDAArchitecturesValidate) + cmake_cuda_architectures_validate(CUDA) if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang") # Clang doesn't automatically select an architecture supported by the SDK. @@ -306,7 +134,7 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) CMAKE_DETERMINE_COMPILER_ID(CUDA CUDAFLAGS CMakeCUDACompilerId.cu) - if(${CMAKE_GENERATOR} MATCHES "Visual Studio") + if(CMAKE_GENERATOR MATCHES "Visual Studio") # Now that we have the path to nvcc, we can compute the toolkit root. get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER}" DIRECTORY) get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY) @@ -316,7 +144,12 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION ${CMAKE_CUDA_COMPILER_VERSION}) endif() - include(${CMAKE_ROOT}/Modules/CUDA/architectures.cmake) + include(Internal/CMakeCUDAArchitecturesAll) + # From CMAKE_CUDA_COMPILER_TOOLKIT_VERSION and CMAKE_CUDA_COMPILER_{ID,VERSION}, get: + # - CMAKE_CUDA_ARCHITECTURES_ALL + # - CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR + # Match arguments with cmake_cuda_find_toolkit call. + cmake_cuda_architectures_all(CUDA CMAKE_CUDA_COMPILER_) _cmake_find_compiler_sysroot(CUDA) endif() @@ -331,7 +164,7 @@ if(MSVC_CUDA_ARCHITECTURE_ID) "set(MSVC_CUDA_ARCHITECTURE_ID ${MSVC_CUDA_ARCHITECTURE_ID})") endif() -if(${CMAKE_GENERATOR} MATCHES "Visual Studio") +if(CMAKE_GENERATOR MATCHES "Visual Studio") set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${CMAKE_LINKER}") set(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES "") set(CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES "") @@ -351,11 +184,12 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio") set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")") elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang") - string(REGEX MATCHALL "-target-cpu sm_([0-9]+)" target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") + string(REGEX MATCHALL "-target-cpu sm_([0-9]+)" _clang_target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") - foreach(cpu ${target_cpus}) - string(REGEX MATCH "-target-cpu sm_([0-9]+)" dont_care "${cpu}") - list(APPEND architectures_detected "${CMAKE_MATCH_1}") + foreach(_clang_target_cpu ${_clang_target_cpus}) + if(_clang_target_cpu MATCHES "-target-cpu sm_([0-9]+)") + list(APPEND CMAKE_CUDA_ARCHITECTURES_DEFAULT "${CMAKE_MATCH_1}") + endif() endforeach() # Find target directory when crosscompiling. @@ -411,141 +245,32 @@ elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang") set(CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES "${_CUDA_LIBRARY_DIR}") set(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES "") set(CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "") -elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") - set(_nvcc_log "") - string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") - if(_nvcc_output_orig MATCHES "#\\\$ +PATH= *([^\n]*)\n") - set(_nvcc_path "${CMAKE_MATCH_1}") - string(APPEND _nvcc_log " found 'PATH=' string: [${_nvcc_path}]\n") - string(REPLACE ":" ";" _nvcc_path "${_nvcc_path}") - else() - set(_nvcc_path "") - string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") - string(APPEND _nvcc_log " no 'PATH=' string found in nvcc output:${_nvcc_output_log}\n") - endif() - if(_nvcc_output_orig MATCHES "#\\\$ +LIBRARIES= *([^\n]*)\n") - set(_nvcc_libraries "${CMAKE_MATCH_1}") - string(APPEND _nvcc_log " found 'LIBRARIES=' string: [${_nvcc_libraries}]\n") - else() - set(_nvcc_libraries "") - string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") - string(APPEND _nvcc_log " no 'LIBRARIES=' string found in nvcc output:${_nvcc_output_log}\n") - endif() - - set(_nvcc_link_line "") - if(_nvcc_libraries) - # Remove variable assignments. - string(REGEX REPLACE "#\\\$ *[^= ]+=[^\n]*\n" "" _nvcc_output "${_nvcc_output_orig}") - # Encode [] characters that break list expansion. - string(REPLACE "[" "{==={" _nvcc_output "${_nvcc_output}") - string(REPLACE "]" "}===}" _nvcc_output "${_nvcc_output}") - # Split lines. - string(REGEX REPLACE "\n+(#\\\$ )?" ";" _nvcc_output "${_nvcc_output}") - foreach(line IN LISTS _nvcc_output) - set(_nvcc_output_line "${line}") - string(REPLACE "{==={" "[" _nvcc_output_line "${_nvcc_output_line}") - string(REPLACE "}===}" "]" _nvcc_output_line "${_nvcc_output_line}") - string(APPEND _nvcc_log " considering line: [${_nvcc_output_line}]\n") - if("${_nvcc_output_line}" MATCHES "^ *nvlink") - string(APPEND _nvcc_log " ignoring nvlink line\n") - elseif(_nvcc_libraries) - if("${_nvcc_output_line}" MATCHES "(@\"?((tmp/)?a\\.exe\\.res)\"?)") - set(_nvcc_link_res_arg "${CMAKE_MATCH_1}") - set(_nvcc_link_res_file "${CMAKE_MATCH_2}") - set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerIdCUDA/${_nvcc_link_res_file}") - if(EXISTS "${_nvcc_link_res}") - file(READ "${_nvcc_link_res}" _nvcc_link_res_content) - string(REPLACE "${_nvcc_link_res_arg}" "${_nvcc_link_res_content}" _nvcc_output_line "${_nvcc_output_line}") - endif() - endif() - string(FIND "${_nvcc_output_line}" "${_nvcc_libraries}" _nvcc_libraries_pos) - if(NOT _nvcc_libraries_pos EQUAL -1) - set(_nvcc_link_line "${_nvcc_output_line}") - string(APPEND _nvcc_log " extracted link line: [${_nvcc_link_line}]\n") - endif() - endif() - endforeach() - endif() - - if(_nvcc_link_line) - if("x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC") - set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${CMAKE_LINKER}") - else() - #extract the compiler that is being used for linking - separate_arguments(_nvcc_link_line_args UNIX_COMMAND "${_nvcc_link_line}") - list(GET _nvcc_link_line_args 0 _nvcc_host_link_launcher) - if(IS_ABSOLUTE "${_nvcc_host_link_launcher}") - string(APPEND _nvcc_log " extracted link launcher absolute path: [${_nvcc_host_link_launcher}]\n") - set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}") - else() - string(APPEND _nvcc_log " extracted link launcher name: [${_nvcc_host_link_launcher}]\n") - find_program(_nvcc_find_host_link_launcher - NAMES ${_nvcc_host_link_launcher} - PATHS ${_nvcc_path} NO_DEFAULT_PATH) - find_program(_nvcc_find_host_link_launcher - NAMES ${_nvcc_host_link_launcher}) - if(_nvcc_find_host_link_launcher) - string(APPEND _nvcc_log " found link launcher absolute path: [${_nvcc_find_host_link_launcher}]\n") - set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_find_host_link_launcher}") - else() - string(APPEND _nvcc_log " could not find link launcher absolute path\n") - set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}") - endif() - unset(_nvcc_find_host_link_launcher CACHE) - endif() - endif() - #prefix the line with cuda-fake-ld so that implicit link info believes it is - #a link line - set(_nvcc_link_line "cuda-fake-ld ${_nvcc_link_line}") - CMAKE_PARSE_IMPLICIT_LINK_INFO("${_nvcc_link_line}" - CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES - CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES - CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES - log - "${CMAKE_CUDA_IMPLICIT_OBJECT_REGEX}" - LANGUAGE CUDA) - - # Detect CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT from the compiler by looking at which - # cudart library exists in the implicit link libraries passed to the host linker. - # This is required when a project sets the cuda runtime library as part of the - # initial flags. - if(";${CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart_static(\.lib)?;]]) - set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "STATIC") - elseif(";${CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart(\.lib)?;]]) - set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "SHARED") - else() - set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "NONE") - endif() - set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT - "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")") + # Don't leak variables unnecessarily to user code. + unset(_CUDA_INCLUDE_DIR) + unset(_CUDA_LIBRARY_DIR) + unset(_CUDA_TARGET_DIR) + unset(_CUDA_TARGET_NAME) +elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") + include(Internal/CMakeNVCCParseImplicitInfo) + # Parse CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT to get: + # - CMAKE_CUDA_ARCHITECTURES_DEFAULT + # - CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES + # - CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + # - CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES + # - CMAKE_CUDA_HOST_LINK_LAUNCHER + # - CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT + # - CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES + # Match arguments with cmake_nvcc_filter_implicit_info call in CMakeTestCUDACompiler. + cmake_nvcc_parse_implicit_info(CUDA CMAKE_CUDA_) - message(CONFIGURE_LOG - "Parsed CUDA nvcc implicit link information:\n${_nvcc_log}\n${log}\n\n") - else() - message(CONFIGURE_LOG - "Failed to parse CUDA nvcc implicit link information:\n${_nvcc_log}\n\n") - message(FATAL_ERROR "Failed to extract nvcc implicit link line.") - endif() + set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT + "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")") endif() -# CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES is detected above as the list of -# libraries that the CUDA compiler implicitly passes to the host linker. -# CMake invokes the host linker directly and so needs to pass these libraries. -# We filter out those that should not be passed unconditionally both here -# and from CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES in CMakeTestCUDACompiler. -set(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE - # The CUDA runtime libraries are controlled by CMAKE_CUDA_RUNTIME_LIBRARY. - cudart cudart.lib - cudart_static cudart_static.lib - cudadevrt cudadevrt.lib - - # Dependencies of the CUDA static runtime library on Linux hosts. - rt - pthread - dl - ) -list(REMOVE_ITEM CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES ${CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE}) +include(Internal/CMakeCUDAFilterImplicitLibs) +# Filter out implicit link libraries that should not be passed unconditionally. +cmake_cuda_filter_implicit_libs(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES) if(CMAKE_CUDA_COMPILER_SYSROOT) string(CONCAT _SET_CMAKE_CUDA_COMPILER_SYSROOT @@ -555,55 +280,17 @@ else() set(_SET_CMAKE_CUDA_COMPILER_SYSROOT "") endif() -# Determine CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES -if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") - set(CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES) - string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") - if(_nvcc_output_orig MATCHES "#\\\$ +INCLUDES= *([^\n]*)\n") - set(_nvcc_includes "${CMAKE_MATCH_1}") - string(APPEND _nvcc_log " found 'INCLUDES=' string: [${_nvcc_includes}]\n") - else() - set(_nvcc_includes "") - string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") - string(APPEND _nvcc_log " no 'INCLUDES=' string found in nvcc output:${_nvcc_output_log}\n") - endif() - if(_nvcc_includes) - # across all operating system each include directory is prefixed with -I - separate_arguments(_nvcc_output NATIVE_COMMAND "${_nvcc_includes}") - foreach(line IN LISTS _nvcc_output) - string(REGEX REPLACE "^-I" "" line "${line}") - get_filename_component(line "${line}" ABSOLUTE) - list(APPEND CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES "${line}") - endforeach() - - message(CONFIGURE_LOG - "Parsed CUDA nvcc include information:\n${_nvcc_log}\n${log}\n\n") - else() - message(CONFIGURE_LOG - "Failed to detect CUDA nvcc include information:\n${_nvcc_log}\n\n") - endif() - - string(REGEX MATCHALL "-arch compute_([0-9]+)" target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") - - foreach(cpu ${target_cpus}) - string(REGEX MATCH "-arch compute_([0-9]+)" dont_care "${cpu}") - list(APPEND architectures_detected "${CMAKE_MATCH_1}") - endforeach() -endif() - -# If the user didn't set the architectures, then set them to a default. -# If the user did, then make sure those architectures worked. +# If the user did not set CMAKE_CUDA_ARCHITECTURES, use the compiler's default. if("${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "") cmake_policy(GET CMP0104 _CUDA_CMP0104) - if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" OR _CUDA_CMP0104 STREQUAL "NEW") - set(CMAKE_CUDA_ARCHITECTURES "${architectures_detected}" CACHE STRING "CUDA architectures") - + set(CMAKE_CUDA_ARCHITECTURES "${CMAKE_CUDA_ARCHITECTURES_DEFAULT}" CACHE STRING "CUDA architectures") if(NOT CMAKE_CUDA_ARCHITECTURES) message(FATAL_ERROR "Failed to detect a default CUDA architecture.\n\nCompiler output:\n${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") endif() endif() endif() +unset(CMAKE_CUDA_ARCHITECTURES_DEFAULT) # configure all variables set in this file configure_file(${CMAKE_ROOT}/Modules/CMakeCUDACompiler.cmake.in @@ -611,14 +298,5 @@ configure_file(${CMAKE_ROOT}/Modules/CMakeCUDACompiler.cmake.in @ONLY ) -# Don't leak variables unnecessarily to user code. -unset(_CUDA_INCLUDE_DIR CACHE) -unset(_CUDA_NVCC_EXECUTABLE CACHE) -unset(_CUDA_LIBRARY_DIR) -unset(_CUDA_TARGET_DIR) -unset(_CUDA_TARGET_NAME) - -unset(architectures_detected) - set(CMAKE_CUDA_COMPILER_ENV_VAR "CUDACXX") set(CMAKE_CUDA_HOST_COMPILER_ENV_VAR "CUDAHOSTCXX") diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index 13bfeec..7d8fa19 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake @@ -26,13 +26,13 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src) if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG) set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}") endif() - if(lang STREQUAL "CUDA") - if(CMAKE_CUDA_ARCHITECTURES STREQUAL "native") + if(lang MATCHES "^(CUDA|HIP)$") + if(CMAKE_${lang}_ARCHITECTURES STREQUAL "native") # We are about to detect the native architectures, so we do # not yet know them. Use all architectures during detection. - set(CMAKE_CUDA_ARCHITECTURES "all") + set(CMAKE_${lang}_ARCHITECTURES "all") endif() - set(CMAKE_CUDA_RUNTIME_LIBRARY "Static") + set(CMAKE_${lang}_RUNTIME_LIBRARY "Static") endif() if(NOT "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC") # Avoid adding our own platform standard libraries for compilers diff --git a/Modules/CMakeDetermineHIPCompiler.cmake b/Modules/CMakeDetermineHIPCompiler.cmake index 9a40e82..e667099 100644 --- a/Modules/CMakeDetermineHIPCompiler.cmake +++ b/Modules/CMakeDetermineHIPCompiler.cmake @@ -5,11 +5,29 @@ include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake) include(${CMAKE_ROOT}/Modules/CMakeParseLibraryArchitecture.cmake) -if( NOT ( ("${CMAKE_GENERATOR}" MATCHES "Make") OR - ("${CMAKE_GENERATOR}" MATCHES "Ninja") ) ) +if(NOT ((CMAKE_GENERATOR MATCHES "Make") OR + (CMAKE_GENERATOR MATCHES "Ninja"))) message(FATAL_ERROR "HIP language not currently supported by \"${CMAKE_GENERATOR}\" generator") endif() +if(NOT CMAKE_HIP_PLATFORM) + execute_process(COMMAND hipconfig --platform + OUTPUT_VARIABLE _CMAKE_HIPCONFIG_PLATFORM OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT + ) + if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND _CMAKE_HIPCONFIG_PLATFORM MATCHES "^(nvidia|nvcc)$") + set(CMAKE_HIP_PLATFORM "nvidia" CACHE STRING "HIP platform" FORCE) + else() + set(CMAKE_HIP_PLATFORM "amd" CACHE STRING "HIP platform" FORCE) + endif() +endif() +if(NOT CMAKE_HIP_PLATFORM MATCHES "^(amd|nvidia)$") + message(FATAL_ERROR + "The CMAKE_HIP_PLATFORM has unsupported value:\n" + " '${CMAKE_HIP_PLATFORM}'\n" + "It must be 'amd' or 'nvidia'." + ) +endif() if(NOT CMAKE_HIP_COMPILER) set(CMAKE_HIP_COMPILER_INIT NOTFOUND) @@ -34,15 +52,19 @@ if(NOT CMAKE_HIP_COMPILER) # finally list compilers to try if(NOT CMAKE_HIP_COMPILER_INIT) - set(CMAKE_HIP_COMPILER_LIST clang++) + if(CMAKE_HIP_PLATFORM STREQUAL "nvidia") + set(CMAKE_HIP_COMPILER_LIST nvcc) + elseif(CMAKE_HIP_PLATFORM STREQUAL "amd") + set(CMAKE_HIP_COMPILER_LIST clang++) - # Look for the Clang coming with ROCm to support HIP. - execute_process(COMMAND hipconfig --hipclangpath - OUTPUT_VARIABLE _CMAKE_HIPCONFIG_CLANGPATH - RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT - ) - if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND EXISTS "${_CMAKE_HIPCONFIG_CLANGPATH}") - set(CMAKE_HIP_COMPILER_HINTS "${_CMAKE_HIPCONFIG_CLANGPATH}") + # Look for the Clang coming with ROCm to support HIP. + execute_process(COMMAND hipconfig --hipclangpath + OUTPUT_VARIABLE _CMAKE_HIPCONFIG_CLANGPATH + RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT + ) + if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND EXISTS "${_CMAKE_HIPCONFIG_CLANGPATH}") + set(CMAKE_HIP_COMPILER_HINTS "${_CMAKE_HIPCONFIG_CLANGPATH}") + endif() endif() endif() @@ -63,17 +85,65 @@ mark_as_advanced(CMAKE_HIP_COMPILER) if(NOT CMAKE_HIP_COMPILER_ID_RUN) set(CMAKE_HIP_COMPILER_ID_RUN 1) - # Try to identify the compiler. + include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) + + # We determine the vendor to use the right flags for detection right away. + # The main compiler identification is still needed below to extract other information. + list(APPEND CMAKE_HIP_COMPILER_ID_VENDORS NVIDIA Clang) + set(CMAKE_HIP_COMPILER_ID_VENDOR_REGEX_NVIDIA "nvcc: NVIDIA \\(R\\) Cuda compiler driver") + set(CMAKE_HIP_COMPILER_ID_VENDOR_REGEX_Clang "(clang version)") + CMAKE_DETERMINE_COMPILER_ID_VENDOR(HIP "--version") + + if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + # Find the CUDA toolkit to get: + # - CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION + # - CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT + # - CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT + # We save them in CMakeHIPCompiler.cmake. + # Match arguments with cmake_cuda_architectures_all call. + include(Internal/CMakeCUDAFindToolkit) + cmake_cuda_find_toolkit(HIP CMAKE_HIP_COMPILER_CUDA_) + + # If the user set CMAKE_HIP_ARCHITECTURES, validate its value. + include(Internal/CMakeCUDAArchitecturesValidate) + cmake_cuda_architectures_validate(HIP) + + if(NOT CMAKE_HIP_HOST_COMPILER AND NOT $ENV{HIPHOSTCXX} STREQUAL "") + get_filename_component(CMAKE_HIP_HOST_COMPILER $ENV{HIPHOSTCXX} PROGRAM) + if(NOT EXISTS "${CMAKE_HIP_HOST_COMPILER}") + message(FATAL_ERROR "Could not find compiler set in environment variable HIPHOSTCXX:\n$ENV{HIPHOSTCXX}.\n${CMAKE_HIP_HOST_COMPILER}") + endif() + endif() + endif() + + if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang") + list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-v") + elseif(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + # Tell nvcc to treat .hip files as CUDA sources. + list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-x cu -v") + if(CMAKE_HIP_HOST_COMPILER) + string(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST " -ccbin=\"${CMAKE_HIP_HOST_COMPILER}\"") + endif() + endif() + + # We perform compiler identification for a second time to extract implicit linking info. + # We need to unset the compiler ID otherwise CMAKE_DETERMINE_COMPILER_ID() doesn't work. set(CMAKE_HIP_COMPILER_ID) set(CMAKE_HIP_PLATFORM_ID) file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in CMAKE_HIP_COMPILER_ID_PLATFORM_CONTENT) - list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-v") - - include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake) CMAKE_DETERMINE_COMPILER_ID(HIP HIPFLAGS CMakeHIPCompilerId.hip) + if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + include(Internal/CMakeCUDAArchitecturesAll) + # From CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION and CMAKE_HIP_COMPILER_{ID,VERSION}, get: + # - CMAKE_HIP_ARCHITECTURES_ALL + # - CMAKE_HIP_ARCHITECTURES_ALL_MAJOR + # Match arguments with cmake_cuda_find_toolkit call. + cmake_cuda_architectures_all(HIP CMAKE_HIP_COMPILER_CUDA_) + endif() + _cmake_find_compiler_sysroot(HIP) endif() @@ -104,56 +174,61 @@ if(NOT CMAKE_HIP_COMPILER_ROCM_ROOT) message(FATAL_ERROR "Failed to find ROCm root directory.") endif() -# Normally implicit link information is not detected until -cmake_parse_implicit_link_info("${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}" - _CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS - _CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS - _CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS - _CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG - "" LANGUAGE HIP) -message(CONFIGURE_LOG - "Parsed HIP implicit link information from compiler id output:\n${_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG}\n\n") -cmake_parse_library_architecture(HIP "${_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS}" "" CMAKE_HIP_LIBRARY_ARCHITECTURE) -if(CMAKE_HIP_LIBRARY_ARCHITECTURE) +if(CMAKE_HIP_PLATFORM STREQUAL "amd") + # For this platform we need the hip-lang cmake package. + + # Normally implicit link information is not detected until ABI detection, + # but we need to populate CMAKE_HIP_LIBRARY_ARCHITECTURE to find hip-lang. + cmake_parse_implicit_link_info("${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}" + _CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS + _CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS + _CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS + _CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG + "" LANGUAGE HIP) message(CONFIGURE_LOG - "Parsed HIP library architecture from compiler id output: ${CMAKE_HIP_LIBRARY_ARCHITECTURE}\n") -endif() -unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS) -unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS) -unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS) -unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG) - -if(NOT CMAKE_HIP_COMPILER_ROCM_LIB) - set(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS - "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib" - "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib64" - ) + "Parsed HIP implicit link information from compiler id output:\n${_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG}\n\n") + cmake_parse_library_architecture(HIP "${_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS}" "" CMAKE_HIP_LIBRARY_ARCHITECTURE) if(CMAKE_HIP_LIBRARY_ARCHITECTURE) - list(APPEND _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib/${CMAKE_HIP_LIBRARY_ARCHITECTURE}") + message(CONFIGURE_LOG + "Parsed HIP library architecture from compiler id output: ${CMAKE_HIP_LIBRARY_ARCHITECTURE}\n") endif() - foreach(dir IN LISTS _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS) - if(EXISTS "${dir}/cmake/hip-lang/hip-lang-config.cmake") - set(CMAKE_HIP_COMPILER_ROCM_LIB "${dir}") - break() - endif() - endforeach() + unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS) + unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS) + unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS) + unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG) + if(NOT CMAKE_HIP_COMPILER_ROCM_LIB) - list(TRANSFORM _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS APPEND "/cmake/hip-lang/hip-lang-config.cmake") - string(REPLACE ";" "\n " _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}") - message(FATAL_ERROR - "The ROCm root directory:\n" - " ${CMAKE_HIP_COMPILER_ROCM_ROOT}\n" - "does not contain the HIP runtime CMake package, expected at one of:\n" - " ${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}\n" + set(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS + "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib" + "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib64" ) + if(CMAKE_HIP_LIBRARY_ARCHITECTURE) + list(APPEND _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib/${CMAKE_HIP_LIBRARY_ARCHITECTURE}") + endif() + foreach(dir IN LISTS _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS) + if(EXISTS "${dir}/cmake/hip-lang/hip-lang-config.cmake") + set(CMAKE_HIP_COMPILER_ROCM_LIB "${dir}") + break() + endif() + endforeach() + if(NOT CMAKE_HIP_COMPILER_ROCM_LIB) + list(TRANSFORM _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS APPEND "/cmake/hip-lang/hip-lang-config.cmake") + string(REPLACE ";" "\n " _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}") + message(FATAL_ERROR + "The ROCm root directory:\n" + " ${CMAKE_HIP_COMPILER_ROCM_ROOT}\n" + "does not contain the HIP runtime CMake package, expected at one of:\n" + " ${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}\n" + ) + endif() + unset(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS) + endif() + if(CMAKE_HIP_COMPILER_ROCM_LIB MATCHES "/lib64$" AND 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. + set(CMAKE_HIP_SIZEOF_DATA_PTR 8) endif() - unset(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS) -endif() -if(CMAKE_HIP_COMPILER_ROCM_LIB MATCHES "/lib64$" AND 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. - set(CMAKE_HIP_SIZEOF_DATA_PTR 8) endif() if (NOT _CMAKE_TOOLCHAIN_LOCATION) @@ -165,6 +240,26 @@ include(CMakeFindBinUtils) include(Compiler/${CMAKE_HIP_COMPILER_ID}-FindBinUtils OPTIONAL) unset(_CMAKE_PROCESSING_LANGUAGE) +if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang") + set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "SHARED") +elseif(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + include(Internal/CMakeNVCCParseImplicitInfo) + # Parse CMAKE_HIP_COMPILER_PRODUCED_OUTPUT to get: + # - CMAKE_HIP_ARCHITECTURES_DEFAULT + # - CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES + # - CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + # - CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES + # - CMAKE_HIP_HOST_LINK_LAUNCHER + # - CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT + # - CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES + # Match arguments with cmake_nvcc_filter_implicit_info call in CMakeTestHIPCompiler. + cmake_nvcc_parse_implicit_info(HIP CMAKE_HIP_CUDA_) + + include(Internal/CMakeCUDAFilterImplicitLibs) + # Filter out implicit link libraries that should not be passed unconditionally. + cmake_cuda_filter_implicit_libs(CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES) +endif() + if(CMAKE_HIP_COMPILER_SYSROOT) string(CONCAT _SET_CMAKE_HIP_COMPILER_SYSROOT "set(CMAKE_HIP_COMPILER_SYSROOT \"${CMAKE_HIP_COMPILER_SYSROOT}\")\n" @@ -185,7 +280,20 @@ if(MSVC_HIP_ARCHITECTURE_ID) "set(MSVC_HIP_ARCHITECTURE_ID ${MSVC_HIP_ARCHITECTURE_ID})") endif() -if(NOT DEFINED CMAKE_HIP_ARCHITECTURES) +if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + if(NOT "$ENV{CUDAARCHS}" STREQUAL "") + set(CMAKE_HIP_ARCHITECTURES "$ENV{CUDAARCHS}" CACHE STRING "CUDA architectures") + endif() + + # If the user did not set CMAKE_HIP_ARCHITECTURES, use the compiler's default. + if("${CMAKE_HIP_ARCHITECTURES}" STREQUAL "") + set(CMAKE_HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES_DEFAULT}" CACHE STRING "HIP architectures" FORCE) + if(NOT CMAKE_HIP_ARCHITECTURES) + message(FATAL_ERROR "Failed to detect a default HIP architecture.\n\nCompiler output:\n${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}") + endif() + endif() + unset(CMAKE_HIP_ARCHITECTURES_DEFAULT) +elseif(NOT DEFINED CMAKE_HIP_ARCHITECTURES) # Use 'rocm_agent_enumerator' to get the current GPU architecture. set(_CMAKE_HIP_ARCHITECTURES) find_program(_CMAKE_HIP_ROCM_AGENT_ENUMERATOR diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in index 0fa5bf0..6d5e62a 100644 --- a/Modules/CMakeHIPCompiler.cmake.in +++ b/Modules/CMakeHIPCompiler.cmake.in @@ -1,4 +1,6 @@ set(CMAKE_HIP_COMPILER "@CMAKE_HIP_COMPILER@") +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_STANDARD_COMPUTED_DEFAULT "@CMAKE_HIP_STANDARD_COMPUTED_DEFAULT@") @@ -45,14 +47,27 @@ if(CMAKE_HIP_LIBRARY_ARCHITECTURE) set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_HIP_LIBRARY_ARCHITECTURE@") endif() -set(CMAKE_HIP_TOOLKIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_TOOLKIT_INCLUDE_DIRECTORIES@") +set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT@") +set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_LIBRARY_ROOT "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_LIBRARY_ROOT@") +set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION@") +set(CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT "@CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT@") + +set(CMAKE_HIP_ARCHITECTURES_ALL "@CMAKE_HIP_ARCHITECTURES_ALL@") +set(CMAKE_HIP_ARCHITECTURES_ALL_MAJOR "@CMAKE_HIP_ARCHITECTURES_ALL_MAJOR@") +set(CMAKE_HIP_ARCHITECTURES_NATIVE "@CMAKE_HIP_ARCHITECTURES_NATIVE@") + +set(CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES@") + +set(CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES@") +set(CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES@") +set(CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") set(CMAKE_HIP_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_IMPLICIT_INCLUDE_DIRECTORIES@") set(CMAKE_HIP_IMPLICIT_LINK_LIBRARIES "@CMAKE_HIP_IMPLICIT_LINK_LIBRARIES@") set(CMAKE_HIP_IMPLICIT_LINK_DIRECTORIES "@CMAKE_HIP_IMPLICIT_LINK_DIRECTORIES@") set(CMAKE_HIP_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_HIP_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@") -set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "SHARED") +set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "@CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT@") set(CMAKE_AR "@CMAKE_AR@") set(CMAKE_HIP_COMPILER_AR "@CMAKE_HIP_COMPILER_AR@") diff --git a/Modules/CMakeHIPCompilerABI.hip b/Modules/CMakeHIPCompilerABI.hip index 6c912bd..7d8b815 100644 --- a/Modules/CMakeHIPCompilerABI.hip +++ b/Modules/CMakeHIPCompilerABI.hip @@ -1,9 +1,13 @@ -#ifndef __HIP__ +#if !defined(__HIP__) && !defined(__NVCC__) # error "A C or C++ compiler has been selected for HIP" #endif #include "CMakeCompilerABI.h" +#if defined(__NVCC__) +# include "CMakeCompilerCUDAArch.h" +#endif + int main(int argc, char* argv[]) { int require = 0; @@ -11,6 +15,16 @@ int main(int argc, char* argv[]) #if defined(ABI_ID) require += info_abi[argc]; #endif - (void)argv; + static_cast<void>(argv); + +#if defined(__NVCC__) + if (!cmakeCompilerCUDAArch()) { + // Convince the compiler that the non-zero return value depends + // on the info strings so they are not optimized out. + return require ? -1 : 1; + } + return 0; +#else return require; +#endif } diff --git a/Modules/CMakeHIPCompilerId.hip.in b/Modules/CMakeHIPCompilerId.hip.in index 3c4a1d4..4ac0f30 100644 --- a/Modules/CMakeHIPCompilerId.hip.in +++ b/Modules/CMakeHIPCompilerId.hip.in @@ -1,4 +1,4 @@ -#ifndef __HIP__ +#if !defined(__HIP__) && !defined(__NVCC__) # error "A C or C++ compiler has been selected for HIP" #endif diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake index 41a98db..3995c36 100644 --- a/Modules/CMakeHIPInformation.cmake +++ b/Modules/CMakeHIPInformation.cmake @@ -8,6 +8,19 @@ else() endif() set(CMAKE_INCLUDE_FLAG_HIP "-I") +# Set implicit links early so compiler-specific modules can use them. +set(__IMPLICIT_LINKS) +foreach(dir ${CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES}) + string(APPEND __IMPLICIT_LINKS " -L\"${dir}\"") +endforeach() +foreach(lib ${CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES}) + if(${lib} MATCHES "/") + string(APPEND __IMPLICIT_LINKS " \"${lib}\"") + else() + string(APPEND __IMPLICIT_LINKS " -l${lib}") + endif() +endforeach() + # Load compiler-specific information. if(CMAKE_HIP_COMPILER_ID) include(Compiler/${CMAKE_HIP_COMPILER_ID}-HIP OPTIONAL) @@ -129,7 +142,7 @@ endif() # compile a HIP file into an object file if(NOT CMAKE_HIP_COMPILE_OBJECT) set(CMAKE_HIP_COMPILE_OBJECT - "<CMAKE_HIP_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> ${_CMAKE_COMPILE_AS_HIP_FLAG} -c <SOURCE>") + "<CMAKE_HIP_COMPILER> ${_CMAKE_HIP_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> ${_CMAKE_COMPILE_AS_HIP_FLAG} -c <SOURCE>") endif() # compile a cu file into an executable diff --git a/Modules/CMakeTestCUDACompiler.cmake b/Modules/CMakeTestCUDACompiler.cmake index 5779e4b..3057fe9 100644 --- a/Modules/CMakeTestCUDACompiler.cmake +++ b/Modules/CMakeTestCUDACompiler.cmake @@ -22,51 +22,10 @@ if(CMAKE_CUDA_ABI_COMPILED) set(CMAKE_CUDA_COMPILER_WORKS TRUE) message(STATUS "Check for working CUDA compiler: ${CMAKE_CUDA_COMPILER} - skipped") - # Run the test binary to detect the native architectures. - execute_process(COMMAND "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_CUDA.bin" - RESULT_VARIABLE _CUDA_ARCHS_RESULT - OUTPUT_VARIABLE _CUDA_ARCHS_OUTPUT - ERROR_VARIABLE _CUDA_ARCHS_OUTPUT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(_CUDA_ARCHS_RESULT EQUAL 0) - if("$ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}") - # Undocumented hook used by CMake's CI. - # Clamp native architecture to version range supported by this CUDA. - list(GET CMAKE_CUDA_ARCHITECTURES_ALL 0 _CUDA_ARCH_MIN) - list(GET CMAKE_CUDA_ARCHITECTURES_ALL -1 _CUDA_ARCH_MAX) - set(CMAKE_CUDA_ARCHITECTURES_NATIVE "") - foreach(_CUDA_ARCH IN LISTS _CUDA_ARCHS_OUTPUT) - if(_CUDA_ARCH LESS _CUDA_ARCH_MIN) - set(_CUDA_ARCH "${_CUDA_ARCH_MIN}") - endif() - if(_CUDA_ARCH GREATER _CUDA_ARCH_MAX) - set(_CUDA_ARCH "${_CUDA_ARCH_MAX}") - endif() - list(APPEND CMAKE_CUDA_ARCHITECTURES_NATIVE ${_CUDA_ARCH}) - endforeach() - unset(_CUDA_ARCH) - unset(_CUDA_ARCH_MIN) - unset(_CUDA_ARCH_MAX) - else() - set(CMAKE_CUDA_ARCHITECTURES_NATIVE "${_CUDA_ARCHS_OUTPUT}") - endif() - list(REMOVE_DUPLICATES CMAKE_CUDA_ARCHITECTURES_NATIVE) - list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_NATIVE APPEND "-real") - else() - if(NOT _CUDA_ARCHS_RESULT MATCHES "[0-9]+") - set(_CUDA_ARCHS_STATUS " (${_CUDA_ARCHS_RESULT})") - else() - set(_CUDA_ARCHS_STATUS "") - endif() - string(REPLACE "\n" "\n " _CUDA_ARCHS_OUTPUT " ${_CUDA_ARCHS_OUTPUT}") - message(CONFIGURE_LOG - "Detecting the CUDA native architecture(s) failed with " - "the following output:\n${_CUDA_ARCHS_OUTPUT}\n\n") - endif() - unset(_CUDA_ARCHS_EXE) - unset(_CUDA_ARCHS_RESULT) - unset(_CUDA_ARCHS_OUTPUT) + include(Internal/CMakeCUDAArchitecturesNative) + # Run the test binary to get: + # - CMAKE_CUDA_ARCHITECTURES_NATIVE + cmake_cuda_architectures_native(CUDA) endif() # This file is used by EnableLanguage in cmGlobalGenerator to @@ -114,22 +73,14 @@ if("x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC") set(CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES "${CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES}") endif() +include(Internal/CMakeCUDAFilterImplicitLibs) # Filter out implicit link libraries that should not be passed unconditionally. -# See CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE in CMakeDetermineCUDACompiler. -list(REMOVE_ITEM CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES ${CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE}) +cmake_cuda_filter_implicit_libs(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES) if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") - # Remove the CUDA Toolkit include directories from the set of - # implicit system include directories. - # This resolves the issue that NVCC doesn't specify these - # includes as SYSTEM includes when compiling device code, and sometimes - # they contain headers that generate warnings, so let users mark them - # as SYSTEM explicitly - if(CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES) - list(REMOVE_ITEM CMAKE_CUDA_IMPLICIT_INCLUDE_DIRECTORIES - ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES} - ) - endif() + include(Internal/CMakeNVCCFilterImplicitInfo) + # Match arguments with cmake_nvcc_parse_implicit_info call in CMakeDetermineCUDACompiler. + cmake_nvcc_filter_implicit_info(CUDA CMAKE_CUDA_) endif() # Re-configure to save learned information. diff --git a/Modules/CMakeTestHIPCompiler.cmake b/Modules/CMakeTestHIPCompiler.cmake index 686f055..ec54d80 100644 --- a/Modules/CMakeTestHIPCompiler.cmake +++ b/Modules/CMakeTestHIPCompiler.cmake @@ -10,7 +10,10 @@ if(CMAKE_HIP_COMPILER_FORCED) endif() set(__CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS}") -string(APPEND CMAKE_HIP_FLAGS " --cuda-host-only") + +if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang") + string(APPEND CMAKE_HIP_FLAGS " --cuda-host-only") +endif() include(CMakeTestCompilerCommon) @@ -31,6 +34,13 @@ if(CMAKE_HIP_ABI_COMPILED) # The compiler worked so skip dedicated test below. set(CMAKE_HIP_COMPILER_WORKS TRUE) message(STATUS "Check for working HIP compiler: ${CMAKE_HIP_COMPILER} - skipped") + + if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + include(Internal/CMakeCUDAArchitecturesNative) + # Run the test binary to get: + # - CMAKE_HIP_ARCHITECTURES_NATIVE + cmake_cuda_architectures_native(HIP) + endif() endif() # This file is used by EnableLanguage in cmGlobalGenerator to @@ -42,7 +52,7 @@ if(NOT CMAKE_HIP_COMPILER_WORKS) PrintTestCompilerStatus("HIP") __TestCompiler_setTryCompileTargetType() string(CONCAT __TestCompiler_testHIPCompilerSource - "#ifndef __HIP__\n" + "#if !defined(__HIP__) && !defined(__NVCC__)\n" "# error \"The CMAKE_HIP_COMPILER is set to a C/CXX compiler\"\n" "#endif\n" "int main(){return 0;}\n") @@ -76,6 +86,16 @@ unset(__CMAKE_HIP_FLAGS) include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake) CMAKE_DETERMINE_COMPILE_FEATURES(HIP) +if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + include(Internal/CMakeNVCCFilterImplicitInfo) + # Match arguments with cmake_nvcc_parse_implicit_info call in CMakeDetermineHIPCompiler. + cmake_nvcc_filter_implicit_info(HIP CMAKE_HIP_CUDA_) + + include(Internal/CMakeCUDAFilterImplicitLibs) + # Filter out implicit link libraries that should not be passed unconditionally. + cmake_cuda_filter_implicit_libs(CMAKE_HIP_IMPLICIT_LINK_LIBRARIES) +endif() + # Re-configure to save learned information. configure_file( ${CMAKE_ROOT}/Modules/CMakeHIPCompiler.cmake.in diff --git a/Modules/CUDA/architectures.cmake b/Modules/CUDA/architectures.cmake deleted file mode 100644 index 7d6a6e0..0000000 --- a/Modules/CUDA/architectures.cmake +++ /dev/null @@ -1,69 +0,0 @@ -# See supported GPUs on Wikipedia -# https://en.wikipedia.org/wiki/CUDA#GPUs_supported - -# Initial set based on CUDA 7.0. -set(CMAKE_CUDA_ARCHITECTURES_ALL 20 21 30 35 37 50 52 53) -set(CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20 30 35 50) - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 8.0) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 60 61 62) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 60) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 9.0) - if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 70 72) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 70) - endif() - - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 20 21) - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 10.0 - AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 75) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.0) - if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 80) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 80) - endif() - - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 30) - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 30) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.1 - AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0)) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 86) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4 - AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8 - AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90) - list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90) -endif() - -if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0 - AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")) - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37) - list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35) -endif() - -# only generate jit code for the newest arch for all/all-major -list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch) -list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL APPEND "-real") -list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL ${_latest_arch}) - -list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR _latest_arch) -list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR APPEND "-real") -list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR ${_latest_arch}) - -unset(_latest_arch) diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake index 04e721c..94948b9 100644 --- a/Modules/CheckLanguage.cmake +++ b/Modules/CheckLanguage.cmake @@ -25,8 +25,8 @@ or :command:`project` commands: If this variable is already set, either explicitly or cached by a previous call, the check is skipped. - :variable:`CMAKE_CUDA_HOST_COMPILER` - This variable is set when ``<lang>`` is ``CUDA``. + :variable:`CMAKE_<LANG>_HOST_COMPILER` + This variable is set when ``<lang>`` is ``CUDA`` or ``HIP``. If the check detects an explicit host compiler that is required for compilation, this variable will be set to that compiler. @@ -34,12 +34,12 @@ or :command:`project` commands: this variable will be cleared. If this variable is already set, its value is preserved only if - :variable:`CMAKE_CUDA_COMPILER <CMAKE_<LANG>_COMPILER>` is also set. + :variable:`CMAKE_<LANG>_COMPILER` is also set. Otherwise, the check runs and overwrites - :variable:`CMAKE_CUDA_HOST_COMPILER` with a new result. - Note that :variable:`CMAKE_CUDA_HOST_COMPILER` documents it should + :variable:`CMAKE_<LANG>_HOST_COMPILER` with a new result. + Note that :variable:`CMAKE_<LANG>_HOST_COMPILER` documents it should not be set without also setting - :variable:`CMAKE_CUDA_COMPILER <CMAKE_<LANG>_COMPILER>` to a NVCC compiler. + :variable:`CMAKE_<LANG>_COMPILER` to a NVCC compiler. For example: @@ -65,7 +65,7 @@ macro(check_language lang) file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang}) set(extra_compiler_variables) - if(${lang} STREQUAL CUDA AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") + if("${lang}" MATCHES "^(CUDA|HIP)$" AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") set(extra_compiler_variables "set(CMAKE_CUDA_HOST_COMPILER \\\"\${CMAKE_CUDA_HOST_COMPILER}\\\")") endif() diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake index 46f5fc1..e5683c2 100644 --- a/Modules/Compiler/Clang.cmake +++ b/Modules/Compiler/Clang.cmake @@ -173,7 +173,12 @@ macro(__compiler_clang_cxx_standards lang) unset(_clang_version_std17) - if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 17.0) + set(_clang_version_std23 17.0) + if(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(_clang_version_std23 18.0) + endif() + + if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS "${_clang_version_std23}") set(CMAKE_${lang}23_STANDARD_COMPILE_OPTION "-std=c++23") set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu++23") set(CMAKE_${lang}26_STANDARD_COMPILE_OPTION "-std=c++26") @@ -183,6 +188,8 @@ macro(__compiler_clang_cxx_standards lang) set(CMAKE_${lang}23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") endif() + unset(_clang_version_std23) + if("x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC") # The MSVC standard library requires C++14, and MSVC itself has no # notion of operating in a mode not aware of at least that standard. diff --git a/Modules/Compiler/CrayClang-C.cmake b/Modules/Compiler/CrayClang-C.cmake new file mode 100644 index 0000000..bf878fc --- /dev/null +++ b/Modules/Compiler/CrayClang-C.cmake @@ -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(Compiler/CrayClang) +__compiler_cray_clang(C) + +set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c) + +string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -DNDEBUG") +string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG") + +set(CMAKE_C90_STANDARD_COMPILE_OPTION -std=c90) +set(CMAKE_C90_EXTENSION_COMPILE_OPTION -std=gnu90) +set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_C99_STANDARD_COMPILE_OPTION -std=c99) +set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=gnu99) +set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11) +set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=gnu11) +set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_C17_STANDARD_COMPILE_OPTION -std=c17) +set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=gnu17) + +set(CMAKE_C23_STANDARD_COMPILE_OPTION -std=c2x) +set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=gnu2x) + +__compiler_check_default_language_standard(C 15.0.0 17) diff --git a/Modules/Compiler/CrayClang-CXX.cmake b/Modules/Compiler/CrayClang-CXX.cmake new file mode 100644 index 0000000..de6a53c --- /dev/null +++ b/Modules/Compiler/CrayClang-CXX.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. + +include(Compiler/CrayClang) +__compiler_cray_clang(CXX) + + +set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++) +set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden") + +string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL_INIT " -DNDEBUG") +string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " -DNDEBUG") + +set(CMAKE_CXX98_STANDARD_COMPILE_OPTION -std=c++98) +set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -std=gnu++98) +set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_CXX11_STANDARD_COMPILE_OPTION -std=c++11) +set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -std=gnu++11) +set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_CXX14_STANDARD_COMPILE_OPTION -std=c++14) +set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -std=gnu++14) +set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_CXX17_STANDARD_COMPILE_OPTION -std=c++17) +set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION -std=gnu++17) + +set(CMAKE_CXX20_STANDARD_COMPILE_OPTION -std=c++20) +set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION -std=gnu++20) + +set(CMAKE_CXX23_STANDARD_COMPILE_OPTION -std=c++2b) +set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION -std=gnu++2b) + +__compiler_check_default_language_standard(CXX 15.0.0 14) diff --git a/Modules/Compiler/CrayClang-DetermineCompiler.cmake b/Modules/Compiler/CrayClang-DetermineCompiler.cmake new file mode 100644 index 0000000..1828444 --- /dev/null +++ b/Modules/Compiler/CrayClang-DetermineCompiler.cmake @@ -0,0 +1,8 @@ +set(_compiler_id_pp_test "defined(__clang__) && defined(__cray__)") + +set(_compiler_id_version_compute " +# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__cray_major__) +# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__cray_minor__) +# define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__cray_patchlevel__) +# define @PREFIX@COMPILER_VERSION_INTERNAL_STR __clang_version__ +") diff --git a/Modules/Compiler/CrayClang.cmake b/Modules/Compiler/CrayClang.cmake new file mode 100644 index 0000000..d2db9dd --- /dev/null +++ b/Modules/Compiler/CrayClang.cmake @@ -0,0 +1,17 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# This module is shared by multiple languages; use include blocker. +if(__COMPILER_CRAYCLANG) + return() +endif() +set(__COMPILER_CRAYCLANG 1) + +include(Compiler/Clang) + +macro (__compiler_cray_clang lang) + set(__crayclang_ver "${CMAKE_${lang}_COMPILER_VERSION}") + set("CMAKE_${lang}_COMPILER_VERSION" "${CMAKE_${lang}_COMPILER_VERSION_INTERNAL}") + __compiler_clang(${lang}) + set("CMAKE_${lang}_COMPILER_VERSION" "${__crayclang_ver}") +endmacro () diff --git a/Modules/Compiler/GNU-CXX.cmake b/Modules/Compiler/GNU-CXX.cmake index b35f254..c276f4e 100644 --- a/Modules/Compiler/GNU-CXX.cmake +++ b/Modules/Compiler/GNU-CXX.cmake @@ -72,3 +72,24 @@ elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) endif() __compiler_check_default_language_standard(CXX 3.4 98 6.0 14 11.1 17) + +if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) + string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE + "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>" + " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>" + " -fmodules-ts -fdeps-file=<DYNDEP_FILE> -fdeps-target=<OBJECT> -fdeps-format=p1689r5" + " -o <PREPROCESSED_SOURCE>") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc") + string(CONCAT CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG + # Turn on modules. + "-fmodules-ts" + # Read the module mapper file. + " -fmodule-mapper=<MODULE_MAP_FILE>" + # Make sure dependency tracking is enabled (missing from `try_*`). + " -MD" + # Suppress `CXX_MODULES +=` from generated depfile snippets. + " -fdeps-format=p1689r5" + # Force C++ as a language. + " -x c++") + set(CMAKE_EXPERIMENTAL_CXX_MODULE_BMI_ONLY_FLAG "-fmodule-only") +endif() diff --git a/Modules/Compiler/IBMClang-CXX.cmake b/Modules/Compiler/IBMClang-CXX.cmake index 5431b17..be9b525 100644 --- a/Modules/Compiler/IBMClang-CXX.cmake +++ b/Modules/Compiler/IBMClang-CXX.cmake @@ -31,6 +31,8 @@ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 17.1.0) set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17") set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++20") set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20") + set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++2b") + set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b") endif() __compiler_check_default_language_standard(CXX 17.1.0 17) diff --git a/Modules/Compiler/IntelLLVM.cmake b/Modules/Compiler/IntelLLVM.cmake index e256c8f..f3c0bf4 100644 --- a/Modules/Compiler/IntelLLVM.cmake +++ b/Modules/Compiler/IntelLLVM.cmake @@ -44,6 +44,13 @@ else() string(APPEND CMAKE_${lang}_FLAGS_INIT " ") string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 2023.0.0) + if("x${lang}" STREQUAL "xFortran") + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -diag-disable:10440") + else() + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Rno-debug-disables-optimization") + endif() + endif() string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os") string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3") string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g") diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake index 13d8d8e..93ad182 100644 --- a/Modules/Compiler/NVIDIA-CUDA.cmake +++ b/Modules/Compiler/NVIDIA-CUDA.cmake @@ -1,11 +1,9 @@ include(Compiler/NVIDIA) __compiler_nvidia_cxx_standards(CUDA) +__compiler_nvidia_cuda_flags(CUDA) set(CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE True) -set(CMAKE_CUDA_VERBOSE_FLAG "-v") -set(CMAKE_CUDA_VERBOSE_COMPILE_FLAG "-Xcompiler=-v") -set(_CMAKE_COMPILE_AS_CUDA_FLAG "-x cu") set(_CMAKE_CUDA_WHOLE_FLAG "-c") set(_CMAKE_CUDA_RDC_FLAG "-rdc=true") set(_CMAKE_CUDA_PTX_FLAG "-ptx") @@ -15,101 +13,11 @@ if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.7.0") set(_CMAKE_CUDA_OPTIX_FLAG "-optix-ir") endif() -if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) - # The -forward-unknown-to-host-compiler flag was only - # added to nvcc in 10.2 so before that we had no good - # way to invoke the CUDA compiler and propagate unknown - # flags such as -pthread to the host compiler - set(_CMAKE_CUDA_EXTRA_FLAGS "-forward-unknown-to-host-compiler") -else() - set(_CMAKE_CUDA_EXTRA_FLAGS "") -endif() - -if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0") - set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "-Wno-deprecated-gpu-targets") -else() - set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "") -endif() - -if(CMAKE_CUDA_HOST_COMPILER AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") - string(APPEND _CMAKE_CUDA_EXTRA_FLAGS " -ccbin=<CMAKE_CUDA_HOST_COMPILER>") -endif() - -if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) - # Starting in 10.2, nvcc supported treating all warnings as errors - set(CMAKE_CUDA_COMPILE_OPTIONS_WARNING_AS_ERROR "-Werror" "all-warnings") -endif() - -if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) - # The -MD flag was only added to nvcc in 10.2 so - # before that we had to invoke the compiler twice - # to get header dependency information - set(CMAKE_DEPFILE_FLAGS_CUDA "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") -else() - set(CMAKE_CUDA_DEPENDS_EXTRA_COMMANDS "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> ${_CMAKE_COMPILE_AS_CUDA_FLAG} -M <SOURCE> -MT <OBJECT> -o <DEP_FILE>") -endif() -set(CMAKE_CUDA_DEPFILE_FORMAT gcc) -if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) - AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") - set(CMAKE_CUDA_DEPENDS_USE_COMPILER TRUE) -endif() - if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2) - set(_CMAKE_CUDA_IPO_SUPPORTED_BY_CMAKE YES) - set(_CMAKE_CUDA_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) - set(CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO " -dlto") endif() -if(NOT "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC") - set(CMAKE_CUDA_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE) - set(CMAKE_CUDA_COMPILE_OPTIONS_PIC -Xcompiler=-fPIC) - set(CMAKE_CUDA_COMPILE_OPTIONS_VISIBILITY -Xcompiler=-fvisibility=) - # CMAKE_SHARED_LIBRARY_CUDA_FLAGS is sent to the host linker so we - # don't need to forward it through nvcc. - set(CMAKE_SHARED_LIBRARY_CUDA_FLAGS -fPIC) - string(APPEND CMAKE_CUDA_FLAGS_INIT " ") - string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -g") - string(APPEND CMAKE_CUDA_FLAGS_RELEASE_INIT " -O3 -DNDEBUG") - string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG") - string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") -endif() - -set(CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS -shared) -set(CMAKE_INCLUDE_SYSTEM_FLAG_CUDA "-isystem ") - -if (CMAKE_CUDA_SIMULATE_ID STREQUAL "GNU") - set(CMAKE_CUDA_LINKER_WRAPPER_FLAG "-Wl,") - set(CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP ",") -elseif(CMAKE_CUDA_SIMULATE_ID STREQUAL "Clang") - set(CMAKE_CUDA_LINKER_WRAPPER_FLAG "-Xlinker" " ") - set(CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP) -endif() - set(CMAKE_CUDA_DEVICE_COMPILER_WRAPPER_FLAG "-Xcompiler=") set(CMAKE_CUDA_DEVICE_COMPILER_WRAPPER_FLAG_SEP ",") set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG "-Xlinker=") set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG_SEP ",") - -set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "cudadevrt;cudart_static") -set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_SHARED "cudadevrt;cudart") -set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_NONE "") - -if(UNIX AND NOT (CMAKE_SYSTEM_NAME STREQUAL "QNX")) - list(APPEND CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl") -endif() - -if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0") - set(CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ") - set(CMAKE_CUDA_RESPONSE_FILE_FLAG "--options-file ") -endif() - -if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0") - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 1) - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 1) - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 1) -else() - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0) - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0) - set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0) -endif() diff --git a/Modules/Compiler/NVIDIA-HIP.cmake b/Modules/Compiler/NVIDIA-HIP.cmake new file mode 100644 index 0000000..c888bc7 --- /dev/null +++ b/Modules/Compiler/NVIDIA-HIP.cmake @@ -0,0 +1,14 @@ +include(Compiler/NVIDIA) +__compiler_nvidia_cxx_standards(HIP) +__compiler_nvidia_cuda_flags(HIP) + +# The ROCm hip-lang cmake package's device runtime library is not needed for NVIDIA GPUs. +set(_CMAKE_HIP_DEVICE_RUNTIME_TARGET "") + +set(CMAKE_HIP_STANDARD_INCLUDE_DIRECTORIES "${CMAKE_HIP_COMPILER_ROCM_ROOT}/include") + +set(CMAKE_HIP_LINK_EXECUTABLE + "<CMAKE_HIP_HOST_LINK_LAUNCHER> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>${__IMPLICIT_LINKS}") +set(CMAKE_HIP_CREATE_SHARED_LIBRARY + "<CMAKE_HIP_HOST_LINK_LAUNCHER> <CMAKE_SHARED_LIBRARY_HIP_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_HIP_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>${__IMPLICIT_LINKS}") +set(CMAKE_HIP_CREATE_SHARED_MODULE "${CMAKE_HIP_CREATE_SHARED_LIBRARY}") diff --git a/Modules/Compiler/NVIDIA.cmake b/Modules/Compiler/NVIDIA.cmake index 686c03d..a126c57 100644 --- a/Modules/Compiler/NVIDIA.cmake +++ b/Modules/Compiler/NVIDIA.cmake @@ -67,3 +67,102 @@ macro(__compiler_nvidia_cxx_standards lang) __compiler_check_default_language_standard(${lang} 6.0 03) endmacro() + +macro(__compiler_nvidia_cuda_flags lang) + set(CMAKE_${lang}_VERBOSE_FLAG "-v") + set(CMAKE_${lang}_VERBOSE_COMPILE_FLAG "-Xcompiler=-v") + set(_CMAKE_COMPILE_AS_${lang}_FLAG "-x cu") + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) + # The -forward-unknown-to-host-compiler flag was only + # added to nvcc in 10.2 so before that we had no good + # way to invoke the NVCC compiler and propagate unknown + # flags such as -pthread to the host compiler + set(_CMAKE_${lang}_EXTRA_FLAGS "-forward-unknown-to-host-compiler") + else() + set(_CMAKE_${lang}_EXTRA_FLAGS "") + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0") + set(_CMAKE_${lang}_EXTRA_DEVICE_LINK_FLAGS "-Wno-deprecated-gpu-targets") + else() + set(_CMAKE_${lang}_EXTRA_DEVICE_LINK_FLAGS "") + endif() + + if(CMAKE_${lang}_HOST_COMPILER AND NOT CMAKE_GENERATOR MATCHES "Visual Studio") + string(APPEND _CMAKE_${lang}_EXTRA_FLAGS " -ccbin=<CMAKE_${lang}_HOST_COMPILER>") + endif() + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) + # Starting in 10.2, nvcc supported treating all warnings as errors + set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "-Werror" "all-warnings") + endif() + + set(CMAKE_${lang}_DEPFILE_FORMAT gcc) + if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER) + AND CMAKE_GENERATOR MATCHES "Makefiles|WMake") + set(CMAKE_${lang}_DEPENDS_USE_COMPILER TRUE) + endif() + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89) + # The -MD flag was only added to nvcc in 10.2 so + # before that we had to invoke the compiler twice + # to get header dependency information + set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") + else() + set(CMAKE_${lang}_DEPENDS_EXTRA_COMMANDS "<CMAKE_${lang}_COMPILER> ${_CMAKE_${lang}_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> ${_CMAKE_COMPILE_AS_${lang}_FLAG} -M <SOURCE> -MT <OBJECT> -o <DEP_FILE>") + endif() + + if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2) + set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES) + set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES) + endif() + + if(NOT "x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC") + set(CMAKE_${lang}_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE) + set(CMAKE_${lang}_COMPILE_OPTIONS_PIC -Xcompiler=-fPIC) + set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY -Xcompiler=-fvisibility=) + # CMAKE_SHARED_LIBRARY_${lang}_FLAGS is sent to the host linker so we + # don't need to forward it through nvcc. + set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS -fPIC) + string(APPEND CMAKE_${lang}_FLAGS_INIT " ") + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") + string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + endif() + + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS -shared) + set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ") + + if (CMAKE_${lang}_SIMULATE_ID STREQUAL "GNU") + set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,") + set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",") + elseif(CMAKE_${lang}_SIMULATE_ID STREQUAL "Clang") + set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ") + set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP) + endif() + + set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "cudadevrt;cudart_static") + set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_SHARED "cudadevrt;cudart") + set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_NONE "") + + if(UNIX AND NOT (CMAKE_SYSTEM_NAME STREQUAL "QNX")) + list(APPEND CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl") + endif() + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0") + set(CMAKE_${lang}_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ") + set(CMAKE_${lang}_RESPONSE_FILE_FLAG "--options-file ") + endif() + + if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0") + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1) + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1) + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1) + else() + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 0) + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 0) + set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 0) + endif() +endmacro() diff --git a/Modules/Compiler/OrangeC-ASM.cmake b/Modules/Compiler/OrangeC-ASM.cmake new file mode 100644 index 0000000..fe78911 --- /dev/null +++ b/Modules/Compiler/OrangeC-ASM.cmake @@ -0,0 +1,7 @@ +include(Compiler/OrangeC) +__compiler_orangec(ASM) + +set(CMAKE_ASM_OUTPUT_EXTENSION ".o") +set(CMAKE_ASM_VERBOSE_FLAG "-yyyyy") + +set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;S;asm;nas) diff --git a/Modules/Compiler/OrangeC-C.cmake b/Modules/Compiler/OrangeC-C.cmake new file mode 100644 index 0000000..15a6476 --- /dev/null +++ b/Modules/Compiler/OrangeC-C.cmake @@ -0,0 +1,20 @@ +include(Compiler/OrangeC) +include(Compiler/CMakeCommonCompilerMacros) + +set(CMAKE_C_OUTPUT_EXTENSION ".o") +set(CMAKE_C_VERBOSE_FLAG "-yyyyy") +set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c) + +set(CMAKE_C90_STANDARD_COMPILE_OPTION -std=c89) +set(CMAKE_C90_EXTENSION_COMPILE_OPTION -std=c89) +set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C99_STANDARD_COMPILE_OPTION -std=c99) +set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=c99) +set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON) +set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11) +set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=c11) +set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON) + +__compiler_orangec(C) +#- 6.38 is the earliest version which version info is available in the preprocessor +__compiler_check_default_language_standard(C 6.38 11) diff --git a/Modules/Compiler/OrangeC-CXX.cmake b/Modules/Compiler/OrangeC-CXX.cmake new file mode 100644 index 0000000..3f9d59c --- /dev/null +++ b/Modules/Compiler/OrangeC-CXX.cmake @@ -0,0 +1,25 @@ +include(Compiler/OrangeC) +include(Compiler/CMakeCommonCompilerMacros) + +set(_ORANGEC_COMPILE_CXX " -x c++") +set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++) + +set(CMAKE_CXX_OUTPUT_EXTENSION ".o") +set(CMAKE_CXX_VERBOSE_FLAG "-yyyyy") + + + +#- OrangeC is a little lax when accepting compiler version specifications. +# Usually changing the version only changes the value of __cplusplus. +# Also we don't support CXX98 +set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11") +set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=c++11") +set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) + +set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") +set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++14") +set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) + +__compiler_orangec(CXX) +#- 6.38 is the earliest version which version info is available in the preprocessor +__compiler_check_default_language_standard(CXX 6.38 14) diff --git a/Modules/Compiler/OrangeC-DetermineCompiler.cmake b/Modules/Compiler/OrangeC-DetermineCompiler.cmake new file mode 100644 index 0000000..2ecc140 --- /dev/null +++ b/Modules/Compiler/OrangeC-DetermineCompiler.cmake @@ -0,0 +1,7 @@ + +set(_compiler_id_pp_test "defined(__ORANGEC__)") + +set(_compiler_id_version_compute " +# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__ORANGEC_MAJOR__) +# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__ORANGEC_MINOR__) +# define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__ORANGEC_PATCHLEVEL__)") diff --git a/Modules/Compiler/OrangeC.cmake b/Modules/Compiler/OrangeC.cmake new file mode 100644 index 0000000..fbb245b --- /dev/null +++ b/Modules/Compiler/OrangeC.cmake @@ -0,0 +1,33 @@ +include_guard() + +macro(__compiler_orangec lang) + if ("x${lang}" MATCHES "^x(C|CXX)$") + set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! <SOURCE> <DEFINES> <INCLUDES> <FLAGS> +i -o <PREPROCESSED_SOURCE>") + set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -S -o <ASSEMBLY_SOURCE>") + endif() + set(CMAKE_${lang}_COMPILE_OBJECT "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! -c <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + unset(_ORANGEC_COMPILE_${lang}) + + set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>") + set(CMAKE_${lang}_DEPFILE_FORMAT gcc) + set(CMAKE_${lang}_DEPENDS_USE_COMPILER TRUE) + + string(APPEND CMAKE_${lang}_FLAGS_INIT " ") + string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g") + string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O2 -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG") + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY + "<CMAKE_${lang}_COMPILER> -! -static -o <TARGET> <LINK_FLAGS> <OBJECTS> ") + set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_${lang}_COMPILER> -! <FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>") + set(CMAKE_${lang}_CREATE_SHARED_LIBRARY + "<CMAKE_${lang}_COMPILER> -! <FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <OBJECTS> <LINK_LIBRARIES>") + set(CMAKE_${lang}_CREATE_SHARED_MODULE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}") + + set(CMAKE_LIBRARY_PATH_FLAG "-L") + set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-! -shared") + + set(CMAKE_${lang}_RESPONSE_FILE_FLAG "@") + set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "@") +endmacro() diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake index 4ff43ed..f10684c 100644 --- a/Modules/FetchContent.cmake +++ b/Modules/FetchContent.cmake @@ -111,6 +111,7 @@ Commands FetchContent_Declare( <name> <contentOptions>... + [EXCLUDE_FROM_ALL] [SYSTEM] [OVERRIDE_FIND_PACKAGE | FIND_PACKAGE_ARGS args...] @@ -240,6 +241,15 @@ Commands See the :prop_tgt:`SYSTEM` target property documentation for a more detailed discussion of the effects. + .. versionadded:: 3.28 + + ``EXCLUDE_FROM_ALL`` + If the ``EXCLUDE_FROM_ALL`` argument is provided, then targets in the + subdirectory added by :command:`FetchContent_MakeAvailable` will not be + included in the ``ALL`` target by default, and may be excluded from IDE + project files. See the `:command:`add_subdirectory` `EXCLUDE_FROM_ALL`` + argument documentation for a more detailed discussion of the effects. + .. command:: FetchContent_MakeAvailable .. versionadded:: 3.14 @@ -358,6 +368,11 @@ Commands :command:`FetchContent_Declare`, the ``SYSTEM`` keyword will be added to the :command:`add_subdirectory` command as well. + .. versionadded:: 3.28 + If the ``EXCLUDE_FROM_ALL`` keyword was included in the call to + :command:`FetchContent_Declare`, the ``EXCLUDE_FROM_ALL`` keyword will + be added to the :command:`add_subdirectory` command as well. + Projects should aim to declare the details of all dependencies they might use before they call ``FetchContent_MakeAvailable()`` for any of them. This ensures that if any of the dependencies are also sub-dependencies of @@ -1466,8 +1481,10 @@ function(__FetchContent_directPopulate contentName) set(options QUIET - # SYSTEM has no meaning for ExternalProject, it is only used by us in - # FetchContent_MakeAvailable(). We need to parse and discard it here. + # EXCLUDE_FROM_ALL and SYSTEM have no meaning for ExternalProject, they + # are only used by us in FetchContent_MakeAvailable(). We need to parse + # and discard them here. + EXCLUDE_FROM_ALL SYSTEM ) set(oneValueArgs @@ -2030,22 +2047,28 @@ macro(FetchContent_MakeAvailable) if("${__cmake_contentDetails}" STREQUAL "") message(FATAL_ERROR "No details have been set for content: ${__cmake_contentName}") endif() - cmake_parse_arguments(__cmake_arg "SYSTEM" "SOURCE_SUBDIR" "" ${__cmake_contentDetails}) + cmake_parse_arguments(__cmake_arg "EXCLUDE_FROM_ALL;SYSTEM" "SOURCE_SUBDIR" "" ${__cmake_contentDetails}) if(NOT "${__cmake_arg_SOURCE_SUBDIR}" STREQUAL "") string(APPEND __cmake_srcdir "/${__cmake_arg_SOURCE_SUBDIR}") endif() if(EXISTS ${__cmake_srcdir}/CMakeLists.txt) - if (__cmake_arg_SYSTEM) - add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR} SYSTEM) - else() - add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR}) + set(__cmake_add_subdirectory_args ${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR}) + if(__cmake_arg_EXCLUDE_FROM_ALL) + list(APPEND __cmake_add_subdirectory_args EXCLUDE_FROM_ALL) + endif() + if(__cmake_arg_SYSTEM) + list(APPEND __cmake_add_subdirectory_args SYSTEM) endif() + add_subdirectory(${__cmake_add_subdirectory_args}) endif() unset(__cmake_srcdir) unset(__cmake_contentDetails) + unset(__cmake_arg_EXCLUDE_FROM_ALL) + unset(__cmake_arg_SYSTEM) unset(__cmake_arg_SOURCE_SUBDIR) + unset(__cmake_add_subdirectory_args) endif() endforeach() diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index f2e4804..1f82bb5 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -1365,7 +1365,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - else() + elseif(Boost_VERSION_STRING VERSION_LESS 1.83.0) set(_Boost_CONTRACT_DEPENDENCIES thread chrono) set(_Boost_COROUTINE_DEPENDENCIES context) set(_Boost_FIBER_DEPENDENCIES context) @@ -1380,7 +1380,21 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.83.0 AND NOT Boost_NO_WARN_NEW_VERSIONS) + else() + set(_Boost_CONTRACT_DEPENDENCIES thread chrono) + set(_Boost_COROUTINE_DEPENDENCIES context) + set(_Boost_FIBER_DEPENDENCIES context) + set(_Boost_IOSTREAMS_DEPENDENCIES regex) + set(_Boost_JSON_DEPENDENCIES container) + set(_Boost_LOG_DEPENDENCIES log_setup filesystem thread regex chrono atomic) + set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l) + set(_Boost_MPI_DEPENDENCIES serialization) + set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization) + set(_Boost_NUMPY_DEPENDENCIES python${component_python_version}) + set(_Boost_THREAD_DEPENDENCIES chrono atomic) + set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic) + set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) + if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.84.0 AND NOT Boost_NO_WARN_NEW_VERSIONS) message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") endif() endif() @@ -1655,7 +1669,7 @@ else() # _Boost_COMPONENT_HEADERS. See the instructions at the top of # _Boost_COMPONENT_DEPENDENCIES. set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} - "1.82.0" "1.82" "1.81.0" "1.81" "1.80.0" "1.80" "1.79.0" "1.79" + "1.83.0" "1.83" "1.82.0" "1.82" "1.81.0" "1.81" "1.80.0" "1.80" "1.79.0" "1.79" "1.78.0" "1.78" "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74" "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake index 76f4759..9903e37 100644 --- a/Modules/FindDoxygen.cmake +++ b/Modules/FindDoxygen.cmake @@ -463,10 +463,6 @@ function(_Doxygen_version_validator version_match doxy_path) else() _Doxygen_get_version(candidate_version version_result "${doxy_path}") - if(version_result) - message(DEBUG "Unable to determine candidate doxygen version at ${doxy_path}: ${version_result}") - endif() - find_package_check_version("${candidate_version}" valid_doxy_version HANDLE_VERSION_RANGE ) @@ -494,15 +490,18 @@ macro(_Doxygen_find_doxygen) _Doxygen_get_version(DOXYGEN_VERSION _Doxygen_version_result "${DOXYGEN_EXECUTABLE}") if(_Doxygen_version_result) - message(WARNING "Unable to determine doxygen version: ${_Doxygen_version_result}") - endif() - - # Create an imported target for Doxygen - if(NOT TARGET Doxygen::doxygen) - add_executable(Doxygen::doxygen IMPORTED GLOBAL) - set_target_properties(Doxygen::doxygen PROPERTIES - IMPORTED_LOCATION "${DOXYGEN_EXECUTABLE}" - ) + if(NOT Doxygen_FIND_QUIETLY) + message(WARNING "Doxygen executable failed unexpected while determining version (exit status: ${_Doxygen_version_result}). Disabling Doxygen.") + endif() + set(DOXYGEN_EXECUTABLE "${DOXYGEN_EXECUTABLE}-FAILED_EXECUTION-NOTFOUND") + else() + # Create an imported target for Doxygen + if(NOT TARGET Doxygen::doxygen) + add_executable(Doxygen::doxygen IMPORTED GLOBAL) + set_target_properties(Doxygen::doxygen PROPERTIES + IMPORTED_LOCATION "${DOXYGEN_EXECUTABLE}" + ) + endif() endif() endif() endmacro() diff --git a/Modules/FindFreetype.cmake b/Modules/FindFreetype.cmake index 82885cb..dcf271d 100644 --- a/Modules/FindFreetype.cmake +++ b/Modules/FindFreetype.cmake @@ -65,6 +65,64 @@ directory of a Freetype installation. # I'm going to attempt to cut out the middleman and hope # everything still works. +set(_Freetype_args) +if (Freetype_FIND_QUIETLY) + list(APPEND _Freetype_args + QUIET) +endif () +if (Freetype_FIND_VERSION) + list(APPEND _Freetype_args + "${Freetype_FIND_VERSION}") + if (Freetype_FIND_VERSION_EXACT) + list(APPEND _Freetype_args + EXACT) + endif () +endif () +set(_Freetype_component_req) +set(_Freetype_component_opt) +foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS) + if (Freetype_FIND_REQUIRE_${_Freetype_component}) + list(APPEND _Freetype_component_req + "${_Freetype_component}") + else () + list(APPEND _Freetype_component_opt + "${_Freetype_component}") + endif () +endforeach () +unset(_Freetype_component) +if (_Freetype_component_req) + list(APPEND _Freetype_args + COMPONENTS "${_Freetype_component_req}") +endif () +unset(_Freetype_component_req) +if (_Freetype_component_opt) + list(APPEND _Freetype_args + OPTIONAL_COMPONENTS "${_Freetype_component_opt}") +endif () +unset(_Freetype_component_opt) +find_package(freetype CONFIG ${_Freetype_args}) +unset(_Freetype_args) +if (freetype_FOUND) + if (NOT TARGET Freetype::Freetype) + add_library(Freetype::Freetype IMPORTED INTERFACE) + set_target_properties(Freetype::Freetype PROPERTIES + INTERFACE_LINK_LIBRARIES freetype) + endif () + get_property(FREETYPE_INCLUDE_DIRS TARGET freetype PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(FREETYPE_LIBRARIES TARGET freetype PROPERTY INTERFACE_LINK_LIBRARIES) + get_property(_Freetype_location TARGET freetype PROPERTY LOCATION) + list(APPEND FREETYPE_LIBRARIES + "${_Freetype_location}") + unset(_Freetype_location) + set(Freetype_FOUND 1) + set(FREETYPE_VERSION_STRING "${freetype_VERSION}") + foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS) + set(Freetype_${_Freetype_component}_FOUND "${freetype_${_Freetype_component}_FOUND}") + endforeach () + unset(_Freetype_component) + return () +endif () + set(FREETYPE_FIND_ARGS HINTS ENV FREETYPE_DIR diff --git a/Modules/FindGLEW.cmake b/Modules/FindGLEW.cmake index bfde40b..dff53e1 100644 --- a/Modules/FindGLEW.cmake +++ b/Modules/FindGLEW.cmake @@ -126,6 +126,10 @@ function(__glew_set_find_library_suffix shared_or_static) set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib;.so" PARENT_SCOPE) elseif(APPLE AND "${shared_or_static}" MATCHES "STATIC") set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE) + elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "SHARED") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" PARENT_SCOPE) + elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "STATIC") + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE) elseif(WIN32 AND "${shared_or_static}" MATCHES "SHARED") set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" PARENT_SCOPE) elseif(WIN32 AND "${shared_or_static}" MATCHES "STATIC") diff --git a/Modules/FindGLUT.cmake b/Modules/FindGLUT.cmake index 09403bc..613d315 100644 --- a/Modules/FindGLUT.cmake +++ b/Modules/FindGLUT.cmake @@ -107,18 +107,8 @@ elseif(APPLE) if(GLUT_cocoa_LIBRARY AND NOT TARGET GLUT::Cocoa) add_library(GLUT::Cocoa UNKNOWN IMPORTED) - # Cocoa should always be a Framework, but we check to make sure. - if(GLUT_cocoa_LIBRARY MATCHES "/([^/]+)\\.framework$") - set(_glut_cocoa "${GLUT_cocoa_LIBRARY}/${CMAKE_MATCH_1}") - if(EXISTS "${_glut_cocoa}.tbd") - string(APPEND _glut_cocoa ".tbd") - endif() - set_target_properties(GLUT::Cocoa PROPERTIES - IMPORTED_LOCATION "${_glut_cocoa}") - else() - set_target_properties(GLUT::Cocoa PROPERTIES - IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}") - endif() + set_target_properties(GLUT::Cocoa PROPERTIES + IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}") endif() else() if(BEOS) @@ -196,32 +186,23 @@ if (GLUT_FOUND) add_library(GLUT::GLUT UNKNOWN IMPORTED) set_target_properties(GLUT::GLUT PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${GLUT_INCLUDE_DIRS}") - if(GLUT_glut_LIBRARY MATCHES "/([^/]+)\\.framework$") - set(_glut_glut "${GLUT_glut_LIBRARY}/${CMAKE_MATCH_1}") - if(EXISTS "${_glut_glut}.tbd") - string(APPEND _glut_glut ".tbd") - endif() + if(GLUT_glut_LIBRARY_RELEASE) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(GLUT::GLUT PROPERTIES + IMPORTED_LOCATION_RELEASE "${GLUT_glut_LIBRARY_RELEASE}") + endif() + + if(GLUT_glut_LIBRARY_DEBUG) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(GLUT::GLUT PROPERTIES - IMPORTED_LOCATION "${_glut_glut}") - else() - if(GLUT_glut_LIBRARY_RELEASE) - set_property(TARGET GLUT::GLUT APPEND PROPERTY - IMPORTED_CONFIGURATIONS RELEASE) - set_target_properties(GLUT::GLUT PROPERTIES - IMPORTED_LOCATION_RELEASE "${GLUT_glut_LIBRARY_RELEASE}") - endif() - - if(GLUT_glut_LIBRARY_DEBUG) - set_property(TARGET GLUT::GLUT APPEND PROPERTY - IMPORTED_CONFIGURATIONS DEBUG) - set_target_properties(GLUT::GLUT PROPERTIES - IMPORTED_LOCATION_DEBUG "${GLUT_glut_LIBRARY_DEBUG}") - endif() - - if(NOT GLUT_glut_LIBRARY_RELEASE AND NOT GLUT_glut_LIBRARY_DEBUG) - set_property(TARGET GLUT::GLUT APPEND PROPERTY - IMPORTED_LOCATION "${GLUT_glut_LIBRARY}") - endif() + IMPORTED_LOCATION_DEBUG "${GLUT_glut_LIBRARY_DEBUG}") + endif() + + if(NOT GLUT_glut_LIBRARY_RELEASE AND NOT GLUT_glut_LIBRARY_DEBUG) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_LOCATION "${GLUT_glut_LIBRARY}") endif() if(TARGET GLUT::Xmu) diff --git a/Modules/FindJNI.cmake b/Modules/FindJNI.cmake index e7050a3..abc76cf 100644 --- a/Modules/FindJNI.cmake +++ b/Modules/FindJNI.cmake @@ -136,76 +136,76 @@ endif() # Expand {libarch} occurrences to java_libarch subdirectory(-ies) and set ${_var} macro(java_append_library_directories _var) - # Determine java arch-specific library subdir - # Mostly based on openjdk/jdk/make/common/shared/Platform.gmk as of openjdk - # 1.6.0_18 + icedtea patches. However, it would be much better to base the - # guess on the first part of the GNU config.guess platform triplet. - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - if(CMAKE_LIBRARY_ARCHITECTURE STREQUAL "x86_64-linux-gnux32") - set(_java_libarch "x32" "amd64" "i386") - else() - set(_java_libarch "amd64" "i386") - endif() - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") - set(_java_libarch "i386") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64") - set(_java_libarch "arm64" "aarch64") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^alpha") - set(_java_libarch "alpha") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") - # Subdir is "arm" for both big-endian (arm) and little-endian (armel). - set(_java_libarch "arm" "aarch32") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips") - # mips* machines are bi-endian mostly so processor does not tell - # endianness of the underlying system. - set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" - "mips" "mipsel" "mipseb" "mipsr6" "mipsr6el" - "mips64" "mips64el" "mips64r6" "mips64r6el" - "mipsn32" "mipsn32el" "mipsn32r6" "mipsn32r6el") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") - set(_java_libarch "ppc64" "ppc64le") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64") - set(_java_libarch "ppc64" "ppc") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)") - set(_java_libarch "ppc" "ppc64") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc") - # Both flavors can run on the same processor - set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "sparc" "sparcv9") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)") - set(_java_libarch "parisc" "parisc64") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390") - # s390 binaries can run on s390x machines - set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "s390" "s390x") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sh") - set(_java_libarch "sh") + # Determine java arch-specific library subdir + # Mostly based on openjdk/jdk/make/common/shared/Platform.gmk as of openjdk + # 1.6.0_18 + icedtea patches. However, it would be much better to base the + # guess on the first part of the GNU config.guess platform triplet. + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + if(CMAKE_LIBRARY_ARCHITECTURE STREQUAL "x86_64-linux-gnux32") + set(_java_libarch "x32" "amd64" "i386") else() - set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}") + set(_java_libarch "amd64" "i386") endif() + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") + set(_java_libarch "i386") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64") + set(_java_libarch "arm64" "aarch64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^alpha") + set(_java_libarch "alpha") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") + # Subdir is "arm" for both big-endian (arm) and little-endian (armel). + set(_java_libarch "arm" "aarch32") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips") + # mips* machines are bi-endian mostly so processor does not tell + # endianness of the underlying system. + set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" + "mips" "mipsel" "mipseb" "mipsr6" "mipsr6el" + "mips64" "mips64el" "mips64r6" "mips64r6el" + "mipsn32" "mipsn32el" "mipsn32r6" "mipsn32r6el") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le") + set(_java_libarch "ppc64" "ppc64le") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64") + set(_java_libarch "ppc64" "ppc") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)") + set(_java_libarch "ppc" "ppc64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc") + # Both flavors can run on the same processor + set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "sparc" "sparcv9") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)") + set(_java_libarch "parisc" "parisc64") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390") + # s390 binaries can run on s390x machines + set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "s390" "s390x") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sh") + set(_java_libarch "sh") + else() + set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}") + endif() - # Append default list architectures if CMAKE_SYSTEM_PROCESSOR was empty or - # system is non-Linux (where the code above has not been well tested) - if(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux")) - list(APPEND _java_libarch "i386" "amd64" "ppc") - endif() + # Append default list architectures if CMAKE_SYSTEM_PROCESSOR was empty or + # system is non-Linux (where the code above has not been well tested) + if(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux")) + list(APPEND _java_libarch "i386" "amd64" "ppc") + endif() - # Sometimes ${CMAKE_SYSTEM_PROCESSOR} is added to the list to prefer - # current value to a hardcoded list. Remove possible duplicates. - list(REMOVE_DUPLICATES _java_libarch) - - foreach(_path ${ARGN}) - if(_path MATCHES "{libarch}") - foreach(_libarch ${_java_libarch}) - string(REPLACE "{libarch}" "${_libarch}" _newpath "${_path}") - if(EXISTS ${_newpath}) - list(APPEND ${_var} "${_newpath}") - endif() - endforeach() - else() - if(EXISTS ${_path}) - list(APPEND ${_var} "${_path}") - endif() + # Sometimes ${CMAKE_SYSTEM_PROCESSOR} is added to the list to prefer + # current value to a hardcoded list. Remove possible duplicates. + list(REMOVE_DUPLICATES _java_libarch) + + foreach(_path ${ARGN}) + if(_path MATCHES "{libarch}") + foreach(_libarch IN LISTS _java_libarch) + string(REPLACE "{libarch}" "${_libarch}" _newpath "${_path}") + if(EXISTS ${_newpath}) + list(APPEND ${_var} "${_newpath}") endif() - endforeach() + endforeach() + else() + if(EXISTS ${_path}) + list(APPEND ${_var} "${_path}") + endif() + endif() + endforeach() endmacro() include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindJavaCommon.cmake) @@ -235,13 +235,12 @@ endif() if (WIN32) set (_JNI_HINTS) macro (_JNI_GET_INSTALLED_VERSIONS _KIND) - execute_process(COMMAND REG QUERY "HKLM\\SOFTWARE\\JavaSoft\\${_KIND}" - RESULT_VARIABLE _JAVA_RESULT - OUTPUT_VARIABLE _JAVA_VERSIONS - ERROR_QUIET) - if (NOT _JAVA_RESULT) - string (REGEX MATCHALL "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\[0-9._]+" _JNI_VERSIONS "${_JAVA_VERSIONS}") - string (REGEX REPLACE "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\([0-9._]+)" "\\1" _JNI_VERSIONS "${_JNI_VERSIONS}") + cmake_host_system_information(RESULT _JNI_VERSIONS + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/JavaSoft/${_KIND}" + SUBKEYS) + if (_JNI_VERSIONS) + string (REGEX MATCHALL "[0-9._]+" _JNI_VERSIONS "${_JNI_VERSIONS}") + string (REGEX REPLACE "([0-9._]+)" "\\1" _JNI_VERSIONS "${_JNI_VERSIONS}") if (_JNI_VERSIONS) # sort versions. Most recent first list (SORT _JNI_VERSIONS COMPARE NATURAL ORDER DESCENDING) @@ -340,7 +339,7 @@ JAVA_APPEND_LIBRARY_DIRECTORIES(JAVA_AWT_LIBRARY_DIRECTORIES ) set(JAVA_JVM_LIBRARY_DIRECTORIES) -foreach(dir ${JAVA_AWT_LIBRARY_DIRECTORIES}) +foreach(dir IN LISTS JAVA_AWT_LIBRARY_DIRECTORIES) list(APPEND JAVA_JVM_LIBRARY_DIRECTORIES "${dir}" "${dir}/client" @@ -365,14 +364,14 @@ JAVA_APPEND_LIBRARY_DIRECTORIES(JAVA_AWT_INCLUDE_DIRECTORIES ${_JNI_JAVA_INCLUDE_TRIES} ) -foreach(JAVA_PROG "${JAVA_RUNTIME}" "${JAVA_COMPILE}" "${JAVA_ARCHIVE}") +foreach(JAVA_PROG IN ITEMS "${JAVA_RUNTIME}" "${JAVA_COMPILE}" "${JAVA_ARCHIVE}") get_filename_component(jpath "${JAVA_PROG}" PATH) - foreach(JAVA_INC_PATH ../include ../java/include ../share/java/include) + foreach(JAVA_INC_PATH IN ITEMS ../include ../java/include ../share/java/include) if(EXISTS ${jpath}/${JAVA_INC_PATH}) list(APPEND JAVA_AWT_INCLUDE_DIRECTORIES "${jpath}/${JAVA_INC_PATH}") endif() endforeach() - foreach(JAVA_LIB_PATH + foreach(JAVA_LIB_PATH IN ITEMS ../lib ../jre/lib ../jre/lib/i386 ../java/lib ../java/jre/lib ../java/jre/lib/i386 ../share/java/lib ../share/java/jre/lib ../share/java/jre/lib/i386) @@ -429,7 +428,7 @@ set(_JNI_NORMAL_JAWT PATHS ${JAVA_AWT_LIBRARY_DIRECTORIES} ) -foreach(search ${_JNI_SEARCHES}) +foreach(search IN LISTS _JNI_SEARCHES) if(JVM IN_LIST JNI_FIND_COMPONENTS) find_library(JAVA_JVM_LIBRARY ${_JNI_${search}_JVM} DOC "Java Virtual Machine library" diff --git a/Modules/FindJava.cmake b/Modules/FindJava.cmake index e8fb120..74e424b 100644 --- a/Modules/FindJava.cmake +++ b/Modules/FindJava.cmake @@ -90,13 +90,12 @@ if(_JAVA_HOME) endif() if (WIN32) macro (_JAVA_GET_INSTALLED_VERSIONS _KIND) - execute_process(COMMAND REG QUERY "HKLM\\SOFTWARE\\JavaSoft\\${_KIND}" - RESULT_VARIABLE _JAVA_RESULT - OUTPUT_VARIABLE _JAVA_VERSIONS - ERROR_QUIET) - if (NOT _JAVA_RESULT) - string (REGEX MATCHALL "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\[0-9._]+" _JAVA_VERSIONS "${_JAVA_VERSIONS}") - string (REGEX REPLACE "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\([0-9._]+)" "\\1" _JAVA_VERSIONS "${_JAVA_VERSIONS}") + cmake_host_system_information(RESULT _JAVA_VERSIONS + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/JavaSoft/${_KIND}" + SUBKEYS) + if (_JAVA_VERSIONS) + string (REGEX MATCHALL "[0-9._]+" _JAVA_VERSIONS "${_JAVA_VERSIONS}") + string (REGEX REPLACE "([0-9._]+)" "\\1" _JAVA_VERSIONS "${_JAVA_VERSIONS}") if (_JAVA_VERSIONS) # sort versions. Most recent first list (SORT _JAVA_VERSIONS COMPARE NATURAL ORDER DESCENDING) @@ -221,7 +220,7 @@ if(Java_JAVA_EXECUTABLE) unset(_java_var) set(Java_VERSION "${Java_VERSION_MAJOR}") if(NOT "x${Java_VERSION}" STREQUAL "x") - foreach(_java_c MINOR PATCH TWEAK) + foreach(_java_c IN ITEMS "MINOR" "PATCH" "TWEAK") if(NOT "x${Java_VERSION_${_java_c}}" STREQUAL "x") string(APPEND Java_VERSION ".${Java_VERSION_${_java_c}}") else() @@ -274,7 +273,7 @@ find_program(Java_JARSIGNER_EXECUTABLE include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) if(Java_FIND_COMPONENTS) set(_JAVA_REQUIRED_VARS) - foreach(component ${Java_FIND_COMPONENTS}) + foreach(component IN LISTS Java_FIND_COMPONENTS) # User just want to execute some Java byte-compiled If(component STREQUAL "Runtime") list(APPEND _JAVA_REQUIRED_VARS Java_JAVA_EXECUTABLE) @@ -316,7 +315,7 @@ if(Java_FIND_COMPONENTS) VERSION_VAR Java_VERSION ) if(Java_FOUND) - foreach(component ${Java_FIND_COMPONENTS}) + foreach(component IN LISTS Java_FIND_COMPONENTS) set(Java_${component}_FOUND TRUE) endforeach() endif() diff --git a/Modules/FindLibXslt.cmake b/Modules/FindLibXslt.cmake index 97943d6..a9920ee 100644 --- a/Modules/FindLibXslt.cmake +++ b/Modules/FindLibXslt.cmake @@ -110,7 +110,8 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXslt VERSION_VAR LIBXSLT_VERSION_STRING) mark_as_advanced(LIBXSLT_INCLUDE_DIR - LIBXSLT_LIBRARIES + LIBXSLT_LIBRARY + LIBXSLT_EXSLT_INCLUDE_DIR LIBXSLT_EXSLT_LIBRARY LIBXSLT_XSLTPROC_EXECUTABLE) diff --git a/Modules/FindLua50.cmake b/Modules/FindLua50.cmake index 0575caa..6ba9008 100644 --- a/Modules/FindLua50.cmake +++ b/Modules/FindLua50.cmake @@ -5,28 +5,19 @@ FindLua50 --------- - - Locate Lua library. -This module defines:: -:: +This module defines:: LUA50_FOUND, if false, do not try to link to Lua LUA_LIBRARIES, both lua and lualib LUA_INCLUDE_DIR, where to find lua.h and lualib.h (and probably lauxlib.h) - - -Note that the expected include convention is - -:: +Note that the expected include convention is:: #include "lua.h" -and not - -:: +and not:: #include <lua/lua.h> diff --git a/Modules/FindLua51.cmake b/Modules/FindLua51.cmake index 283a3eb..405a7a7 100644 --- a/Modules/FindLua51.cmake +++ b/Modules/FindLua51.cmake @@ -5,29 +5,20 @@ FindLua51 --------- - - Locate Lua library. This module defines:: -:: - LUA51_FOUND, if false, do not try to link to Lua LUA_LIBRARIES LUA_INCLUDE_DIR, where to find lua.h LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) - -Note that the expected include convention is - -:: +Note that the expected include convention is:: #include "lua.h" -and not - -:: +and not:: #include <lua/lua.h> diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 3ab6bc1..639cc5f 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -46,7 +46,7 @@ The module supports the following components: The version given to the :command:`find_package` directive is the Matlab **version**, which should not be confused with the Matlab *release* name - (eg. `R2014`). + (e.g. `R2023b`). The :command:`matlab_get_version_from_release_name` and :command:`matlab_get_release_name_from_version` provide a mapping between the release name and the version. @@ -57,7 +57,7 @@ specific: * Windows: The installed versions of Matlab/MCR are retrieved from the Windows registry -* OS X: The installed versions of Matlab/MCR are given by the MATLAB +* 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``. * Unix: The desired Matlab should be accessible from the ``PATH``. This does @@ -140,8 +140,8 @@ Result variables ``Matlab_VERSION`` .. versionadded:: 3.27 - the numerical version (e.g. 9.13) of Matlab found. Not to be confused with - Matlab release name (e.g. R2022b) that can be obtained with + the numerical version (e.g. 23.2.0) of Matlab found. Not to be confused with + Matlab release name (e.g. R2023b) that can be obtained with :command:`matlab_get_release_name_from_version`. ``Matlab_ROOT_DIR`` the final root of the Matlab installation determined by the FindMatlab @@ -302,6 +302,7 @@ if(NOT MATLAB_ADDITIONAL_VERSIONS) endif() set(MATLAB_VERSIONS_MAPPING + "R2023b=23.2" "R2023a=9.14" "R2022b=9.13" "R2022a=9.12" @@ -336,9 +337,7 @@ set(MATLAB_VERSIONS_MAPPING # temporary folder for all Matlab runs set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab) -if(NOT EXISTS "${_matlab_temporary_folder}") - file(MAKE_DIRECTORY "${_matlab_temporary_folder}") -endif() +file(MAKE_DIRECTORY "${_matlab_temporary_folder}") #[=======================================================================[.rst: .. command:: matlab_get_version_from_release_name @@ -347,14 +346,14 @@ endif() matlab_get_version_from_release_name(release version) - * Input: ``release`` is the release name (R2022b) - * Output: ``version`` is the version of Matlab (9.13) + * Input: ``release`` is the release name (e.g. R2023b) + * Output: ``version`` is the version of Matlab (e.g. 23.2.0) Returns the version of Matlab from a release name #]=======================================================================] macro(matlab_get_version_from_release_name release_name version_name) - string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING}) + string(REGEX MATCHALL "${release_name}=([0-9]+\\.[0-9]+)" _matched ${MATLAB_VERSIONS_MAPPING}) set(${version_name} "") if(NOT _matched STREQUAL "") @@ -374,16 +373,24 @@ endmacro() matlab_get_release_name_from_version(version release_name) - * Input: ``version`` is the version of Matlab (9.13) - * Output: ``release_name`` is the release name (R2022b) + * Input: ``version`` is the version of Matlab (e.g. 23.2.0) + * Output: ``release_name`` is the release name (R2023b) Returns the release name from the version of Matlab #]=======================================================================] macro(matlab_get_release_name_from_version version release_name) + # only the major.minor version is used + string(REGEX MATCH "([0-9]+\\.[0-9]+)" _match ${version}) + if(CMAKE_MATCH_1) + set(short_version ${CMAKE_MATCH_1}) + else() + set(short_version ${version}) + endif() + set(${release_name} "") foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) - string(REGEX MATCHALL "(.+)=${version}" _matched ${_var}) + string(REGEX MATCHALL "(.+)=${short_version}" _matched ${_var}) if(NOT _matched STREQUAL "") set(${release_name} ${CMAKE_MATCH_1}) break() @@ -393,7 +400,7 @@ macro(matlab_get_release_name_from_version version release_name) unset(_var) unset(_matched) if(${release_name} STREQUAL "") - message(WARNING "[MATLAB] The version ${version} is not registered") + message(WARNING "[MATLAB] The version ${short_version} is not registered") endif() endmacro() @@ -404,7 +411,7 @@ endmacro() macro(matlab_get_supported_releases list_releases) set(${list_releases}) foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) - string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + string(REGEX MATCHALL "(.+)=([0-9]+\\.[0-9]+)" _matched ${_var}) if(NOT _matched STREQUAL "") list(APPEND ${list_releases} ${CMAKE_MATCH_1}) endif() @@ -421,7 +428,7 @@ endmacro() macro(matlab_get_supported_versions list_versions) set(${list_versions}) foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) - string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + string(REGEX MATCHALL "(.+)=([0-9]+\\.[0-9]+)" _matched ${_var}) if(NOT _matched STREQUAL "") list(APPEND ${list_versions} ${CMAKE_MATCH_2}) endif() @@ -459,50 +466,44 @@ endmacro() function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions) if(NOT CMAKE_HOST_WIN32) - message(FATAL_ERROR "[MATLAB] This macro can only be called by a windows host (call to reg.exe)") + message(FATAL_ERROR "[MATLAB] This macro can only be called by a Windows host") endif() if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64") - set(APPEND_REG "/reg:64") + set(_view "64") else() - set(APPEND_REG "/reg:32") + set(_view "32") endif() set(matlabs_from_registry) foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime") - # /reg:64 should be added on 64 bits capable OSs in order to enable the - # redirection of 64 bits applications - execute_process( - COMMAND reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\${_installation_type}" /f * /k ${APPEND_REG} - RESULT_VARIABLE resultMatlab - OUTPUT_VARIABLE varMatlab - ERROR_VARIABLE errMatlab - INPUT_FILE NUL - ) + cmake_host_system_information(RESULT _reg + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}" + SUBKEYS VIEW ${_view} + ) + if(_reg) - if(resultMatlab EQUAL 0) + string(REGEX MATCHALL "([0-9]+\\.[0-9]+)" _versions_regex ${_reg}) - string( - REGEX MATCHALL "${_installation_type}\\\\([0-9]+(\\.[0-9]+)?)" - matlab_versions_regex ${varMatlab}) - - foreach(match IN LISTS matlab_versions_regex) - string( - REGEX MATCH "${_installation_type}\\\\(([0-9]+)(\\.([0-9]+))?)" - current_match ${match}) - - set(_matlab_current_version ${CMAKE_MATCH_1}) - set(current_matlab_version_major ${CMAKE_MATCH_2}) - set(current_matlab_version_minor ${CMAKE_MATCH_4}) - if(NOT current_matlab_version_minor) - set(current_matlab_version_minor "0") + foreach(match IN LISTS _versions_regex) + string(REGEX MATCH "([0-9]+\\.[0-9]+)" current_match ${match}) + + if(NOT CMAKE_MATCH_1) + continue() endif() - list(APPEND matlabs_from_registry ${_matlab_current_version}) - unset(_matlab_current_version) + cmake_host_system_information(RESULT _reg + QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${CMAKE_MATCH_1}" + VALUE "MATLABROOT" + ) + + _Matlab_VersionInfoXML(${_reg} _matlab_version_tmp) + if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") + list(APPEND matlabs_from_registry ${_matlab_version_tmp}) + endif() endforeach() endif() @@ -510,8 +511,7 @@ function(matlab_extract_all_installed_versions_from_registry win64 matlab_versio if(matlabs_from_registry) list(REMOVE_DUPLICATES matlabs_from_registry) - list(SORT matlabs_from_registry COMPARE NATURAL) - list(REVERSE matlabs_from_registry) + list(SORT matlabs_from_registry COMPARE NATURAL ORDER DESCENDING) endif() set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE) @@ -529,8 +529,7 @@ macro(extract_matlab_versions_from_registry_brute_force matlab_versions) # we order from more recent to older if(matlab_supported_versions) list(REMOVE_DUPLICATES matlab_supported_versions) - list(SORT matlab_supported_versions COMPARE NATURAL) - list(REVERSE matlab_supported_versions) + list(SORT matlab_supported_versions COMPARE NATURAL ORDER DESCENDING) endif() set(${matlab_versions} ${matlab_supported_versions}) @@ -567,7 +566,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]" ABSOLUTE) - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT}) endif() @@ -583,7 +582,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ # remove the dot string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") endif() @@ -599,7 +598,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_ # remove the dot string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}") - if(EXISTS "${current_MATLAB_ROOT}") + if(IS_DIRECTORY "${current_MATLAB_ROOT}") list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}") endif() @@ -842,7 +841,7 @@ function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_ve string(SUBSTRING "${_matlab_version_from_cmd}" ${index} -1 substring_ans) string( - REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)?)" + REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)+)" matlab_versions_regex ${substring_ans}) foreach(match IN LISTS matlab_versions_regex) @@ -1281,7 +1280,7 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve if(NOT matlab_known_version STREQUAL "NOTFOUND") # the version is known, we just return it set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE) - set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)") return() endif() @@ -1292,7 +1291,7 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve if(EXISTS "${matlab_root}/appdata/version.xml") # we inspect the application version.xml file that contains the product information - file(STRINGS "${matlab_root}/appdata/version.xml" productinfo_string NEWLINE_CONSUME) + file(READ "${matlab_root}/appdata/version.xml" productinfo_string) string(REGEX MATCH "<installedProductData.*displayedString=\"([a-zA-Z ]+)\".*/>" product_reg_match ${productinfo_string} @@ -1325,7 +1324,7 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve if(NOT _matlab_current_program) set(_find_matlab_options) - if(matlab_root AND EXISTS ${matlab_root}) + if(IS_DIRECTORY "${matlab_root}") set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH) endif() @@ -1337,13 +1336,13 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve ) endif() - if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program}) + if(NOT _matlab_current_program) # if not found, clear the dependent variables if(MATLAB_FIND_DEBUG) message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}") endif() - set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) - set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version") + set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version") unset(_matlab_current_program) unset(_matlab_current_program CACHE) return() @@ -1363,10 +1362,16 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve # update the location of the program set(Matlab_PROG_VERSION_STRING_AUTO_DETECT ${_matlab_main_real_path_tmp} - CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + CACHE INTERNAL "internal matlab location for the discovered version") - set(matlab_list_of_all_versions) - matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions) + _Matlab_VersionInfoXML(${matlab_root} _matlab_version_tmp) + if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") + # at least back to R2016 VersionInfo.xml exists + set(matlab_list_of_all_versions ${_matlab_version_tmp}) + else() + # time consuming, less stable way to find Matlab version by running Matlab + matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions) + endif() list(LENGTH matlab_list_of_all_versions list_of_all_versions_length) if(list_of_all_versions_length GREATER 0) @@ -1376,40 +1381,54 @@ function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_ve endif() # set the version into the cache - set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)") # warning, just in case several versions found (should not happen) if((list_of_all_versions_length GREATER 1) AND MATLAB_FIND_DEBUG) message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})") endif() - # return the updated value - set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) - elseif(EXISTS "${matlab_root}/VersionInfo.xml") + else() # MCR # we cannot run anything in order to extract the version. We assume that the file # VersionInfo.xml exists under the MatlabRoot, we look for it and extract the version from there - set(_matlab_version_tmp "unknown") - file(STRINGS "${matlab_root}/VersionInfo.xml" versioninfo_string NEWLINE_CONSUME) - - if(versioninfo_string) - # parses "<version>9.2.0.538062</version>" - string(REGEX MATCH "<version>(.*)</version>" - version_reg_match - ${versioninfo_string} - ) - - if(CMAKE_MATCH_1 MATCHES "(([0-9]+)\\.([0-9]+))[\\.0-9]*") - set(_matlab_version_tmp "${CMAKE_MATCH_1}") - endif() + _Matlab_VersionInfoXML(${matlab_root} _matlab_version_tmp) + if(NOT "${_matlab_version_tmp}" STREQUAL "unknown") + set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)") endif() - set(${matlab_final_version} "${_matlab_version_tmp}" PARENT_SCOPE) - set(Matlab_VERSION_STRING_INTERNAL - "${_matlab_version_tmp}" - CACHE INTERNAL "Matlab (MCR) version (automatically determined)" - FORCE) endif() # Matlab or MCR + # return the updated value + set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) + +endfunction() + + +function(_Matlab_VersionInfoXML matlab_root _version) + + set(_ver "unknown") + + set(_XMLfile ${matlab_root}/VersionInfo.xml) + if(NOT EXISTS ${_XMLfile}) + return() + endif() + + file(READ ${_XMLfile} versioninfo_string) + + if(versioninfo_string) + # parses "<version>23.2.0.2365128</version>" + string(REGEX MATCH "<version>([0-9]+(\\.[0-9]+)+)</version>" + version_reg_match + ${versioninfo_string} + ) + + if(CMAKE_MATCH_1) + set(_ver "${CMAKE_MATCH_1}") + endif() + endif() + + set(${_version} ${_ver} PARENT_SCOPE) + endfunction() @@ -1444,8 +1463,8 @@ function(_Matlab_find_instances_win32 matlab_roots) endfunction() -# Utility function for finding Matlab or MCR on OSX -function(_Matlab_find_instances_osx matlab_roots) +# Utility function for finding Matlab or MCR on macOS +function(_Matlab_find_instances_macos matlab_roots) set(_matlab_possible_roots) # on mac, we look for the /Application paths @@ -1462,8 +1481,13 @@ function(_Matlab_find_instances_osx matlab_roots) 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() + # Check Matlab, has precedence over MCR - if(EXISTS ${_matlab_base_path}) + 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() @@ -1472,7 +1496,7 @@ function(_Matlab_find_instances_osx matlab_roots) # Checks MCR set(_mcr_path "/Applications/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}") - if(EXISTS "${_mcr_path}") + 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() @@ -1565,7 +1589,7 @@ endif() if(Matlab_ROOT_DIR) # if the user specifies a possible root, we keep this one - if(NOT EXISTS "${Matlab_ROOT_DIR}") + if(NOT IS_DIRECTORY "${Matlab_ROOT_DIR}") # if Matlab_ROOT_DIR specified but erroneous if(MATLAB_FIND_DEBUG) message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})") @@ -1587,8 +1611,8 @@ else() _Matlab_find_instances_win32(_matlab_possible_roots_win32) list(APPEND _matlab_possible_roots ${_matlab_possible_roots_win32}) elseif(APPLE) - _Matlab_find_instances_osx(_matlab_possible_roots_osx) - list(APPEND _matlab_possible_roots ${_matlab_possible_roots_osx}) + _Matlab_find_instances_macos(_matlab_possible_roots_macos) + list(APPEND _matlab_possible_roots ${_matlab_possible_roots_macos}) endif() endif() @@ -1604,10 +1628,6 @@ if(MATLAB_FIND_DEBUG) message(STATUS "[MATLAB] Matlab root folders are ${_matlab_possible_roots}") endif() - - - - # take the first possible Matlab root list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots) set(Matlab_VERSION_STRING "NOTFOUND") @@ -1799,7 +1819,7 @@ endif() # This small stub around find_library is to prevent any pollution of CMAKE_FIND_LIBRARY_PREFIXES in the global scope. # This is the function to be used below instead of the find_library directives. function(_Matlab_find_library _matlab_library_prefix) - set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} ${_matlab_library_prefix}) + list(APPEND CMAKE_FIND_LIBRARY_PREFIXES ${_matlab_library_prefix}) find_library(${ARGN}) endfunction() diff --git a/Modules/FindOpenAL.cmake b/Modules/FindOpenAL.cmake index 3d58569..c8e295b 100644 --- a/Modules/FindOpenAL.cmake +++ b/Modules/FindOpenAL.cmake @@ -106,15 +106,9 @@ find_package_handle_standard_args( mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR) if(OPENAL_FOUND AND NOT TARGET OpenAL::OpenAL) - if(OPENAL_LIBRARY MATCHES "/([^/]+)\\.framework$") - add_library(OpenAL::OpenAL INTERFACE IMPORTED) - set_target_properties(OpenAL::OpenAL PROPERTIES - INTERFACE_LINK_LIBRARIES "${OPENAL_LIBRARY}") - else() - add_library(OpenAL::OpenAL UNKNOWN IMPORTED) - set_target_properties(OpenAL::OpenAL PROPERTIES - IMPORTED_LOCATION "${OPENAL_LIBRARY}") - endif() + add_library(OpenAL::OpenAL UNKNOWN IMPORTED) + set_target_properties(OpenAL::OpenAL PROPERTIES + IMPORTED_LOCATION "${OPENAL_LIBRARY}") set_target_properties(OpenAL::OpenAL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}") endif() diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake index 843f787..1527c31 100644 --- a/Modules/FindOpenGL.cmake +++ b/Modules/FindOpenGL.cmake @@ -655,17 +655,8 @@ if(OPENGL_FOUND) # A legacy GL library is available, so use it for the legacy GL target. if(IS_ABSOLUTE "${OPENGL_gl_LIBRARY}") add_library(OpenGL::GL UNKNOWN IMPORTED) - if(OPENGL_gl_LIBRARY MATCHES "/([^/]+)\\.framework$") - set(_gl_fw "${OPENGL_gl_LIBRARY}/${CMAKE_MATCH_1}") - if(EXISTS "${_gl_fw}.tbd") - string(APPEND _gl_fw ".tbd") - endif() - set_target_properties(OpenGL::GL PROPERTIES - IMPORTED_LOCATION "${_gl_fw}") - else() - set_target_properties(OpenGL::GL PROPERTIES - IMPORTED_LOCATION "${OPENGL_gl_LIBRARY}") - endif() + set_target_properties(OpenGL::GL PROPERTIES + IMPORTED_LOCATION "${OPENGL_gl_LIBRARY}") else() add_library(OpenGL::GL INTERFACE IMPORTED) set_target_properties(OpenGL::GL PROPERTIES @@ -709,17 +700,8 @@ if(OPENGL_FOUND) if(OPENGL_GLU_FOUND AND NOT TARGET OpenGL::GLU) if(IS_ABSOLUTE "${OPENGL_glu_LIBRARY}") add_library(OpenGL::GLU UNKNOWN IMPORTED) - if(OPENGL_glu_LIBRARY MATCHES "/([^/]+)\\.framework$") - set(_glu_fw "${OPENGL_glu_LIBRARY}/${CMAKE_MATCH_1}") - if(EXISTS "${_glu_fw}.tbd") - string(APPEND _glu_fw ".tbd") - endif() - set_target_properties(OpenGL::GLU PROPERTIES - IMPORTED_LOCATION "${_glu_fw}") - else() - set_target_properties(OpenGL::GLU PROPERTIES - IMPORTED_LOCATION "${OPENGL_glu_LIBRARY}") - endif() + set_target_properties(OpenGL::GLU PROPERTIES + IMPORTED_LOCATION "${OPENGL_glu_LIBRARY}") else() add_library(OpenGL::GLU INTERFACE IMPORTED) set_target_properties(OpenGL::GLU PROPERTIES diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index cd912c3..151b215 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -298,8 +298,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR) set("${OPENMP_LIB_NAMES_VAR}" "" PARENT_SCOPE) endif() break() - elseif(CMAKE_${LANG}_COMPILER_ID STREQUAL "AppleClang" - AND CMAKE_${LANG}_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") + elseif((CMAKE_${LANG}_COMPILER_ID STREQUAL "AppleClang" + AND CMAKE_${LANG}_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") OR + (CMAKE_${LANG}_COMPILER_ID STREQUAL "Clang" AND APPLE)) # Check for separate OpenMP library on AppleClang 7+ find_library(OpenMP_libomp_LIBRARY diff --git a/Modules/FindPhysFS.cmake b/Modules/FindPhysFS.cmake index a32f83a..19e5ba9 100644 --- a/Modules/FindPhysFS.cmake +++ b/Modules/FindPhysFS.cmake @@ -5,16 +5,20 @@ FindPhysFS ---------- +Locate PhysFS library This module defines: +``PHYSFS_LIBRARY`` + the name of the library to link against +``PHYSFS_FOUND`` + if false, do not try to link to PHYSFS +``PHYSFS_INCLUDE_DIR`` + where to find physfs.h -Locate PhysFS library This module defines PHYSFS_LIBRARY, the name of -the library to link against PHYSFS_FOUND, if false, do not try to link -to PHYSFS PHYSFS_INCLUDE_DIR, where to find physfs.h +``$PHYSFSDIR`` is an environment variable that would correspond to:: -$PHYSFSDIR is an environment variable that would correspond to the -./configure --prefix=$PHYSFSDIR used in building PHYSFS. + ./configure --prefix=$PHYSFSDIR -Created by Eric Wing. +used in building PHYSFS. #]=======================================================================] find_path(PHYSFS_INCLUDE_DIR physfs.h diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake index 02f7fb4..5466e79 100644 --- a/Modules/FindPkgConfig.cmake +++ b/Modules/FindPkgConfig.cmake @@ -425,13 +425,19 @@ macro(_pkg_set_path_internal) unset(_pkgconfig_path) endif() - # Tell pkg-config not to strip any -L paths so we can search them all. + # Tell pkg-config not to strip any -I or -L paths so we can search them all. if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) set(_pkgconfig_allow_system_libs_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}") else() unset(_pkgconfig_allow_system_libs_old) endif() set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} 1) + if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) + set(_pkgconfig_allow_system_cflags_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}") + else() + unset(_pkgconfig_allow_system_cflags_old) + endif() + set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} 1) endmacro() macro(_pkg_restore_path_internal) @@ -445,6 +451,12 @@ macro(_pkg_restore_path_internal) else() unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) endif() + if(DEFINED _pkgconfig_allow_system_cflags_old) + set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} "${_pkgconfig_allow_system_cflags_old}") + unset(_pkgconfig_allow_system_cflags_old) + else() + unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) + endif() unset(_extra_paths) unset(_pkgconfig_path_old) @@ -551,8 +563,8 @@ macro(_pkg_check_modules_internal _is_required _is_silent _no_cmake_path _no_cma foreach (_pkg_check_modules_pkg ${_pkg_check_modules_list}) set(_pkg_check_modules_exist_query) - # check whether version is given - if (_pkg_check_modules_pkg MATCHES "(.*[^><])(=|[><]=?)(.*)") + # check whether version is given while ignoring whitespace + if (_pkg_check_modules_pkg MATCHES "(.*[^>< \t])[ \t]*(=|[><]=?)[ \t]*(.*)") set(_pkg_check_modules_pkg_name "${CMAKE_MATCH_1}") set(_pkg_check_modules_pkg_op "${CMAKE_MATCH_2}") set(_pkg_check_modules_pkg_ver "${CMAKE_MATCH_3}") @@ -911,11 +923,20 @@ endmacro() .. code-block:: cmake - pkg_get_variable(<resultVar> <moduleName> <varName>) + pkg_get_variable(<resultVar> <moduleName> <varName> + [DEFINE_VARIABLES <key>=<value>...]) If ``pkg-config`` returns multiple values for the specified variable, ``resultVar`` will contain a :ref:`;-list <CMake Language Lists>`. + Options: + + ``DEFINE_VARIABLES <key>=<value>...`` + .. versionadded:: 3.28 + + Specify key-value pairs to redefine variables affecting the variable + retrieved with ``pkg-config``. + For example: .. code-block:: cmake @@ -923,8 +944,20 @@ endmacro() pkg_get_variable(GI_GIRDIR gobject-introspection-1.0 girdir) #]========================================] function (pkg_get_variable result pkg variable) + set(_multiValueArgs DEFINE_VARIABLES) + + CMAKE_PARSE_ARGUMENTS(_parsedArguments "" "" "${_multiValueArgs}" ${ARGN}) + set(defined_variables ) + foreach(_def_var ${_parsedArguments_DEFINE_VARIABLES}) + if(NOT _def_var MATCHES "^.+=.*$") + message(FATAL_ERROR "DEFINE_VARIABLES should contain arguments in the form of key=value") + endif() + + list(APPEND defined_variables "--define-variable=${_def_var}") + endforeach() + _pkg_set_path_internal() - _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}") + _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}" ${defined_variables}) set("${result}" "${prefix_result}" PARENT_SCOPE) diff --git a/Modules/FindProducer.cmake b/Modules/FindProducer.cmake index 65495b5..8a16e8a 100644 --- a/Modules/FindProducer.cmake +++ b/Modules/FindProducer.cmake @@ -5,8 +5,6 @@ FindProducer ------------ - - Though Producer isn't directly part of OpenSceneGraph, its primary user is OSG so I consider this part of the Findosg* suite used to find OpenSceneGraph components. You'll notice that I accept OSGDIR as an @@ -17,19 +15,25 @@ must also opt into OpenGL (and OpenThreads?) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular module (perhaps because the default -FindOpenGL.cmake module doesn't work with your system as an example). +:module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, -use the FindOpenSceneGraph.cmake instead of the Findosg*.cmake +use the :module:`FindOpenSceneGraph` instead of the Findosg*.cmake modules. -Locate Producer This module defines PRODUCER_LIBRARY PRODUCER_FOUND, -if false, do not try to link to Producer PRODUCER_INCLUDE_DIR, where -to find the headers +Locate Producer This module defines: + +``PRODUCER_LIBRARY`` + +``PRODUCER_FOUND`` + if false, do not try to link to Producer +``PRODUCER_INCLUDE_DIR`` + where to find the headers + +``$PRODUCER_DIR`` is an environment variable that would correspond to:: -$PRODUCER_DIR is an environment variable that would correspond to the -./configure --prefix=$PRODUCER_DIR used in building osg. + ./configure --prefix=$PRODUCER_DIR -Created by Eric Wing. +used in building osg. #]=======================================================================] # Header files are presumed to be included like diff --git a/Modules/FindQuickTime.cmake b/Modules/FindQuickTime.cmake index 107486d..89be2a6 100644 --- a/Modules/FindQuickTime.cmake +++ b/Modules/FindQuickTime.cmake @@ -5,16 +5,18 @@ FindQuickTime ------------- +Locate QuickTime This module defines: +``QUICKTIME_LIBRARY`` -Locate QuickTime This module defines QUICKTIME_LIBRARY -QUICKTIME_FOUND, if false, do not try to link to gdal -QUICKTIME_INCLUDE_DIR, where to find the headers +``QUICKTIME_FOUND`` + if false, do not try to link to gdal +``QUICKTIME_INCLUDE_DIR`` + where to find the headers -$QUICKTIME_DIR is an environment variable that would correspond to the -./configure --prefix=$QUICKTIME_DIR +``$QUICKTIME_DIR`` is an environment variable that would correspond to:: -Created by Eric Wing. + ./configure --prefix=$QUICKTIME_DIR #]=======================================================================] find_path(QUICKTIME_INCLUDE_DIR QuickTime/QuickTime.h QuickTime.h diff --git a/Modules/FindTIFF.cmake b/Modules/FindTIFF.cmake index ed2657c..4a93c04 100644 --- a/Modules/FindTIFF.cmake +++ b/Modules/FindTIFF.cmake @@ -65,6 +65,79 @@ The following cache variables may also be set: Debug and Release variants are found separately. #]=======================================================================] +set(_TIFF_args) +if (TIFF_FIND_QUIETLY) + list(APPEND _TIFF_args + QUIET) +endif () +if (TIFF_FIND_VERSION) + list(APPEND _TIFF_args + "${TIFF_FIND_VERSION}") + if (TIFF_FIND_VERSION_EXACT) + list(APPEND _TIFF_args + EXACT) + endif () +endif () +set(_TIFF_component_req) +set(_TIFF_component_opt) +foreach (_TIFF_component IN LISTS TIFF_FIND_COMPONENTS) + if (TIFF_FIND_REQUIRE_${_TIFF_component}) + list(APPEND _TIFF_component_req + "${_TIFF_component}") + else () + list(APPEND _TIFF_component_opt + "${_TIFF_component}") + endif () +endforeach () +unset(_TIFF_component) +if (_TIFF_component_req) + list(APPEND _TIFF_args + COMPONENTS "${_TIFF_component_req}") +endif () +unset(_TIFF_component_req) +if (_TIFF_component_opt) + list(APPEND _TIFF_args + OPTIONAL_COMPONENTS "${_TIFF_component_opt}") +endif () +unset(_TIFF_component_opt) +find_package(tiff CONFIG ${_TIFF_args}) +unset(_TIFF_args) +if (tiff_FOUND) + if (NOT TARGET TIFF::TIFF) + add_library(TIFF::TIFF IMPORTED INTERFACE) + set_target_properties(TIFF::TIFF PROPERTIES + INTERFACE_LINK_LIBRARIES TIFF::tiff) + endif () + get_property(TIFF_INCLUDE_DIRS TARGET TIFF::tiff PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(TIFF_LIBRARIES TARGET TIFF::tiff PROPERTY INTERFACE_LINK_LIBRARIES) + get_property(_TIFF_location TARGET TIFF::tiff PROPERTY LOCATION) + list(APPEND TIFF_LIBRARIES + "${_TIFF_location}") + unset(_TIFF_location) + set(TIFF_FOUND 1) + if("CXX" IN_LIST TIFF_FIND_COMPONENTS) + if (TARGET TIFF::CXX) + get_property(_TIFF_CXX_location TARGET TIFF::CXX PROPERTY LOCATION) + list(APPEND TIFF_LIBRARIES ${_TIFF_CXX_location}) + unset(_TIFF_CXX_location) + set(TIFF_CXX_FOUND 1) + else () + set(TIFF_CXX_FOUND 0) + if (TIFF_FIND_REQUIRED_CXX) + set(TIFF_FOUND 0) + list(APPEND TIFF_NOT_FOUND_REASON + "No C++ bindings target found") + endif () + endif () + endif () + set(TIFF_VERSION_STRING "${tiff_VERSION}") + foreach (_TIFF_component IN LISTS TIFF_FIND_COMPONENTS) + set(TIFF_${_TIFF_component}_FOUND "${tiff_${_TIFF_component}_FOUND}") + endforeach () + unset(_TIFF_component) + return () +endif () + cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) diff --git a/Modules/Findosg.cmake b/Modules/Findosg.cmake index 027f315..9e952c9 100644 --- a/Modules/Findosg.cmake +++ b/Modules/Findosg.cmake @@ -7,32 +7,40 @@ Findosg -NOTE: It is highly recommended that you use the new -FindOpenSceneGraph.cmake introduced in CMake 2.6.3 and not use this -Find module directly. +.. note:: + It is highly recommended that you use the new + :module:`FindOpenSceneGraph` introduced in CMake 2.6.3 and not use this + Find module directly. -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osg This module defines +Locate osg This module defines: -OSG_FOUND - Was the Osg found? OSG_INCLUDE_DIR - Where to find the -headers OSG_LIBRARIES - The libraries to link against for the OSG (use -this) +``OSG_FOUND`` + Was the Osg found? +``OSG_INCLUDE_DIR`` + Where to find theheaders +``OSG_LIBRARIES`` + The libraries to link against for the OSG (use this) +``OSG_LIBRARY`` + The OSG library +``OSG_LIBRARY_DEBUG`` + The OSG debug library -OSG_LIBRARY - The OSG library OSG_LIBRARY_DEBUG - The OSG debug -library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgAnimation.cmake b/Modules/FindosgAnimation.cmake index 65e3016..e31987e 100644 --- a/Modules/FindosgAnimation.cmake +++ b/Modules/FindosgAnimation.cmake @@ -7,29 +7,35 @@ FindosgAnimation -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgAnimation This module defines +Locate osgAnimation This module defines: -OSGANIMATION_FOUND - Was osgAnimation found? OSGANIMATION_INCLUDE_DIR -- Where to find the headers OSGANIMATION_LIBRARIES - The libraries to -link against for the OSG (use this) +``OSGANIMATION_FOUND`` + Was osgAnimation found? +``OSGANIMATION_INCLUDE_DIR`` + Where to find the headers +``OSGANIMATION_LIBRARIES`` + The libraries to link against for the OSG (use this) +``OSGANIMATION_LIBRARY`` + The OSG library +``OSGANIMATION_LIBRARY_DEBUG`` + The OSG debug library -OSGANIMATION_LIBRARY - The OSG library OSGANIMATION_LIBRARY_DEBUG - -The OSG debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgDB.cmake b/Modules/FindosgDB.cmake index a28f650..9366d21 100644 --- a/Modules/FindosgDB.cmake +++ b/Modules/FindosgDB.cmake @@ -37,7 +37,9 @@ Locate osgDB This module defines: ``$OSGDIR`` is an environment variable that would correspond to:: - ./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. #]=======================================================================] diff --git a/Modules/FindosgFX.cmake b/Modules/FindosgFX.cmake index 438fab7..79362ef 100644 --- a/Modules/FindosgFX.cmake +++ b/Modules/FindosgFX.cmake @@ -7,28 +7,35 @@ FindosgFX -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgFX This module defines +Locate osgFX This module defines: -OSGFX_FOUND - Was osgFX found? OSGFX_INCLUDE_DIR - Where to find the -headers OSGFX_LIBRARIES - The libraries to link against for the osgFX -(use this) +``OSGFX_FOUND`` + Was osgFX found? +``OSGFX_INCLUDE_DIR`` + Where to find the headers +``OSGFX_LIBRARIES`` + The libraries to link against for the osgFX (use this) +``OSGFX_LIBRARY`` + The osgFX library +``OSGFX_LIBRARY_DEBUG`` + The osgFX debug library -OSGFX_LIBRARY - The osgFX library OSGFX_LIBRARY_DEBUG - The osgFX -debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgGA.cmake b/Modules/FindosgGA.cmake index 7b6ef30..7ebcce8 100644 --- a/Modules/FindosgGA.cmake +++ b/Modules/FindosgGA.cmake @@ -7,28 +7,35 @@ FindosgGA -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgGA This module defines +Locate osgGA This module defines: -OSGGA_FOUND - Was osgGA found? OSGGA_INCLUDE_DIR - Where to find the -headers OSGGA_LIBRARIES - The libraries to link against for the osgGA -(use this) +``OSGGA_FOUND`` + Was osgGA found? +``OSGGA_INCLUDE_DIR`` + Where to find the headers +``OSGGA_LIBRARIES`` + The libraries to link against for the osgGA (use this) +``OSGGA_LIBRARY`` + The osgGA library +``OSGGA_LIBRARY_DEBUG`` + The osgGA debug library -OSGGA_LIBRARY - The osgGA library OSGGA_LIBRARY_DEBUG - The osgGA -debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgIntrospection.cmake b/Modules/FindosgIntrospection.cmake index 625e4c2..c0c28f2 100644 --- a/Modules/FindosgIntrospection.cmake +++ b/Modules/FindosgIntrospection.cmake @@ -7,29 +7,35 @@ FindosgIntrospection -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgINTROSPECTION This module defines +Locate osgINTROSPECTION This module defines: -OSGINTROSPECTION_FOUND - Was osgIntrospection found? -OSGINTROSPECTION_INCLUDE_DIR - Where to find the headers -OSGINTROSPECTION_LIBRARIES - The libraries to link for -osgIntrospection (use this) +``OSGINTROSPECTION_FOUND`` + Was osgIntrospection found? +``OSGINTROSPECTION_INCLUDE_DIR`` + Where to find the headers +``OSGINTROSPECTION_LIBRARIES`` + The libraries to link for osgIntrospection (use this) +``OSGINTROSPECTION_LIBRARY`` + The osgIntrospection library +``OSGINTROSPECTION_LIBRARY_DEBUG`` + The osgIntrospection debug library -OSGINTROSPECTION_LIBRARY - The osgIntrospection library -OSGINTROSPECTION_LIBRARY_DEBUG - The osgIntrospection debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgManipulator.cmake b/Modules/FindosgManipulator.cmake index 857ff5d..47c74f6 100644 --- a/Modules/FindosgManipulator.cmake +++ b/Modules/FindosgManipulator.cmake @@ -7,29 +7,35 @@ FindosgManipulator -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgManipulator This module defines +Locate osgManipulator This module defines: -OSGMANIPULATOR_FOUND - Was osgManipulator found? -OSGMANIPULATOR_INCLUDE_DIR - Where to find the headers -OSGMANIPULATOR_LIBRARIES - The libraries to link for osgManipulator -(use this) +``OSGMANIPULATOR_FOUND`` + Was osgManipulator found? +``OSGMANIPULATOR_INCLUDE_DIR`` + Where to find the headers +``OSGMANIPULATOR_LIBRARIES`` + The libraries to link for osgManipulator (use this) +``OSGMANIPULATOR_LIBRARY`` + The osgManipulator library +``OSGMANIPULATOR_LIBRARY_DEBUG`` + The osgManipulator debug library -OSGMANIPULATOR_LIBRARY - The osgManipulator library -OSGMANIPULATOR_LIBRARY_DEBUG - The osgManipulator debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgParticle.cmake b/Modules/FindosgParticle.cmake index 91a30dc..cbe033d 100644 --- a/Modules/FindosgParticle.cmake +++ b/Modules/FindosgParticle.cmake @@ -7,28 +7,35 @@ FindosgParticle -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgParticle This module defines +Locate osgParticle This module defines: -OSGPARTICLE_FOUND - Was osgParticle found? OSGPARTICLE_INCLUDE_DIR - -Where to find the headers OSGPARTICLE_LIBRARIES - The libraries to -link for osgParticle (use this) +``OSGPARTICLE_FOUND`` + Was osgParticle found? +``OSGPARTICLE_INCLUDE_DIR`` + Where to find the headers +``OSGPARTICLE_LIBRARIES`` + The libraries to link for osgParticle (use this) +``OSGPARTICLE_LIBRARY`` + The osgParticle library +``OSGPARTICLE_LIBRARY_DEBUG`` + The osgParticle debug library -OSGPARTICLE_LIBRARY - The osgParticle library -OSGPARTICLE_LIBRARY_DEBUG - The osgParticle debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgPresentation.cmake b/Modules/FindosgPresentation.cmake index eae75d6..e8c8b4f 100644 --- a/Modules/FindosgPresentation.cmake +++ b/Modules/FindosgPresentation.cmake @@ -7,30 +7,35 @@ FindosgPresentation -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgPresentation This module defines +Locate osgPresentation This module defines: -OSGPRESENTATION_FOUND - Was osgPresentation found? -OSGPRESENTATION_INCLUDE_DIR - Where to find the headers -OSGPRESENTATION_LIBRARIES - The libraries to link for osgPresentation -(use this) +``OSGPRESENTATION_FOUND`` + Was osgPresentation found? +``OSGPRESENTATION_INCLUDE_DIR`` + Where to find the headers +``OSGPRESENTATION_LIBRARIES`` + The libraries to link for osgPresentation (use this) +``OSGPRESENTATION_LIBRARY`` + The osgPresentation library +``OSGPRESENTATION_LIBRARY_DEBUG`` + The osgPresentation debug library -OSGPRESENTATION_LIBRARY - The osgPresentation library -OSGPRESENTATION_LIBRARY_DEBUG - The osgPresentation debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR +used in building osg. Created by Eric Wing. Modified to work with osgPresentation by Robert Osfield, January 2012. #]=======================================================================] diff --git a/Modules/FindosgProducer.cmake b/Modules/FindosgProducer.cmake index 33b9f73..92f0d20 100644 --- a/Modules/FindosgProducer.cmake +++ b/Modules/FindosgProducer.cmake @@ -7,28 +7,35 @@ FindosgProducer -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgProducer This module defines +Locate osgProducer This module defines: -OSGPRODUCER_FOUND - Was osgProducer found? OSGPRODUCER_INCLUDE_DIR - -Where to find the headers OSGPRODUCER_LIBRARIES - The libraries to -link for osgProducer (use this) +``OSGPRODUCER_FOUND`` + Was osgProducer found? +``OSGPRODUCER_INCLUDE_DIR`` + Where to find the headers +``OSGPRODUCER_LIBRARIES`` + The libraries to link for osgProducer (use this) +``OSGPRODUCER_LIBRARY`` + The osgProducer library +``OSGPRODUCER_LIBRARY_DEBUG`` + The osgProducer debug library -OSGPRODUCER_LIBRARY - The osgProducer library -OSGPRODUCER_LIBRARY_DEBUG - The osgProducer debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgQt.cmake b/Modules/FindosgQt.cmake index cf35630..2ad7174 100644 --- a/Modules/FindosgQt.cmake +++ b/Modules/FindosgQt.cmake @@ -7,27 +7,35 @@ FindosgQt -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgQt This module defines +Locate osgQt This module defines: -OSGQT_FOUND - Was osgQt found? OSGQT_INCLUDE_DIR - Where to find the -headers OSGQT_LIBRARIES - The libraries to link for osgQt (use this) +``OSGQT_FOUND`` + Was osgQt found? +``OSGQT_INCLUDE_DIR`` + Where to find the headers +``OSGQT_LIBRARIES`` + The libraries to link for osgQt (use this) +``OSGQT_LIBRARY`` + The osgQt library +``OSGQT_LIBRARY_DEBUG`` + The osgQt debug library -OSGQT_LIBRARY - The osgQt library OSGQT_LIBRARY_DEBUG - The osgQt -debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. Modified to work with osgQt by Robert Osfield, January 2012. diff --git a/Modules/FindosgShadow.cmake b/Modules/FindosgShadow.cmake index 0049c4e..12eb9da 100644 --- a/Modules/FindosgShadow.cmake +++ b/Modules/FindosgShadow.cmake @@ -7,28 +7,35 @@ FindosgShadow -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgShadow This module defines +Locate osgShadow This module defines: -OSGSHADOW_FOUND - Was osgShadow found? OSGSHADOW_INCLUDE_DIR - Where -to find the headers OSGSHADOW_LIBRARIES - The libraries to link for -osgShadow (use this) +``OSGSHADOW_FOUND`` + Was osgShadow found? +``OSGSHADOW_INCLUDE_DIR`` + Where to find the headers +``OSGSHADOW_LIBRARIES`` + The libraries to link for osgShadow (use this) +``OSGSHADOW_LIBRARY`` + The osgShadow library +``OSGSHADOW_LIBRARY_DEBUG`` + The osgShadow debug library -OSGSHADOW_LIBRARY - The osgShadow library OSGSHADOW_LIBRARY_DEBUG - -The osgShadow debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgSim.cmake b/Modules/FindosgSim.cmake index 43ba542..37ef03c 100644 --- a/Modules/FindosgSim.cmake +++ b/Modules/FindosgSim.cmake @@ -7,28 +7,35 @@ FindosgSim -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgSim This module defines +Locate osgSim This module defines: -OSGSIM_FOUND - Was osgSim found? OSGSIM_INCLUDE_DIR - Where to find -the headers OSGSIM_LIBRARIES - The libraries to link for osgSim (use -this) +``OSGSIM_FOUND`` + Was osgSim found? +``OSGSIM_INCLUDE_DIR`` + Where to find the headers +``OSGSIM_LIBRARIES`` + The libraries to link for osgSim (use this) +``OSGSIM_LIBRARY`` + The osgSim library +``OSGSIM_LIBRARY_DEBUG`` + The osgSim debug library -OSGSIM_LIBRARY - The osgSim library OSGSIM_LIBRARY_DEBUG - The osgSim -debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgTerrain.cmake b/Modules/FindosgTerrain.cmake index c6f5b69..a2de4ea 100644 --- a/Modules/FindosgTerrain.cmake +++ b/Modules/FindosgTerrain.cmake @@ -7,28 +7,35 @@ FindosgTerrain -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgTerrain This module defines +Locate osgTerrain This module defines: -OSGTERRAIN_FOUND - Was osgTerrain found? OSGTERRAIN_INCLUDE_DIR - -Where to find the headers OSGTERRAIN_LIBRARIES - The libraries to link -for osgTerrain (use this) +``OSGTERRAIN_FOUND`` + Was osgTerrain found? +``OSGTERRAIN_INCLUDE_DIR`` + Where to find the headers +``OSGTERRAIN_LIBRARIES`` + The libraries to link for osgTerrain (use this) +``OSGTERRAIN_LIBRARY`` + The osgTerrain library +``OSGTERRAIN_LIBRARY_DEBUG`` + The osgTerrain debug library -OSGTERRAIN_LIBRARY - The osgTerrain library OSGTERRAIN_LIBRARY_DEBUG - -The osgTerrain debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgText.cmake b/Modules/FindosgText.cmake index fd3c232..7646ad0 100644 --- a/Modules/FindosgText.cmake +++ b/Modules/FindosgText.cmake @@ -7,28 +7,35 @@ FindosgText -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgText This module defines +Locate osgText This module defines: -OSGTEXT_FOUND - Was osgText found? OSGTEXT_INCLUDE_DIR - Where to find -the headers OSGTEXT_LIBRARIES - The libraries to link for osgText (use -this) +``OSGTEXT_FOUND`` + Was osgText found? +``OSGTEXT_INCLUDE_DIR`` + Where to find the headers +``OSGTEXT_LIBRARIES`` + The libraries to link for osgText (use this) +``OSGTEXT_LIBRARY`` + The osgText library +``OSGTEXT_LIBRARY_DEBUG`` + The osgText debug library -OSGTEXT_LIBRARY - The osgText library OSGTEXT_LIBRARY_DEBUG - The -osgText debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgUtil.cmake b/Modules/FindosgUtil.cmake index e84727a..a34fea0 100644 --- a/Modules/FindosgUtil.cmake +++ b/Modules/FindosgUtil.cmake @@ -7,28 +7,35 @@ FindosgUtil -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgUtil This module defines +Locate osgUtil This module defines: -OSGUTIL_FOUND - Was osgUtil found? OSGUTIL_INCLUDE_DIR - Where to find -the headers OSGUTIL_LIBRARIES - The libraries to link for osgUtil (use -this) +``OSGUTIL_FOUND`` + Was osgUtil found? +``OSGUTIL_INCLUDE_DIR`` + Where to find the headers +``OSGUTIL_LIBRARIES`` + The libraries to link for osgUtil (use this) +``OSGUTIL_LIBRARY`` + The osgUtil library +``OSGUTIL_LIBRARY_DEBUG`` + The osgUtil debug library -OSGUTIL_LIBRARY - The osgUtil library OSGUTIL_LIBRARY_DEBUG - The -osgUtil debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgViewer.cmake b/Modules/FindosgViewer.cmake index 2174357..c3834e8 100644 --- a/Modules/FindosgViewer.cmake +++ b/Modules/FindosgViewer.cmake @@ -7,28 +7,35 @@ FindosgViewer -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgViewer This module defines +Locate osgViewer This module defines: -OSGVIEWER_FOUND - Was osgViewer found? OSGVIEWER_INCLUDE_DIR - Where -to find the headers OSGVIEWER_LIBRARIES - The libraries to link for -osgViewer (use this) +``OSGVIEWER_FOUND`` + Was osgViewer found? +``OSGVIEWER_INCLUDE_DIR`` + Where to find the headers +``OSGVIEWER_LIBRARIES`` + The libraries to link for osgViewer (use this) +``OSGVIEWER_LIBRARY`` + The osgViewer library +``OSGVIEWER_LIBRARY_DEBUG`` + The osgViewer debug library -OSGVIEWER_LIBRARY - The osgViewer library OSGVIEWER_LIBRARY_DEBUG - -The osgViewer debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgVolume.cmake b/Modules/FindosgVolume.cmake index 35defef..58d9f7a 100644 --- a/Modules/FindosgVolume.cmake +++ b/Modules/FindosgVolume.cmake @@ -7,28 +7,35 @@ FindosgVolume -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgVolume This module defines +Locate osgVolume This module defines: -OSGVOLUME_FOUND - Was osgVolume found? OSGVOLUME_INCLUDE_DIR - Where -to find the headers OSGVOLUME_LIBRARIES - The libraries to link for -osgVolume (use this) +``OSGVOLUME_FOUND`` + Was osgVolume found? +``OSGVOLUME_INCLUDE_DIR`` + Where to find the headers +``OSGVOLUME_LIBRARIES`` + The libraries to link for osgVolume (use this) +``OSGVOLUME_LIBRARY`` + The osgVolume library +``OSGVOLUME_LIBRARY_DEBUG`` + The osgVolume debug library -OSGVOLUME_LIBRARY - The osgVolume library OSGVOLUME_LIBRARY_DEBUG - -The osgVolume debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. Created by Eric Wing. #]=======================================================================] diff --git a/Modules/FindosgWidget.cmake b/Modules/FindosgWidget.cmake index c7aae44..4049fad 100644 --- a/Modules/FindosgWidget.cmake +++ b/Modules/FindosgWidget.cmake @@ -7,28 +7,35 @@ FindosgWidget -This is part of the Findosg* suite used to find OpenSceneGraph +This is part of the ``Findosg*`` suite used to find OpenSceneGraph components. Each component is separate and you must opt in to each module. You must also opt into OpenGL and OpenThreads (and Producer if needed) as these modules won't do it for you. This is to allow you control over your own system piece by piece in case you need to opt out of certain components or change the Find behavior for a particular -module (perhaps because the default FindOpenGL.cmake module doesn't +module (perhaps because the default :module:`FindOpenGL` module doesn't work with your system as an example). If you want to use a more convenient module that includes everything, use the -FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules. +:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules. -Locate osgWidget This module defines +Locate osgWidget This module defines: -OSGWIDGET_FOUND - Was osgWidget found? OSGWIDGET_INCLUDE_DIR - Where -to find the headers OSGWIDGET_LIBRARIES - The libraries to link for -osgWidget (use this) +``OSGWIDGET_FOUND`` + Was osgWidget found? +``OSGWIDGET_INCLUDE_DIR`` + Where to find the headers +``OSGWIDGET_LIBRARIES`` + The libraries to link for osgWidget (use this) +``OSGWIDGET_LIBRARY`` + The osgWidget library +``OSGWIDGET_LIBRARY_DEBUG`` + The osgWidget debug library -OSGWIDGET_LIBRARY - The osgWidget library OSGWIDGET_LIBRARY_DEBUG - -The osgWidget debug library +``$OSGDIR`` is an environment variable that would correspond to:: -$OSGDIR is an environment variable that would correspond to the -./configure --prefix=$OSGDIR used in building osg. + ./configure --prefix=$OSGDIR + +used in building osg. FindosgWidget.cmake tweaked from Findosg* suite as created by Eric Wing. diff --git a/Modules/Findosg_functions.cmake b/Modules/Findosg_functions.cmake index 563b6bd..5226102 100644 --- a/Modules/Findosg_functions.cmake +++ b/Modules/Findosg_functions.cmake @@ -10,7 +10,7 @@ Findosg_functions This CMake file contains two macros to assist with searching for OSG -libraries and nodekits. Please see FindOpenSceneGraph.cmake for full +libraries and nodekits. Please see :module:`FindOpenSceneGraph` for full documentation. #]=======================================================================] diff --git a/Modules/FortranCInterface.cmake b/Modules/FortranCInterface.cmake index 2c85029..81481a7 100644 --- a/Modules/FortranCInterface.cmake +++ b/Modules/FortranCInterface.cmake @@ -374,6 +374,7 @@ function(FortranCInterface_VERIFY) "-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE}" "-DCMAKE_Fortran_FLAGS_RELEASE:STRING=${CMAKE_Fortran_FLAGS_RELEASE}" "-DFortranCInterface_BINARY_DIR=${FortranCInterface_BINARY_DIR}" + "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" ${_FortranCInterface_OSX_ARCH} ${_FortranCInterface_EXE_LINKER_FLAGS} OUTPUT_VARIABLE _output) diff --git a/Modules/Internal/CMakeCUDAArchitecturesAll.cmake b/Modules/Internal/CMakeCUDAArchitecturesAll.cmake new file mode 100644 index 0000000..873400a --- /dev/null +++ b/Modules/Internal/CMakeCUDAArchitecturesAll.cmake @@ -0,0 +1,88 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# See supported GPUs on Wikipedia +# https://en.wikipedia.org/wiki/CUDA#GPUs_supported + +function(cmake_cuda_architectures_all lang lang_var_) + # Initial set based on CUDA 7.0. + set(CMAKE_CUDA_ARCHITECTURES_ALL 20 21 30 35 37 50 52 53) + set(CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20 30 35 50) + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 8.0) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 60 61 62) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 60) + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 9.0) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA" + OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) + ) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 70 72) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 70) + endif() + + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 20 21) + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20) + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 10.0) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA" + OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) + ) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 75) + endif() + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.0) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA" + OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) + ) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 80) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 80) + endif() + + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 30) + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 30) + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.1) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA" + OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) + ) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 86) + endif() + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87) + endif() + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90) + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90) + endif() + endif() + + if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0) + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37) + list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35) + endif() + endif() + + # only generate jit code for the newest arch for all/all-major + list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch) + list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL APPEND "-real") + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL ${_latest_arch}) + + list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR _latest_arch) + list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR APPEND "-real") + list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR ${_latest_arch}) + + set(CMAKE_${lang}_ARCHITECTURES_ALL "${CMAKE_CUDA_ARCHITECTURES_ALL}" PARENT_SCOPE) + set(CMAKE_${lang}_ARCHITECTURES_ALL_MAJOR "${CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR}" PARENT_SCOPE) +endfunction() diff --git a/Modules/Internal/CMakeCUDAArchitecturesNative.cmake b/Modules/Internal/CMakeCUDAArchitecturesNative.cmake new file mode 100644 index 0000000..4185eda --- /dev/null +++ b/Modules/Internal/CMakeCUDAArchitecturesNative.cmake @@ -0,0 +1,49 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +function(cmake_cuda_architectures_native lang) + # Run the test binary to detect the native architectures. + execute_process(COMMAND "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_${lang}.bin" + RESULT_VARIABLE archs_result + OUTPUT_VARIABLE archs_output + ERROR_VARIABLE archs_output + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(archs_result EQUAL 0) + if("$ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}") + # Undocumented hook used by CMake's CI. + # Clamp native architecture to version range supported by this CUDA. + list(GET CMAKE_${lang}_ARCHITECTURES_ALL 0 arch_min) + list(GET CMAKE_${lang}_ARCHITECTURES_ALL -1 arch_max) + set(CMAKE_CUDA_ARCHITECTURES_NATIVE "") + foreach(arch IN LISTS archs_output) + if(arch LESS arch_min) + set(arch "${arch_min}") + endif() + if(arch GREATER arch_max) + set(arch "${arch_max}") + endif() + list(APPEND CMAKE_CUDA_ARCHITECTURES_NATIVE ${arch}) + endforeach() + unset(arch) + unset(arch_min) + unset(arch_max) + else() + set(CMAKE_CUDA_ARCHITECTURES_NATIVE "${archs_output}") + endif() + list(REMOVE_DUPLICATES CMAKE_CUDA_ARCHITECTURES_NATIVE) + list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_NATIVE APPEND "-real") + else() + if(NOT archs_result MATCHES "[0-9]+") + set(archs_status " (${archs_result})") + else() + set(archs_status "") + endif() + string(REPLACE "\n" "\n " archs_output " ${archs_output}") + message(CONFIGURE_LOG + "Detecting the CUDA native architecture(s) failed with " + "the following output${archs_status}:\n${archs_output}\n\n") + endif() + + set(CMAKE_${lang}_ARCHITECTURES_NATIVE "${CMAKE_CUDA_ARCHITECTURES_NATIVE}" PARENT_SCOPE) +endfunction() diff --git a/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake b/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake new file mode 100644 index 0000000..b6997d2 --- /dev/null +++ b/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake @@ -0,0 +1,19 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +function(cmake_cuda_architectures_validate lang) + if(DEFINED CMAKE_${lang}_ARCHITECTURES) + if(CMAKE_${lang}_ARCHITECTURES STREQUAL "") + message(FATAL_ERROR "CMAKE_${lang}_ARCHITECTURES must be non-empty if set.") + elseif(CMAKE_${lang}_ARCHITECTURES AND NOT CMAKE_${lang}_ARCHITECTURES MATCHES "^([0-9]+a?(-real|-virtual)?(;[0-9]+a?(-real|-virtual)?|;)*|all|all-major|native)$") + message(FATAL_ERROR + "CMAKE_${lang}_ARCHITECTURES:\n" + " ${CMAKE_${lang}_ARCHITECTURES}\n" + "is not one of the following:\n" + " * a semicolon-separated list of integers, each optionally\n" + " followed by '-real' or '-virtual'\n" + " * a special value: all, all-major, native\n" + ) + endif() + endif() +endfunction() diff --git a/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake b/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake new file mode 100644 index 0000000..60287af --- /dev/null +++ b/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake @@ -0,0 +1,20 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +# In CMakeDetermineCUDACompiler and CMakeTestCUDACompiler we detect +# libraries that the CUDA compiler implicitly passes to the host linker. +# CMake invokes the host linker directly and so needs to pass these libraries. +# Filter out implicit link libraries that should not be passed unconditionally. +macro(cmake_cuda_filter_implicit_libs _var_CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES) + list(REMOVE_ITEM "${_var_CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES}" + # The CUDA runtime libraries are controlled by CMAKE_CUDA_RUNTIME_LIBRARY. + cudart cudart.lib + cudart_static cudart_static.lib + cudadevrt cudadevrt.lib + + # Dependencies of the CUDA static runtime library on Linux hosts. + rt + pthread + dl + ) +endmacro() diff --git a/Modules/Internal/CMakeCUDAFindToolkit.cmake b/Modules/Internal/CMakeCUDAFindToolkit.cmake new file mode 100644 index 0000000..58d1e55 --- /dev/null +++ b/Modules/Internal/CMakeCUDAFindToolkit.cmake @@ -0,0 +1,173 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +macro(cmake_cuda_find_toolkit lang lang_var_) + # This is very similar to FindCUDAToolkit, but somewhat simplified since we can issue fatal errors + # if we fail and we don't need to account for searching the libraries. + + # For NVCC we can easily deduce the SDK binary directory from the compiler path. + if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA") + set(_CUDA_NVCC_EXECUTABLE "${CMAKE_${lang}_COMPILER}") + else() + # Search using CUDAToolkit_ROOT and then CUDA_PATH for equivalence with FindCUDAToolkit. + # In FindCUDAToolkit CUDAToolkit_ROOT is searched automatically due to being in a find_package(). + # First we search candidate non-default paths to give them priority. + find_program(_CUDA_NVCC_EXECUTABLE + NAMES nvcc nvcc.exe + PATHS ${CUDAToolkit_ROOT} + ENV CUDAToolkit_ROOT + ENV CUDA_PATH + PATH_SUFFIXES bin + NO_DEFAULT_PATH + NO_CACHE + ) + + # If we didn't find NVCC, then try the default paths. + find_program(_CUDA_NVCC_EXECUTABLE + NAMES nvcc nvcc.exe + PATH_SUFFIXES bin + NO_CACHE + ) + + # If the user specified CUDAToolkit_ROOT but nvcc could not be found, this is an error. + if(NOT _CUDA_NVCC_EXECUTABLE AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT})) + set(fail_base "Could not find nvcc executable in path specified by") + + if(DEFINED CUDAToolkit_ROOT) + message(FATAL_ERROR "${fail_base} CUDAToolkit_ROOT=${CUDAToolkit_ROOT}") + elseif(DEFINED ENV{CUDAToolkit_ROOT}) + message(FATAL_ERROR "${fail_base} environment variable CUDAToolkit_ROOT=$ENV{CUDAToolkit_ROOT}") + endif() + endif() + + # CUDAToolkit_ROOT cmake/env variable not specified, try platform defaults. + # + # - Linux: /usr/local/cuda-X.Y + # - macOS: /Developer/NVIDIA/CUDA-X.Y + # - Windows: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\vX.Y + # + # We will also search the default symlink location /usr/local/cuda first since + # if CUDAToolkit_ROOT is not specified, it is assumed that the symlinked + # directory is the desired location. + if(NOT _CUDA_NVCC_EXECUTABLE) + if(UNIX) + if(NOT APPLE) + set(platform_base "/usr/local/cuda-") + else() + set(platform_base "/Developer/NVIDIA/CUDA-") + endif() + else() + set(platform_base "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v") + endif() + + # Build out a descending list of possible cuda installations, e.g. + file(GLOB possible_paths "${platform_base}*") + # Iterate the glob results and create a descending list. + set(versions) + foreach(p ${possible_paths}) + # Extract version number from end of string + string(REGEX MATCH "[0-9][0-9]?\\.[0-9]$" p_version ${p}) + if(IS_DIRECTORY ${p} AND p_version) + list(APPEND versions ${p_version}) + endif() + endforeach() + + # Sort numerically in descending order, so we try the newest versions first. + list(SORT versions COMPARE NATURAL ORDER DESCENDING) + + # With a descending list of versions, populate possible paths to search. + set(search_paths) + foreach(v ${versions}) + list(APPEND search_paths "${platform_base}${v}") + endforeach() + + # Force the global default /usr/local/cuda to the front on Unix. + if(UNIX) + list(INSERT search_paths 0 "/usr/local/cuda") + endif() + + # Now search for nvcc again using the platform default search paths. + find_program(_CUDA_NVCC_EXECUTABLE + NAMES nvcc nvcc.exe + PATHS ${search_paths} + PATH_SUFFIXES bin + NO_CACHE + ) + + # We are done with these variables now, cleanup. + unset(platform_base) + unset(possible_paths) + unset(versions) + unset(search_paths) + + if(NOT _CUDA_NVCC_EXECUTABLE) + message(FATAL_ERROR "Failed to find nvcc.\nCompiler ${CMAKE_${lang}_COMPILER_ID} requires the CUDA toolkit. Please set the CUDAToolkit_ROOT variable.") + endif() + endif() + endif() + + # Given that NVCC can be provided by multiple different sources (NVIDIA HPC SDK, CUDA Toolkit, distro) + # each of which has a different layout, we need to extract the CUDA toolkit root from the compiler + # itself, allowing us to support numerous different scattered toolkit layouts + execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda" + OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT) + if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)") + get_filename_component(${lang_var_}TOOLKIT_ROOT "${CMAKE_MATCH_1}" ABSOLUTE) + else() + get_filename_component(${lang_var_}TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY) + get_filename_component(${lang_var_}TOOLKIT_ROOT "${${lang_var_}TOOLKIT_ROOT}" DIRECTORY) + endif() + + if(_CUDA_NVCC_OUT MATCHES "\\#\\$ NVVMIR_LIBRARY_DIR=([^\r\n]*)") + get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${CMAKE_MATCH_1}" ABSOLUTE) + + #We require the path to end in `/nvvm/libdevice' + if(_CUDA_NVVMIR_LIBRARY_DIR MATCHES "nvvm/libdevice$") + get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}/../.." ABSOLUTE) + set(_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}") + endif() + + unset(_CUDA_NVVMIR_LIBRARY_DIR) + unset(_cuda_nvvmir_dir_name) + endif() + unset(_CUDA_NVCC_OUT) + + # In a non-scattered installation the following are equivalent to ${lang_var_}TOOLKIT_ROOT. + # We first check for a non-scattered installation to prefer it over a scattered installation. + + # ${lang_var_}LIBRARY_ROOT contains the device library. + if(DEFINED _CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR) + set(${lang_var_}LIBRARY_ROOT "${_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR}") + elseif(EXISTS "${${lang_var_}TOOLKIT_ROOT}/nvvm/libdevice") + set(${lang_var_}LIBRARY_ROOT "${${lang_var_}TOOLKIT_ROOT}") + elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/nvvm/libdevice") + set(${lang_var_}LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda") + elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/nvvm/libdevice") + set(${lang_var_}LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/cuda") + else() + message(FATAL_ERROR "Couldn't find CUDA library root.") + endif() + unset(_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR) + + # ${lang_var_}TOOLKIT_LIBRARY_ROOT contains the linking stubs necessary for device linking and other low-level library files. + if(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub") + set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit") + elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub") + set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit") + else() + set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${${lang_var_}TOOLKIT_ROOT}") + endif() + + # For regular nvcc we the toolkit version is the same as the compiler version and we can parse it from the vendor test output. + # For Clang we need to invoke nvcc to get version output. + if(CMAKE_${lang}_COMPILER_ID STREQUAL "Clang") + execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE CMAKE_${lang}_COMPILER_ID_OUTPUT) + endif() + + if(CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=]) + set(${lang_var_}TOOLKIT_VERSION "${CMAKE_MATCH_1}") + endif() + + # Don't leak variables unnecessarily to user code. + unset(_CUDA_NVCC_EXECUTABLE) +endmacro() diff --git a/Modules/Internal/CMakeNVCCFilterImplicitInfo.cmake b/Modules/Internal/CMakeNVCCFilterImplicitInfo.cmake new file mode 100644 index 0000000..dee7580 --- /dev/null +++ b/Modules/Internal/CMakeNVCCFilterImplicitInfo.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. + +macro(cmake_nvcc_filter_implicit_info lang lang_var_) + # Remove the CUDA Toolkit include directories from the set of + # implicit system include directories. + # This resolves the issue that NVCC doesn't specify these + # includes as SYSTEM includes when compiling device code, and sometimes + # they contain headers that generate warnings, so let users mark them + # as SYSTEM explicitly + if(${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES) + list(REMOVE_ITEM CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES + ${${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES} + ) + endif() +endmacro() diff --git a/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake new file mode 100644 index 0000000..32ff28a --- /dev/null +++ b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake @@ -0,0 +1,149 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +macro(cmake_nvcc_parse_implicit_info lang lang_var_) + set(_nvcc_log "") + string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_${lang}_COMPILER_PRODUCED_OUTPUT}") + if(_nvcc_output_orig MATCHES "#\\\$ +PATH= *([^\n]*)\n") + set(_nvcc_path "${CMAKE_MATCH_1}") + string(APPEND _nvcc_log " found 'PATH=' string: [${_nvcc_path}]\n") + string(REPLACE ":" ";" _nvcc_path "${_nvcc_path}") + else() + set(_nvcc_path "") + string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") + string(APPEND _nvcc_log " no 'PATH=' string found in nvcc output:${_nvcc_output_log}\n") + endif() + if(_nvcc_output_orig MATCHES "#\\\$ +LIBRARIES= *([^\n]*)\n") + set(_nvcc_libraries "${CMAKE_MATCH_1}") + string(APPEND _nvcc_log " found 'LIBRARIES=' string: [${_nvcc_libraries}]\n") + else() + set(_nvcc_libraries "") + string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") + string(APPEND _nvcc_log " no 'LIBRARIES=' string found in nvcc output:${_nvcc_output_log}\n") + endif() + if(_nvcc_output_orig MATCHES "#\\\$ +INCLUDES= *([^\n]*)\n") + set(_nvcc_includes "${CMAKE_MATCH_1}") + string(APPEND _nvcc_log " found 'INCLUDES=' string: [${_nvcc_includes}]\n") + else() + set(_nvcc_includes "") + string(REPLACE "\n" "\n " _nvcc_output_log "\n${_nvcc_output_orig}") + string(APPEND _nvcc_log " no 'INCLUDES=' string found in nvcc output:${_nvcc_output_log}\n") + endif() + string(REGEX MATCHALL "-arch compute_([0-9]+)" _nvcc_target_cpus "${_nvcc_output_orig}") + foreach(_nvcc_target_cpu ${_nvcc_target_cpus}) + if(_nvcc_target_cpu MATCHES "-arch compute_([0-9]+)") + list(APPEND CMAKE_${lang}_ARCHITECTURES_DEFAULT "${CMAKE_MATCH_1}") + endif() + endforeach() + + set(_nvcc_link_line "") + if(_nvcc_libraries) + # Remove variable assignments. + string(REGEX REPLACE "#\\\$ *[^= ]+=[^\n]*\n" "" _nvcc_output "${_nvcc_output_orig}") + # Encode [] characters that break list expansion. + string(REPLACE "[" "{==={" _nvcc_output "${_nvcc_output}") + string(REPLACE "]" "}===}" _nvcc_output "${_nvcc_output}") + # Split lines. + string(REGEX REPLACE "\n+(#\\\$ )?" ";" _nvcc_output "${_nvcc_output}") + foreach(line IN LISTS _nvcc_output) + set(_nvcc_output_line "${line}") + string(REPLACE "{==={" "[" _nvcc_output_line "${_nvcc_output_line}") + string(REPLACE "}===}" "]" _nvcc_output_line "${_nvcc_output_line}") + string(APPEND _nvcc_log " considering line: [${_nvcc_output_line}]\n") + if("${_nvcc_output_line}" MATCHES "^ *nvlink") + string(APPEND _nvcc_log " ignoring nvlink line\n") + elseif(_nvcc_libraries) + if("${_nvcc_output_line}" MATCHES "(@\"?((tmp/)?a\\.exe\\.res)\"?)") + set(_nvcc_link_res_arg "${CMAKE_MATCH_1}") + set(_nvcc_link_res_file "${CMAKE_MATCH_2}") + set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerId${lang}/${_nvcc_link_res_file}") + if(EXISTS "${_nvcc_link_res}") + file(READ "${_nvcc_link_res}" _nvcc_link_res_content) + string(REPLACE "${_nvcc_link_res_arg}" "${_nvcc_link_res_content}" _nvcc_output_line "${_nvcc_output_line}") + endif() + endif() + string(FIND "${_nvcc_output_line}" "${_nvcc_libraries}" _nvcc_libraries_pos) + if(NOT _nvcc_libraries_pos EQUAL -1) + set(_nvcc_link_line "${_nvcc_output_line}") + string(APPEND _nvcc_log " extracted link line: [${_nvcc_link_line}]\n") + endif() + endif() + endforeach() + endif() + + if(_nvcc_link_line) + if("x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC") + set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${CMAKE_LINKER}") + else() + #extract the compiler that is being used for linking + separate_arguments(_nvcc_link_line_args UNIX_COMMAND "${_nvcc_link_line}") + list(GET _nvcc_link_line_args 0 _nvcc_host_link_launcher) + if(IS_ABSOLUTE "${_nvcc_host_link_launcher}") + string(APPEND _nvcc_log " extracted link launcher absolute path: [${_nvcc_host_link_launcher}]\n") + set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}") + else() + string(APPEND _nvcc_log " extracted link launcher name: [${_nvcc_host_link_launcher}]\n") + find_program(_nvcc_find_host_link_launcher + NAMES ${_nvcc_host_link_launcher} + PATHS ${_nvcc_path} NO_DEFAULT_PATH) + find_program(_nvcc_find_host_link_launcher + NAMES ${_nvcc_host_link_launcher}) + if(_nvcc_find_host_link_launcher) + string(APPEND _nvcc_log " found link launcher absolute path: [${_nvcc_find_host_link_launcher}]\n") + set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_find_host_link_launcher}") + else() + string(APPEND _nvcc_log " could not find link launcher absolute path\n") + set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}") + endif() + unset(_nvcc_find_host_link_launcher CACHE) + endif() + endif() + + #prefix the line with cuda-fake-ld so that implicit link info believes it is + #a link line + set(_nvcc_link_line "cuda-fake-ld ${_nvcc_link_line}") + CMAKE_PARSE_IMPLICIT_LINK_INFO("${_nvcc_link_line}" + CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES + CMAKE_${lang}_HOST_IMPLICIT_LINK_DIRECTORIES + CMAKE_${lang}_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES + log + "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}" + LANGUAGE ${lang}) + + # Detect CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT from the compiler by looking at which + # cudart library exists in the implicit link libraries passed to the host linker. + # This is required when a project sets the cuda runtime library as part of the + # initial flags. + if(";${CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart_static(\.lib)?;]]) + set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "STATIC") + elseif(";${CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart(\.lib)?;]]) + set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "SHARED") + else() + set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "NONE") + endif() + + message(CONFIGURE_LOG + "Parsed ${lang} nvcc implicit link information:\n${_nvcc_log}\n${log}\n\n") + else() + message(CONFIGURE_LOG + "Failed to parse ${lang} nvcc implicit link information:\n${_nvcc_log}\n\n") + message(FATAL_ERROR "Failed to extract nvcc implicit link line.") + endif() + + set(${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES) + if(_nvcc_includes) + # across all operating system each include directory is prefixed with -I + separate_arguments(_nvcc_output NATIVE_COMMAND "${_nvcc_includes}") + foreach(line IN LISTS _nvcc_output) + string(REGEX REPLACE "^-I" "" line "${line}") + get_filename_component(line "${line}" ABSOLUTE) + list(APPEND ${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES "${line}") + endforeach() + + message(CONFIGURE_LOG + "Parsed CUDA nvcc include information:\n${_nvcc_log}\n${log}\n\n") + else() + message(CONFIGURE_LOG + "Failed to detect CUDA nvcc include information:\n${_nvcc_log}\n\n") + endif() +endmacro() diff --git a/Modules/Internal/CPack/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in index 21753af..6349f9d 100644 --- a/Modules/Internal/CPack/NSIS.template.in +++ b/Modules/Internal/CPack/NSIS.template.in @@ -981,7 +981,7 @@ inst: ;MessageBox MB_OK 'User "$0" is in the Admin group' StrCpy $SV_ALLUSERS "AllUsers" Goto done - StrCmp $1 "Power" 0 +4 + StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' StrCpy $SV_ALLUSERS "AllUsers" diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake index 4d7546a..57b3910 100644 --- a/Modules/Platform/Apple-Clang.cmake +++ b/Modules/Platform/Apple-Clang.cmake @@ -14,6 +14,10 @@ macro(__apple_compiler_clang lang) if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.2) set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") endif() + + set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") + set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) + if(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneOS") set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-miphoneos-version-min=") elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneSimulator") diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake index 9572736..823c790 100644 --- a/Modules/Platform/Apple-GNU.cmake +++ b/Modules/Platform/Apple-GNU.cmake @@ -14,6 +14,9 @@ macro(__apple_compiler_gnu lang) if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.3) set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ") endif() + + set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>") + set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE) endmacro() macro(cmake_gnu_set_sysroot_flag lang) diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake index 088b238..412af6b 100644 --- a/Modules/Platform/Windows-GNU.cmake +++ b/Modules/Platform/Windows-GNU.cmake @@ -10,6 +10,25 @@ set(__WINDOWS_GNU 1) set(MINGW 1) +# On Windows hosts, in MSYSTEM environments, search standard prefixes. +if(CMAKE_HOST_WIN32) + # Bootstrap CMake does not have cmake_host_system_information. + if(COMMAND cmake_host_system_information) + cmake_host_system_information(RESULT _MSYSTEM_PREFIX QUERY MSYSTEM_PREFIX) + elseif(IS_DIRECTORY "$ENV{MSYSTEM_PREFIX}") + set(_MSYSTEM_PREFIX "$ENV{MSYSTEM_PREFIX}") + endif() + + # Search this MSYSTEM environment's equivalent to /usr/local and /usr. + if(_MSYSTEM_PREFIX) + list(PREPEND CMAKE_SYSTEM_PREFIX_PATH "${_MSYSTEM_PREFIX}") + if(IS_DIRECTORY "${_MSYSTEM_PREFIX}/local") + list(PREPEND CMAKE_SYSTEM_PREFIX_PATH "${_MSYSTEM_PREFIX}/local") + endif() + endif() + unset(_MSYSTEM_PREFIX) +endif() + set(CMAKE_IMPORT_LIBRARY_PREFIX "lib") set(CMAKE_SHARED_LIBRARY_PREFIX "lib") set(CMAKE_SHARED_MODULE_PREFIX "lib") diff --git a/Modules/Platform/Windows-OrangeC-C.cmake b/Modules/Platform/Windows-OrangeC-C.cmake new file mode 100644 index 0000000..6b7e6b5 --- /dev/null +++ b/Modules/Platform/Windows-OrangeC-C.cmake @@ -0,0 +1,2 @@ +include(Platform/Windows-OrangeC) +__windows_compiler_orangec(C) diff --git a/Modules/Platform/Windows-OrangeC-CXX.cmake b/Modules/Platform/Windows-OrangeC-CXX.cmake new file mode 100644 index 0000000..7de6716 --- /dev/null +++ b/Modules/Platform/Windows-OrangeC-CXX.cmake @@ -0,0 +1,2 @@ +include(Platform/Windows-OrangeC) +__windows_compiler_orangec(CXX) diff --git a/Modules/Platform/Windows-OrangeC.cmake b/Modules/Platform/Windows-OrangeC.cmake new file mode 100644 index 0000000..4f66e0e --- /dev/null +++ b/Modules/Platform/Windows-OrangeC.cmake @@ -0,0 +1,10 @@ +set(CMAKE_LINK_LIBRARY_SUFFIX "") +set(CMAKE_STATIC_LIBRARY_SUFFIX ".l") +set(CMAKE_IMPORT_LIBRARY_SUFFIX ".l") +set(CMAKE_FIND_LIBRARY_PREFIXES "") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".l") + +macro(__windows_compiler_orangec lang) + set(CMAKE_${lang}_CREATE_WIN32_EXE "-Wg") + set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Wc") +endmacro() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 84df4a3..cddb097 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 27) -set(CMake_VERSION_PATCH 20230823) +set(CMake_VERSION_PATCH 20230929) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx index 6ad3755..1248d17 100644 --- a/Source/CPack/cmCPackSTGZGenerator.cxx +++ b/Source/CPack/cmCPackSTGZGenerator.cxx @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <fcntl.h> + #include "cmsys/FStream.hxx" #include "cm_sys_stat.h" diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx index 246e811..36df344 100644 --- a/Source/CTest/cmCTestBZR.cxx +++ b/Source/CTest/cmCTestBZR.cxx @@ -135,14 +135,14 @@ private: std::string cmCTestBZR::LoadInfo() { // Run "bzr info" to get the repository info from the work tree. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_info[] = { bzr, "info", nullptr }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_info = { bzr, "info" }; InfoParser iout(this, "info-out> "); OutputLogger ierr(this->Log, "info-err> "); this->RunChild(bzr_info, &iout, &ierr); // Run "bzr revno" to get the repository revision number from the work tree. - const char* bzr_revno[] = { bzr, "revno", nullptr }; + std::vector<std::string> bzr_revno = { bzr, "revno" }; std::string rev; RevnoParser rout(this, "revno-out> ", rev); OutputLogger rerr(this->Log, "revno-err> "); @@ -372,22 +372,18 @@ bool cmCTestBZR::UpdateImpl() // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) // Use "bzr pull" to update the working tree. - std::vector<char const*> bzr_update; - bzr_update.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> bzr_update; + bzr_update.push_back(this->CommandLineTool); bzr_update.push_back("pull"); - for (std::string const& arg : args) { - bzr_update.push_back(arg.c_str()); - } - - bzr_update.push_back(this->URL.c_str()); + cm::append(bzr_update, args); - bzr_update.push_back(nullptr); + bzr_update.push_back(this->URL); // For some reason bzr uses stderr to display the update status. OutputLogger out(this->Log, "pull-out> "); UpdateParser err(this, "pull-err> "); - return this->RunUpdateCommand(bzr_update.data(), &out, &err); + return this->RunUpdateCommand(bzr_update, &out, &err); } bool cmCTestBZR::LoadRevisions() @@ -408,10 +404,9 @@ bool cmCTestBZR::LoadRevisions() } // Run "bzr log" to get all global revisions of interest. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_log[] = { - bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr - }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_log = { bzr, "log", "-v", "-r", + revs, "--xml", this->URL }; { LogParser out(this, "log-out> "); OutputLogger err(this->Log, "log-err> "); @@ -467,8 +462,8 @@ private: bool cmCTestBZR::LoadModifications() { // Run "bzr status" which reports local modifications. - const char* bzr = this->CommandLineTool.c_str(); - const char* bzr_status[] = { bzr, "status", "-SV", nullptr }; + std::string bzr = this->CommandLineTool; + std::vector<std::string> bzr_status = { bzr, "status", "-SV" }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(bzr_status, &out, &err); diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 5feb953..bb6ccc3 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -7,8 +7,6 @@ #include <cstring> #include <ratio> -#include "cmsys/Process.h" - #include "cmBuildOptions.h" #include "cmCTest.h" #include "cmCTestTestHandler.h" @@ -308,12 +306,11 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) return 1; } - std::vector<const char*> testCommand; - testCommand.push_back(fullPath.c_str()); + std::vector<std::string> testCommand; + testCommand.push_back(fullPath); for (std::string const& testCommandArg : this->TestCommandArgs) { - testCommand.push_back(testCommandArg.c_str()); + testCommand.push_back(testCommandArg); } - testCommand.push_back(nullptr); std::string outs; int retval = 0; // run the test from the this->BuildRunDir if set @@ -349,10 +346,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) } } - int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, - remainingTime, nullptr); + bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr, + remainingTime, nullptr); - if (runTestRes != cmsysProcess_State_Exited || retval != 0) { + if (!runTestRes || retval != 0) { out << "Test command failed: " << testCommand[0] << "\n"; retval = 1; } diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 00ecf42..859798e 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -3,15 +3,17 @@ #include "cmCTestBuildHandler.h" #include <cstdlib> +#include <memory> #include <ratio> #include <set> #include <utility> #include <cmext/algorithm> +#include <cm3p/uv.h> + #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestLaunchReporter.h" @@ -24,6 +26,9 @@ #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -420,7 +425,7 @@ int cmCTestBuildHandler::ProcessHandler() cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr); this->ColorRemover = &colorRemover; int retVal = 0; - int res = cmsysProcess_State_Exited; + bool res = true; if (!this->CTest->GetShowOnly()) { res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, ofs); @@ -475,7 +480,7 @@ int cmCTestBuildHandler::ProcessHandler() } this->GenerateXMLFooter(xml, elapsed_build_time); - if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) { + if (!res || retVal || this->TotalErrors > 0) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Error(s) when building project" << std::endl); } @@ -764,10 +769,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers( } } -int cmCTestBuildHandler::RunMakeCommand(const std::string& command, - int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding) +bool cmCTestBuildHandler::RunMakeCommand(const std::string& command, + int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding) { // First generate the command and arguments std::vector<std::string> args = cmSystemTools::ParseArguments(command); @@ -776,19 +781,9 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, return false; } - std::vector<const char*> argv; - argv.reserve(args.size() + 1); - for (std::string const& arg : args) { - argv.push_back(arg.c_str()); - } - argv.push_back(nullptr); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run command:", this->Quiet); - for (char const* arg : argv) { - if (!arg) { - break; - } + for (auto const& arg : args) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"", this->Quiet); } @@ -800,21 +795,20 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, static_cast<void>(launchHelper); // Now create process object - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - cmsysProcess_SetTimeout(cp, timeout); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(args) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + if (dir) { + builder.SetWorkingDirectory(dir); + } + auto chain = builder.Start(); // Initialize tick's std::string::size_type tick = 0; - const std::string::size_type tick_len = 1024; + static constexpr std::string::size_type tick_len = 1024; - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; cmCTestOptionalLog( this->CTest, HANDLER_PROGRESS_OUTPUT, " Each symbol represents " @@ -836,39 +830,65 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, this->WarningQuotaReached = false; this->ErrorQuotaReached = false; + cm::uv_timer_ptr timer; + bool timedOut = false; + timer.init(chain.GetLoop(), &timedOut); + if (timeout > 0) { + timer.start( + [](uv_timer_t* t) { + auto* timedOutPtr = static_cast<bool*>(t->data); + *timedOutPtr = true; + }, + timeout * 1000, 0); + } + // For every chunk of data - int res; - while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - // Replace '\0' with '\n', since '\0' does not really make sense. This is - // for Visual Studio output - for (int cc = 0; cc < length; ++cc) { - if (data[cc] == 0) { - data[cc] = '\n'; - } - } + cm::uv_pipe_ptr outputStream; + bool outFinished = false; + cm::uv_pipe_ptr errorStream; + bool errFinished = false; + auto startRead = [this, &chain, &processOutput, &tick, + &ofs](cm::uv_pipe_ptr& pipe, int stream, + t_BuildProcessingQueueType& queue, bool& finished, + int id) -> std::unique_ptr<cmUVStreamReadHandle> { + pipe.init(chain.GetLoop(), 0); + uv_pipe_open(pipe, stream); + return cmUVStreamRead( + pipe, + [this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) { + // Replace '\0' with '\n', since '\0' does not really make sense. This + // is for Visual Studio output + for (auto& c : data) { + if (c == 0) { + c = '\n'; + } + } - // Process the chunk of data - if (res == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 1); - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingErrorQueue); - } else { - processOutput.DecodeText(data, length, strdata, 2); - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingQueue); - } - } - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingErrorQueue); - } - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs, - &this->BuildProcessingQueue); + // Process the chunk of data + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, + ofs, &queue); + }, + [this, &processOutput, &queue, id, &tick, &ofs, &finished]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, + ofs, &queue); + } + finished = true; + }); + }; + auto outputHandle = startRead(outputStream, chain.OutputStream(), + this->BuildProcessingQueue, outFinished, 1); + auto errorHandle = + startRead(errorStream, chain.ErrorStream(), + this->BuildProcessingErrorQueue, errFinished, 2); + + while (!timedOut && !(outFinished && errFinished && chain.Finished())) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); } - this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, &this->BuildProcessingQueue); this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs, @@ -879,90 +899,93 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, << std::endl, this->Quiet); - // Properly handle output of the build command - cmsysProcess_WaitForExit(cp, nullptr); - int result = cmsysProcess_GetState(cp); - - if (result == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, - "Command exited with the value: " << *retVal - << std::endl, - this->Quiet); - // if a non zero return value - if (*retVal) { - // If there was an error running command, report that on the - // dashboard. - if (this->UseCTestLaunch) { - // For launchers, do not record this top-level error if other - // more granular build errors have already been captured. - bool launcherXMLFound = false; - cmsys::Directory launchDir; - launchDir.Load(this->CTestLaunchDir); - unsigned long n = launchDir.GetNumberOfFiles(); - for (unsigned long i = 0; i < n; ++i) { - const char* fname = launchDir.GetFile(i); - if (cmHasLiteralSuffix(fname, ".xml")) { - launcherXMLFound = true; - break; + if (chain.Finished()) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (retVal) { + *retVal = static_cast<int>(status.ExitStatus); + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Command exited with the value: " << *retVal + << std::endl, + this->Quiet); + // if a non zero return value + if (*retVal) { + // If there was an error running command, report that on the + // dashboard. + if (this->UseCTestLaunch) { + // For launchers, do not record this top-level error if other + // more granular build errors have already been captured. + bool launcherXMLFound = false; + cmsys::Directory launchDir; + launchDir.Load(this->CTestLaunchDir); + unsigned long n = launchDir.GetNumberOfFiles(); + for (unsigned long i = 0; i < n; ++i) { + const char* fname = launchDir.GetFile(i); + if (cmHasLiteralSuffix(fname, ".xml")) { + launcherXMLFound = true; + break; + } + } + if (!launcherXMLFound) { + cmCTestLaunchReporter reporter; + reporter.RealArgs = args; + reporter.ComputeFileNames(); + reporter.ExitCode = *retVal; + reporter.Status = status; + // Use temporary BuildLog file to populate this error for + // CDash. + ofs.flush(); + reporter.LogOut = this->LogFileNames["Build"]; + reporter.LogOut += ".tmp"; + reporter.WriteXML(); + } + } else { + cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; + errorwarning.LogLine = 1; + errorwarning.Text = cmStrCat( + "*** WARNING non-zero return value in ctest from: ", args[0]); + errorwarning.PreContext.clear(); + errorwarning.PostContext.clear(); + errorwarning.Error = false; + this->ErrorsAndWarnings.push_back(std::move(errorwarning)); + this->TotalWarnings++; } } - if (!launcherXMLFound) { - cmCTestLaunchReporter reporter; - reporter.RealArgs = args; - reporter.ComputeFileNames(); - reporter.ExitCode = *retVal; - reporter.Process = cp; - // Use temporary BuildLog file to populate this error for CDash. - ofs.flush(); - reporter.LogOut = this->LogFileNames["Build"]; - reporter.LogOut += ".tmp"; - reporter.WriteXML(); - } - } else { - cmCTestBuildErrorWarning errorwarning; - errorwarning.LineNumber = 0; - errorwarning.LogLine = 1; - errorwarning.Text = cmStrCat( - "*** WARNING non-zero return value in ctest from: ", argv[0]); - errorwarning.PreContext.clear(); - errorwarning.PostContext.clear(); - errorwarning.Error = false; - this->ErrorsAndWarnings.push_back(std::move(errorwarning)); - this->TotalWarnings++; } - } - } - } else if (result == cmsysProcess_State_Exception) { - if (retVal) { - *retVal = cmsysProcess_GetExitException(cp); - cmCTestOptionalLog(this->CTest, WARNING, - "There was an exception: " << *retVal << std::endl, - this->Quiet); + break; + case cmUVProcessChain::ExceptionCode::Spawn: { + // If there was an error running command, report that on the dashboard. + cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; + errorwarning.LogLine = 1; + errorwarning.Text = + cmStrCat("*** ERROR executing: ", exception.second); + errorwarning.PreContext.clear(); + errorwarning.PostContext.clear(); + errorwarning.Error = true; + this->ErrorsAndWarnings.push_back(std::move(errorwarning)); + this->TotalErrors++; + cmCTestLog(this->CTest, ERROR_MESSAGE, + "There was an error: " << exception.second << std::endl); + } break; + default: + if (retVal) { + *retVal = status.TermSignal; + cmCTestOptionalLog( + this->CTest, WARNING, + "There was an exception: " << *retVal << std::endl, this->Quiet); + } + break; } - } else if (result == cmsysProcess_State_Expired) { + } else { cmCTestOptionalLog(this->CTest, WARNING, "There was a timeout" << std::endl, this->Quiet); - } else if (result == cmsysProcess_State_Error) { - // If there was an error running command, report that on the dashboard. - cmCTestBuildErrorWarning errorwarning; - errorwarning.LineNumber = 0; - errorwarning.LogLine = 1; - errorwarning.Text = - cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp)); - errorwarning.PreContext.clear(); - errorwarning.PostContext.clear(); - errorwarning.Error = true; - this->ErrorsAndWarnings.push_back(std::move(errorwarning)); - this->TotalErrors++; - cmCTestLog(this->CTest, ERROR_MESSAGE, - "There was an error: " << cmsysProcess_GetErrorString(cp) - << std::endl); } - cmsysProcess_Delete(cp); - return result; + return true; } // ###################################################################### diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index e33294d..90945b1 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -53,9 +53,9 @@ private: //! Run command specialized for make and configure. Returns process status // and retVal is return value or exception. - int RunMakeCommand(const std::string& command, int* retVal, const char* dir, - int timeout, std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + bool RunMakeCommand(const std::string& command, int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding = cmProcessOutput::Auto); enum { diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index 95e898c..ef95b25 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -5,6 +5,7 @@ #include <utility> #include <cm/string_view> +#include <cmext/algorithm> #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" @@ -89,18 +90,15 @@ bool cmCTestCVS::UpdateImpl() } // Run "cvs update" to update the work tree. - std::vector<char const*> cvs_update; - cvs_update.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> cvs_update; + cvs_update.push_back(this->CommandLineTool); cvs_update.push_back("-z3"); cvs_update.push_back("update"); - for (std::string const& arg : args) { - cvs_update.push_back(arg.c_str()); - } - cvs_update.push_back(nullptr); + cm::append(cvs_update, args); UpdateParser out(this, "up-out> "); UpdateParser err(this, "up-err> "); - return this->RunUpdateCommand(cvs_update.data(), &out, &err); + return this->RunUpdateCommand(cvs_update, &out, &err); } class cmCTestCVS::LogParser : public cmCTestVC::LineParser @@ -221,10 +219,8 @@ void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag, cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush); // Run "cvs log" to get revisions of this file on this branch. - const char* cvs = this->CommandLineTool.c_str(); - const char* cvs_log[] = { - cvs, "log", "-N", branchFlag, file.c_str(), nullptr - }; + std::string cvs = this->CommandLineTool; + std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file }; LogParser out(this, "log-out> ", revisions); OutputLogger err(this->Log, "log-err> "); diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx index 914930e..dd8952f 100644 --- a/Source/CTest/cmCTestConfigureHandler.cxx +++ b/Source/CTest/cmCTestConfigureHandler.cxx @@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler() auto elapsed_time_start = std::chrono::steady_clock::now(); std::string output; int retVal = 0; - int res = 0; + bool res = false; if (!this->CTest->GetShowOnly()) { cmGeneratedFileStream os; if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) { diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 2874be7..1aa49cf 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -9,6 +9,7 @@ #include <cstring> #include <iomanip> #include <iterator> +#include <memory> #include <ratio> #include <sstream> #include <type_traits> @@ -18,9 +19,10 @@ #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" +#include "cm_fileno.hxx" + #include "cmCTest.h" #include "cmDuration.h" #include "cmGeneratedFileStream.h" @@ -33,6 +35,7 @@ #include "cmParsePHPCoverage.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmWorkingDirectory.h" #include "cmXMLWriter.h" @@ -40,85 +43,6 @@ class cmMakefile; #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0)) -class cmCTestRunProcess -{ -public: - cmCTestRunProcess() - { - this->Process = cmsysProcess_New(); - this->PipeState = -1; - this->TimeOut = cmDuration(-1); - } - ~cmCTestRunProcess() - { - if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None && - this->PipeState != cmsysProcess_Pipe_Timeout) { - this->WaitForExit(); - } - cmsysProcess_Delete(this->Process); - } - cmCTestRunProcess(const cmCTestRunProcess&) = delete; - cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete; - void SetCommand(const char* command) - { - this->CommandLineStrings.clear(); - this->CommandLineStrings.emplace_back(command); - } - void AddArgument(const char* arg) - { - if (arg) { - this->CommandLineStrings.emplace_back(arg); - } - } - void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; } - void SetTimeout(cmDuration t) { this->TimeOut = t; } - bool StartProcess() - { - std::vector<const char*> args; - args.reserve(this->CommandLineStrings.size()); - for (std::string const& cl : this->CommandLineStrings) { - args.push_back(cl.c_str()); - } - args.push_back(nullptr); // null terminate - cmsysProcess_SetCommand(this->Process, args.data()); - if (!this->WorkingDirectory.empty()) { - cmsysProcess_SetWorkingDirectory(this->Process, - this->WorkingDirectory.c_str()); - } - - cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1); - if (this->TimeOut >= cmDuration::zero()) { - cmsysProcess_SetTimeout(this->Process, this->TimeOut.count()); - } - cmsysProcess_Execute(this->Process); - this->PipeState = cmsysProcess_GetState(this->Process); - // if the process is running or exited return true - return this->PipeState == cmsysProcess_State_Executing || - this->PipeState == cmsysProcess_State_Exited; - } - void SetStdoutFile(const char* fname) - { - cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname); - } - void SetStderrFile(const char* fname) - { - cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname); - } - int WaitForExit(double* timeout = nullptr) - { - this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout); - return this->PipeState; - } - int GetProcessState() const { return this->PipeState; } - -private: - int PipeState; - cmsysProcess* Process; - std::vector<std::string> CommandLineStrings; - std::string WorkingDirectory; - cmDuration TimeOut; -}; - cmCTestCoverageHandler::cmCTestCoverageHandler() = default; void cmCTestCoverageHandler::Initialize() @@ -1940,34 +1864,35 @@ int cmCTestCoverageHandler::RunBullseyeCommand( cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n"); return 0; } + std::vector<std::string> args{ cmd }; if (arg) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run : " << program << " " << arg << "\n", this->Quiet); + args.emplace_back(arg); } else { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run : " << program << "\n", this->Quiet); } // create a process object and start it - cmCTestRunProcess runCoverageSrc; - runCoverageSrc.SetCommand(program.c_str()); - runCoverageSrc.AddArgument(arg); + cmUVProcessChainBuilder builder; std::string stdoutFile = cmStrCat(cont->BinaryDir, "/Testing/Temporary/", this->GetCTestInstance()->GetCurrentTag(), '-', cmd); std::string stderrFile = stdoutFile; stdoutFile += ".stdout"; stderrFile += ".stderr"; - runCoverageSrc.SetStdoutFile(stdoutFile.c_str()); - runCoverageSrc.SetStderrFile(stderrFile.c_str()); - if (!runCoverageSrc.StartProcess()) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Could not run : " << program << " " << arg << "\n" - << "kwsys process state : " - << runCoverageSrc.GetProcessState()); - return 0; - } + std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle( + cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose); + std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle( + cmsys::SystemTools::Fopen(stderrFile, "w"), fclose); + builder.AddCommand(args) + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdoutHandle.get())) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderrHandle.get())); // since we set the output file names wait for it to end - runCoverageSrc.WaitForExit(); + auto chain = builder.Start(); + chain.Wait(); outputFile = stdoutFile; return 1; } diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index 5f8cb74..ca8659e 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -9,8 +9,9 @@ #include <utility> #include <vector> +#include <cmext/algorithm> + #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestVC.h" @@ -18,6 +19,7 @@ #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmValue.h" static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major, @@ -58,9 +60,9 @@ private: std::string cmCTestGIT::GetWorkingRevision() { // Run plumbing "git rev-list" to get work tree revision. - const char* git = this->CommandLineTool.c_str(); - const char* git_rev_list[] = { git, "rev-list", "-n", "1", - "HEAD", "--", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_list = { git, "rev-list", "-n", + "1", "HEAD", "--" }; std::string rev; OneLineParser out(this, "rl-out> ", rev); OutputLogger err(this->Log, "rl-err> "); @@ -92,13 +94,13 @@ std::string cmCTestGIT::FindGitDir() std::string git_dir; // Run "git rev-parse --git-dir" to locate the real .git directory. - const char* git = this->CommandLineTool.c_str(); - char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" }; std::string git_dir_line; OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line); OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); - if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, - cmProcessOutput::UTF8)) { + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, + std::string{}, cmProcessOutput::UTF8)) { git_dir = git_dir_line; } if (git_dir.empty()) { @@ -117,11 +119,10 @@ std::string cmCTestGIT::FindGitDir() std::string cygpath_exe = cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe"); if (cmSystemTools::FileExists(cygpath_exe)) { - char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(), - 0 }; + std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir }; OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line); OutputLogger cygpath_err(this->Log, "cygpath-err> "); - if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr, + if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{}, cmProcessOutput::UTF8)) { git_dir = git_dir_line; } @@ -136,12 +137,12 @@ std::string cmCTestGIT::FindTopDir() std::string top_dir = this->SourceDirectory; // Run "git rev-parse --show-cdup" to locate the top of the tree. - const char* git = this->CommandLineTool.c_str(); - char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" }; std::string cdup; OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup); OutputLogger rev_parse_err(this->Log, "rev-parse-err> "); - if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr, + if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "", cmProcessOutput::UTF8) && !cdup.empty()) { top_dir += "/"; @@ -153,10 +154,10 @@ std::string cmCTestGIT::FindTopDir() bool cmCTestGIT::UpdateByFetchAndReset() { - const char* git = this->CommandLineTool.c_str(); + std::string git = this->CommandLineTool; // Use "git fetch" to get remote commits. - std::vector<char const*> git_fetch; + std::vector<std::string> git_fetch; git_fetch.push_back(git); git_fetch.push_back("fetch"); @@ -166,17 +167,12 @@ bool cmCTestGIT::UpdateByFetchAndReset() opts = this->CTest->GetCTestConfiguration("GITUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - git_fetch.push_back(arg.c_str()); - } - - // Sentinel argument. - git_fetch.push_back(nullptr); + cm::append(git_fetch, args); // Fetch upstream refs. OutputLogger fetch_out(this->Log, "fetch-out> "); OutputLogger fetch_err(this->Log, "fetch-err> "); - if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) { + if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) { return false; } @@ -207,25 +203,22 @@ bool cmCTestGIT::UpdateByFetchAndReset() } // Reset the local branch to point at that tracked from upstream. - char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr }; + std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 }; OutputLogger reset_out(this->Log, "reset-out> "); OutputLogger reset_err(this->Log, "reset-err> "); - return this->RunChild(&git_reset[0], &reset_out, &reset_err); + return this->RunChild(git_reset, &reset_out, &reset_err); } bool cmCTestGIT::UpdateByCustom(std::string const& custom) { cmList git_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<char const*> git_custom; - git_custom.reserve(git_custom_command.size() + 1); - for (std::string const& i : git_custom_command) { - git_custom.push_back(i.c_str()); - } - git_custom.push_back(nullptr); + std::vector<std::string> git_custom; + git_custom.reserve(git_custom_command.size()); + cm::append(git_custom, git_custom_command); OutputLogger custom_out(this->Log, "custom-out> "); OutputLogger custom_err(this->Log, "custom-err> "); - return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err); + return this->RunUpdateCommand(git_custom, &custom_out, &custom_err); } bool cmCTestGIT::UpdateInternal() @@ -244,13 +237,14 @@ bool cmCTestGIT::UpdateImpl() } std::string top_dir = this->FindTopDir(); - const char* git = this->CommandLineTool.c_str(); - const char* recursive = "--recursive"; - const char* sync_recursive = "--recursive"; + std::string git = this->CommandLineTool; + std::string recursive = "--recursive"; + std::string sync_recursive = "--recursive"; // Git < 1.6.5 did not support submodule --recursive + bool support_recursive = true; if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) { - recursive = nullptr; + support_recursive = false; // No need to require >= 1.6.5 if there are no submodules. if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { this->Log << "Git < 1.6.5 cannot update submodules recursively\n"; @@ -258,8 +252,9 @@ bool cmCTestGIT::UpdateImpl() } // Git < 1.8.1 did not support sync --recursive + bool support_sync_recursive = true; if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) { - sync_recursive = nullptr; + support_sync_recursive = false; // No need to require >= 1.8.1 if there are no submodules. if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) { this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n"; @@ -274,35 +269,39 @@ bool cmCTestGIT::UpdateImpl() std::string init_submodules = this->CTest->GetCTestConfiguration("GITInitSubmodules"); if (cmIsOn(init_submodules)) { - char const* git_submodule_init[] = { git, "submodule", "init", nullptr }; + std::vector<std::string> git_submodule_init = { git, "submodule", "init" }; ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); if (!ret) { return false; } } - char const* git_submodule_sync[] = { git, "submodule", "sync", - sync_recursive, nullptr }; + std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" }; + if (support_sync_recursive) { + git_submodule_sync.push_back(sync_recursive); + } ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); if (!ret) { return false; } - char const* git_submodule[] = { git, "submodule", "update", recursive, - nullptr }; + std::vector<std::string> git_submodule = { git, "submodule", "update" }; + if (support_recursive) { + git_submodule.push_back(recursive); + } return this->RunChild(git_submodule, &submodule_out, &submodule_err, - top_dir.c_str()); + top_dir); } unsigned int cmCTestGIT::GetGitVersion() { if (!this->CurrentGitVersion) { - const char* git = this->CommandLineTool.c_str(); - char const* git_version[] = { git, "--version", nullptr }; + std::string git = this->CommandLineTool; + std::vector<std::string> git_version = { git, "--version" }; std::string version; OneLineParser version_out(this, "version-out> ", version); OutputLogger version_err(this->Log, "version-err> "); @@ -605,50 +604,49 @@ bool cmCTestGIT::LoadRevisions() { // Use 'git rev-list ... | git diff-tree ...' to get revisions. std::string range = this->OldRevision + ".." + this->NewRevision; - const char* git = this->CommandLineTool.c_str(); - const char* git_rev_list[] = { git, "rev-list", "--reverse", - range.c_str(), "--", nullptr }; - const char* git_diff_tree[] = { - git, "diff-tree", "--stdin", "--always", "-z", - "-r", "--pretty=raw", "--encoding=utf-8", nullptr + std::string git = this->CommandLineTool; + std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse", + range, "--" }; + std::vector<std::string> git_diff_tree = { + git, "diff-tree", "--stdin", "--always", + "-z", "-r", "--pretty=raw", "--encoding=utf-8" }; this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | " << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n"; - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_AddCommand(cp, git_rev_list); - cmsysProcess_AddCommand(cp, git_diff_tree); - cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str()); + cmUVProcessChainBuilder builder; + builder.AddCommand(git_rev_list) + .AddCommand(git_diff_tree) + .SetWorkingDirectory(this->SourceDirectory); CommitParser out(this, "dt-out> "); OutputLogger err(this->Log, "dt-err> "); - cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8); + cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8); // Send one extra zero-byte to terminate the last record. out.Process("", 1); - cmsysProcess_Delete(cp); return true; } bool cmCTestGIT::LoadModifications() { - const char* git = this->CommandLineTool.c_str(); + std::string git = this->CommandLineTool; // Use 'git update-index' to refresh the index w.r.t. the work tree. - const char* git_update_index[] = { git, "update-index", "--refresh", - nullptr }; + std::vector<std::string> git_update_index = { git, "update-index", + "--refresh" }; OutputLogger ui_out(this->Log, "ui-out> "); OutputLogger ui_err(this->Log, "ui-err> "); - this->RunChild(git_update_index, &ui_out, &ui_err, nullptr, + this->RunChild(git_update_index, &ui_out, &ui_err, "", cmProcessOutput::UTF8); // Use 'git diff-index' to get modified files. - const char* git_diff_index[] = { git, "diff-index", "-z", - "HEAD", "--", nullptr }; + std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD", + "--" }; DiffParser out(this, "di-out> "); OutputLogger err(this->Log, "di-err> "); - this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8); + this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8); for (Change const& c : out.Changes) { this->DoModification(PathModified, c.Path); diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx index 02837ba..e1a945d 100644 --- a/Source/CTest/cmCTestHG.cxx +++ b/Source/CTest/cmCTestHG.cxx @@ -95,8 +95,8 @@ private: std::string cmCTestHG::GetWorkingRevision() { // Run plumbing "hg identify" to get work tree revision. - const char* hg = this->CommandLineTool.c_str(); - const char* hg_identify[] = { hg, "identify", "-i", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_identify = { hg, "identify", "-i" }; std::string rev; IdentifyParser out(this, "rev-out> ", rev); OutputLogger err(this->Log, "rev-err> "); @@ -127,16 +127,16 @@ bool cmCTestHG::UpdateImpl() { // Use "hg pull" followed by "hg update" to update the working tree. { - const char* hg = this->CommandLineTool.c_str(); - const char* hg_pull[] = { hg, "pull", "-v", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_pull = { hg, "pull", "-v" }; OutputLogger out(this->Log, "pull-out> "); OutputLogger err(this->Log, "pull-err> "); - this->RunChild(&hg_pull[0], &out, &err); + this->RunChild(hg_pull, &out, &err); } // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) - std::vector<char const*> hg_update; + std::vector<std::string> hg_update; hg_update.push_back(this->CommandLineTool.c_str()); hg_update.push_back("update"); hg_update.push_back("-v"); @@ -147,16 +147,11 @@ bool cmCTestHG::UpdateImpl() opts = this->CTest->GetCTestConfiguration("HGUpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - hg_update.push_back(arg.c_str()); - } - - // Sentinel argument. - hg_update.push_back(nullptr); + cm::append(hg_update, args); OutputLogger out(this->Log, "update-out> "); OutputLogger err(this->Log, "update-err> "); - return this->RunUpdateCommand(hg_update.data(), &out, &err); + return this->RunUpdateCommand(hg_update, &out, &err); } class cmCTestHG::LogParser @@ -277,8 +272,8 @@ bool cmCTestHG::LoadRevisions() // the project has spaces in the path. Also, they may not have // proper XML escapes. std::string range = this->OldRevision + ":" + this->NewRevision; - const char* hg = this->CommandLineTool.c_str(); - const char* hgXMLTemplate = "<logentry\n" + std::string hg = this->CommandLineTool; + std::string hgXMLTemplate = "<logentry\n" " revision=\"{node|short}\">\n" " <author>{author|person}</author>\n" " <email>{author|email}</email>\n" @@ -288,10 +283,8 @@ bool cmCTestHG::LoadRevisions() " <file_adds>{file_adds}</file_adds>\n" " <file_dels>{file_dels}</file_dels>\n" "</logentry>\n"; - const char* hg_log[] = { - hg, "log", "--removed", "-r", range.c_str(), - "--template", hgXMLTemplate, nullptr - }; + std::vector<std::string> hg_log = { hg, "log", "--removed", "-r", + range, "--template", hgXMLTemplate }; LogParser out(this, "log-out> "); out.Process("<?xml version=\"1.0\"?>\n" @@ -305,8 +298,8 @@ bool cmCTestHG::LoadRevisions() bool cmCTestHG::LoadModifications() { // Use 'hg status' to get modified files. - const char* hg = this->CommandLineTool.c_str(); - const char* hg_status[] = { hg, "status", nullptr }; + std::string hg = this->CommandLineTool; + std::vector<std::string> hg_status = { hg, "status" }; StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); this->RunChild(hg_status, &out, &err); diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 4a33869..6b13ad1 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -2,13 +2,19 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestLaunch.h" +#include <cstdio> #include <cstring> #include <iostream> +#include <memory> +#include <utility> + +#include <cm3p/uv.h> #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" +#include "cm_fileno.hxx" + #include "cmCTestLaunchReporter.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" @@ -17,6 +23,9 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmake.h" #ifdef _WIN32 @@ -28,8 +37,6 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) { - this->Process = nullptr; - if (!this->ParseArguments(argc, argv)) { return; } @@ -40,13 +47,9 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) this->ScrapeRulesLoaded = false; this->HaveOut = false; this->HaveErr = false; - this->Process = cmsysProcess_New(); } -cmCTestLaunch::~cmCTestLaunch() -{ - cmsysProcess_Delete(this->Process); -} +cmCTestLaunch::~cmCTestLaunch() = default; bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) { @@ -113,15 +116,12 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) // Extract the real command line. if (arg0) { - this->RealArgC = argc - arg0; - this->RealArgV = argv + arg0; - for (int i = 0; i < this->RealArgC; ++i) { - this->HandleRealArg(this->RealArgV[i]); + for (int i = 0; i < argc - arg0; ++i) { + this->RealArgV.emplace_back((argv + arg0)[i]); + this->HandleRealArg((argv + arg0)[i]); } return true; } - this->RealArgC = 0; - this->RealArgV = nullptr; std::cerr << "No launch/command separator ('--') found!\n"; return false; } @@ -151,17 +151,22 @@ void cmCTestLaunch::RunChild() } // Prepare to run the real command. - cmsysProcess* cp = this->Process; - cmsysProcess_SetCommand(cp, this->RealArgV); + cmUVProcessChainBuilder builder; + builder.AddCommand(this->RealArgV); cmsys::ofstream fout; cmsys::ofstream ferr; if (this->Reporter.Passthru) { // In passthru mode we just share the output pipes. - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); + builder + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderr)); } else { // In full mode we record the child output pipes to log files. + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary); ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary); } @@ -174,51 +179,65 @@ void cmCTestLaunch::RunChild() #endif // Run the real command. - cmsysProcess_Execute(cp); + auto chain = builder.Start(); // Record child stdout and stderr if necessary. + cm::uv_pipe_ptr outPipe; + cm::uv_pipe_ptr errPipe; + bool outFinished = true; + bool errFinished = true; + cmProcessOutput processOutput; + std::unique_ptr<cmUVStreamReadHandle> outputHandle; + std::unique_ptr<cmUVStreamReadHandle> errorHandle; if (!this->Reporter.Passthru) { - char* data = nullptr; - int length = 0; - cmProcessOutput processOutput; - std::string strdata; - while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { - if (p == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata, 1); - fout.write(strdata.c_str(), strdata.size()); - std::cout.write(strdata.c_str(), strdata.size()); - this->HaveOut = true; - } else if (p == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 2); - ferr.write(strdata.c_str(), strdata.size()); - std::cerr.write(strdata.c_str(), strdata.size()); - this->HaveErr = true; - } - } - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - fout.write(strdata.c_str(), strdata.size()); - std::cout.write(strdata.c_str(), strdata.size()); - } - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - ferr.write(strdata.c_str(), strdata.size()); - std::cerr.write(strdata.c_str(), strdata.size()); - } + auto beginRead = [&chain, &processOutput]( + cm::uv_pipe_ptr& pipe, int stream, std::ostream& out, + cmsys::ofstream& file, bool& haveData, bool& finished, + int id) -> std::unique_ptr<cmUVStreamReadHandle> { + pipe.init(chain.GetLoop(), 0); + uv_pipe_open(pipe, stream); + finished = false; + return cmUVStreamRead( + pipe, + [&processOutput, &out, &file, id, &haveData](std::vector<char> data) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + file.write(strdata.c_str(), strdata.size()); + out.write(strdata.c_str(), strdata.size()); + haveData = true; + }, + [&processOutput, &out, &file, &finished, id]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + file.write(strdata.c_str(), strdata.size()); + out.write(strdata.c_str(), strdata.size()); + } + finished = true; + }); + }; + outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout, + this->HaveOut, outFinished, 1); + errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr, + this->HaveErr, errFinished, 2); } // Wait for the real command to finish. - cmsysProcess_WaitForExit(cp, nullptr); - this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp); + while (!(chain.Finished() && outFinished && errFinished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } + this->Reporter.Status = chain.GetStatus(0); + if (this->Reporter.Status.GetException().first == + cmUVProcessChain::ExceptionCode::Spawn) { + this->Reporter.ExitCode = 1; + } else { + this->Reporter.ExitCode = + static_cast<int>(this->Reporter.Status.ExitStatus); + } } int cmCTestLaunch::Run() { - if (!this->Process) { - std::cerr << "Could not allocate cmsysProcess instance!\n"; - return -1; - } - this->RunChild(); if (this->CheckResults()) { @@ -226,7 +245,6 @@ int cmCTestLaunch::Run() } this->LoadConfig(); - this->Reporter.Process = this->Process; this->Reporter.WriteXML(); return this->Reporter.ExitCode; diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index c5a6476..ef21a26 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -43,15 +43,12 @@ private: bool ParseArguments(int argc, const char* const* argv); // The real command line appearing after launcher arguments. - int RealArgC; - const char* const* RealArgV; + std::vector<std::string> RealArgV; // The real command line after response file expansion. std::vector<std::string> RealArgs; void HandleRealArg(const char* arg); - struct cmsysProcess_s* Process; - // Whether or not any data have been written to stdout or stderr. bool HaveOut; bool HaveErr; diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx index 149ba5d..4b4e5c5 100644 --- a/Source/CTest/cmCTestLaunchReporter.cxx +++ b/Source/CTest/cmCTestLaunchReporter.cxx @@ -2,8 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestLaunchReporter.h" +#include <utility> + #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmCryptoHash.h" @@ -22,6 +23,7 @@ cmCTestLaunchReporter::cmCTestLaunchReporter() { this->Passthru = true; + this->Status.Finished = true; this->ExitCode = 1; this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); @@ -231,35 +233,23 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2) // ExitCondition cmXMLElement e4(e3, "ExitCondition"); - cmsysProcess* cp = this->Process; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Starting: - e4.Content("No process has been executed"); - break; - case cmsysProcess_State_Executing: - e4.Content("The process is still executing"); - break; - case cmsysProcess_State_Disowned: - e4.Content("Disowned"); - break; - case cmsysProcess_State_Killed: - e4.Content("Killed by parent"); - break; - - case cmsysProcess_State_Expired: - e4.Content("Killed when timeout expired"); - break; - case cmsysProcess_State_Exited: - e4.Content(this->ExitCode); - break; - case cmsysProcess_State_Exception: - e4.Content("Terminated abnormally: "); - e4.Content(cmsysProcess_GetExceptionString(cp)); - break; - case cmsysProcess_State_Error: - e4.Content("Error administrating child process: "); - e4.Content(cmsysProcess_GetErrorString(cp)); - break; + if (this->Status.Finished) { + auto exception = this->Status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + e4.Content(this->ExitCode); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + e4.Content("Error administrating child process: "); + e4.Content(exception.second); + break; + default: + e4.Content("Terminated abnormally: "); + e4.Content(exception.second); + break; + } + } else { + e4.Content("Killed when timeout expired"); } } diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h index 4be0d9b..2bb78f8 100644 --- a/Source/CTest/cmCTestLaunchReporter.h +++ b/Source/CTest/cmCTestLaunchReporter.h @@ -10,6 +10,8 @@ #include "cmsys/RegularExpression.hxx" +#include "cmUVProcessChain.h" + class cmXMLElement; /** \class cmCTestLaunchReporter @@ -48,7 +50,7 @@ public: void ComputeFileNames(); bool Passthru; - struct cmsysProcess_s* Process; + cmUVProcessChain::Status Status; int ExitCode; // Temporary log files for stdout and stderr of real command. diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx index 0e002b9..5d71b84 100644 --- a/Source/CTest/cmCTestP4.cxx +++ b/Source/CTest/cmCTestP4.cxx @@ -149,17 +149,16 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username) auto it = this->Users.find(username); if (it == this->Users.end()) { - std::vector<char const*> p4_users; + std::vector<std::string> p4_users; this->SetP4Options(p4_users); p4_users.push_back("users"); p4_users.push_back("-m"); p4_users.push_back("1"); - p4_users.push_back(username.c_str()); - p4_users.push_back(nullptr); + p4_users.push_back(username); UserParser out(this, "users-out> "); OutputLogger err(this->Log, "users-err> "); - this->RunChild(p4_users.data(), &out, &err); + this->RunChild(p4_users, &out, &err); // The user should now be added to the map. Search again. it = this->Users.find(username); @@ -303,10 +302,10 @@ private: } }; -void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) +void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions) { if (this->P4Options.empty()) { - const char* p4 = this->CommandLineTool.c_str(); + std::string p4 = this->CommandLineTool; this->P4Options.emplace_back(p4); // The CTEST_P4_CLIENT variable sets the P4 client used when issuing @@ -328,15 +327,12 @@ void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) cm::append(this->P4Options, cmSystemTools::ParseArguments(opts)); } - CommandOptions.clear(); - for (std::string const& o : this->P4Options) { - CommandOptions.push_back(o.c_str()); - } + CommandOptions = this->P4Options; } std::string cmCTestP4::GetWorkingRevision() { - std::vector<char const*> p4_identify; + std::vector<std::string> p4_identify; this->SetP4Options(p4_identify); p4_identify.push_back("changes"); @@ -345,14 +341,13 @@ std::string cmCTestP4::GetWorkingRevision() p4_identify.push_back("-t"); std::string source = this->SourceDirectory + "/...#have"; - p4_identify.push_back(source.c_str()); - p4_identify.push_back(nullptr); + p4_identify.push_back(source); std::string rev; IdentifyParser out(this, "p4_changes-out> ", rev); OutputLogger err(this->Log, "p4_changes-err> "); - bool result = this->RunChild(p4_identify.data(), &out, &err); + bool result = this->RunChild(p4_identify, &out, &err); // If there was a problem contacting the server return "<unknown>" if (!result) { @@ -388,7 +383,7 @@ bool cmCTestP4::NoteNewRevision() bool cmCTestP4::LoadRevisions() { - std::vector<char const*> p4_changes; + std::vector<std::string> p4_changes; this->SetP4Options(p4_changes); // Use 'p4 changes ...@old,new' to get a list of changelists @@ -409,38 +404,36 @@ bool cmCTestP4::LoadRevisions() .append(this->NewRevision); p4_changes.push_back("changes"); - p4_changes.push_back(range.c_str()); - p4_changes.push_back(nullptr); + p4_changes.push_back(range); ChangesParser out(this, "p4_changes-out> "); OutputLogger err(this->Log, "p4_changes-err> "); this->ChangeLists.clear(); - this->RunChild(p4_changes.data(), &out, &err); + this->RunChild(p4_changes, &out, &err); if (this->ChangeLists.empty()) { return true; } // p4 describe -s ...@1111111,2222222 - std::vector<char const*> p4_describe; + std::vector<std::string> p4_describe; for (std::string const& i : cmReverseRange(this->ChangeLists)) { this->SetP4Options(p4_describe); p4_describe.push_back("describe"); p4_describe.push_back("-s"); - p4_describe.push_back(i.c_str()); - p4_describe.push_back(nullptr); + p4_describe.push_back(i); DescribeParser outDescribe(this, "p4_describe-out> "); OutputLogger errDescribe(this->Log, "p4_describe-err> "); - this->RunChild(p4_describe.data(), &outDescribe, &errDescribe); + this->RunChild(p4_describe, &outDescribe, &errDescribe); } return true; } bool cmCTestP4::LoadModifications() { - std::vector<char const*> p4_diff; + std::vector<std::string> p4_diff; this->SetP4Options(p4_diff); p4_diff.push_back("diff"); @@ -448,12 +441,11 @@ bool cmCTestP4::LoadModifications() // Ideally we would use -Od but not all clients support it p4_diff.push_back("-dn"); std::string source = this->SourceDirectory + "/..."; - p4_diff.push_back(source.c_str()); - p4_diff.push_back(nullptr); + p4_diff.push_back(source); DiffParser out(this, "p4_diff-out> "); OutputLogger err(this->Log, "p4_diff-err> "); - this->RunChild(p4_diff.data(), &out, &err); + this->RunChild(p4_diff, &out, &err); return true; } @@ -461,17 +453,14 @@ bool cmCTestP4::UpdateCustom(const std::string& custom) { cmList p4_custom_command{ custom, cmList::EmptyElements::Yes }; - std::vector<char const*> p4_custom; - p4_custom.reserve(p4_custom_command.size() + 1); - for (std::string const& i : p4_custom_command) { - p4_custom.push_back(i.c_str()); - } - p4_custom.push_back(nullptr); + std::vector<std::string> p4_custom; + p4_custom.reserve(p4_custom_command.size()); + cm::append(p4_custom, p4_custom_command); OutputLogger custom_out(this->Log, "p4_customsync-out> "); OutputLogger custom_err(this->Log, "p4_customsync-err> "); - return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err); + return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err); } bool cmCTestP4::UpdateImpl() @@ -488,7 +477,7 @@ bool cmCTestP4::UpdateImpl() return false; } - std::vector<char const*> p4_sync; + std::vector<std::string> p4_sync; this->SetP4Options(p4_sync); p4_sync.push_back("sync"); @@ -499,9 +488,7 @@ bool cmCTestP4::UpdateImpl() opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); } std::vector<std::string> args = cmSystemTools::ParseArguments(opts); - for (std::string const& arg : args) { - p4_sync.push_back(arg.c_str()); - } + cm::append(p4_sync, args); std::string source = this->SourceDirectory + "/..."; @@ -515,11 +502,10 @@ bool cmCTestP4::UpdateImpl() source.append("@\"").append(date).append("\""); } - p4_sync.push_back(source.c_str()); - p4_sync.push_back(nullptr); + p4_sync.push_back(source); OutputLogger out(this->Log, "p4_sync-out> "); OutputLogger err(this->Log, "p4_sync-err> "); - return this->RunUpdateCommand(p4_sync.data(), &out, &err); + return this->RunUpdateCommand(p4_sync, &out, &err); } diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h index 1889520..827caa1 100644 --- a/Source/CTest/cmCTestP4.h +++ b/Source/CTest/cmCTestP4.h @@ -39,7 +39,7 @@ private: std::vector<std::string> P4Options; User GetUserData(const std::string& username); - void SetP4Options(std::vector<char const*>& options); + void SetP4Options(std::vector<std::string>& options); std::string GetWorkingRevision(); bool NoteOldRevision() override; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 91a1177..14bc510 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default; void cmCTestSVN::CleanupImpl() { - std::vector<const char*> svn_cleanup; + std::vector<std::string> svn_cleanup; svn_cleanup.push_back("cleanup"); OutputLogger out(this->Log, "cleanup-out> "); OutputLogger err(this->Log, "cleanup-err> "); @@ -88,9 +88,9 @@ static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2) std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo) { // Run "svn info" to get the repository info from the work tree. - std::vector<const char*> svn_info; + std::vector<std::string> svn_info; svn_info.push_back("info"); - svn_info.push_back(svninfo.LocalPath.c_str()); + svn_info.push_back(svninfo.LocalPath); std::string rev; InfoParser out(this, "info-out> ", rev, svninfo); OutputLogger err(this->Log, "info-err> "); @@ -251,26 +251,24 @@ bool cmCTestSVN::UpdateImpl() args.push_back("-r{" + this->GetNightlyTime() + " +0000}"); } - std::vector<char const*> svn_update; + std::vector<std::string> svn_update; svn_update.push_back("update"); - for (std::string const& arg : args) { - svn_update.push_back(arg.c_str()); - } + cm::append(svn_update, args); UpdateParser out(this, "up-out> "); OutputLogger err(this->Log, "up-err> "); return this->RunSVNCommand(svn_update, &out, &err); } -bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, +bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters, OutputParser* out, OutputParser* err) { if (parameters.empty()) { return false; } - std::vector<char const*> args; - args.push_back(this->CommandLineTool.c_str()); + std::vector<std::string> args; + args.push_back(this->CommandLineTool); cm::append(args, parameters); args.push_back("--non-interactive"); @@ -278,16 +276,12 @@ bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, std::vector<std::string> parsedUserOptions = cmSystemTools::ParseArguments(userOptions); - for (std::string const& opt : parsedUserOptions) { - args.push_back(opt.c_str()); - } - - args.push_back(nullptr); + cm::append(args, parsedUserOptions); - if (strcmp(parameters[0], "update") == 0) { - return this->RunUpdateCommand(args.data(), out, err); + if (parameters[0] == "update") { + return this->RunUpdateCommand(args, out, err); } - return this->RunChild(args.data(), out, err); + return this->RunChild(args, out, err); } class cmCTestSVN::LogParser @@ -393,7 +387,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo) } // Run "svn log" to get all global revisions of interest. - std::vector<const char*> svn_log; + std::vector<std::string> svn_log; svn_log.push_back("log"); svn_log.push_back("--xml"); svn_log.push_back("-v"); @@ -472,7 +466,7 @@ private: bool cmCTestSVN::LoadModifications() { // Run "svn status" which reports local modifications. - std::vector<const char*> svn_status; + std::vector<std::string> svn_status; svn_status.push_back("status"); StatusParser out(this, "status-out> "); OutputLogger err(this->Log, "status-err> "); @@ -534,7 +528,7 @@ bool cmCTestSVN::LoadRepositories() this->RootInfo = &(this->Repositories.back()); // Run "svn status" to get the list of external repositories - std::vector<const char*> svn_status; + std::vector<std::string> svn_status; svn_status.push_back("status"); ExternalParser out(this, "external-out> "); OutputLogger err(this->Log, "external-err> "); diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h index 370d176..1485dc0 100644 --- a/Source/CTest/cmCTestSVN.h +++ b/Source/CTest/cmCTestSVN.h @@ -33,7 +33,7 @@ private: bool NoteNewRevision() override; bool UpdateImpl() override; - bool RunSVNCommand(std::vector<char const*> const& parameters, + bool RunSVNCommand(std::vector<std::string> const& parameters, OutputParser* out, OutputParser* err); // Information about an SVN repository (root repository or external) diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 461ad1a..48f8f6d 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -11,8 +11,9 @@ #include <cm/memory> +#include <cm3p/uv.h> + #include "cmsys/Directory.hxx" -#include "cmsys/Process.h" #include "cmCTest.h" #include "cmCTestBuildCommand.h" @@ -40,6 +41,8 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmake.h" @@ -148,66 +151,65 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) // now pass through all the other arguments std::vector<std::string>& 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].c_str()); - } - argv.push_back(nullptr); // Now create process object - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - // cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - // cmsysProcess_SetTimeout(cp, timeout); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(initArgs) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + auto process = builder.Start(); + cm::uv_pipe_ptr outPipe; + outPipe.init(process.GetLoop(), 0); + uv_pipe_open(outPipe, process.OutputStream()); + cm::uv_pipe_ptr errPipe; + errPipe.init(process.GetLoop(), 0); + uv_pipe_open(errPipe, process.ErrorStream()); std::vector<char> out; std::vector<char> err; std::string line; - int pipe = - cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err); - while (pipe != cmsysProcess_Pipe_None) { + auto pipe = + cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, + std::chrono::seconds(100), out, err); + while (pipe != cmSystemTools::WaitForLineResult::None) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Output: " << line << "\n"); - if (pipe == cmsysProcess_Pipe_STDERR) { + if (pipe == cmSystemTools::WaitForLineResult::STDERR) { cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n"); - } else if (pipe == cmsysProcess_Pipe_STDOUT) { + } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n"); } - pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, - err); + pipe = + cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line, + std::chrono::seconds(100), out, err); } // Properly handle output of the build command - cmsysProcess_WaitForExit(cp, nullptr); - int result = cmsysProcess_GetState(cp); + process.Wait(); + auto const& status = process.GetStatus(0); + auto result = status.GetException(); int retVal = 0; bool failed = false; - if (result == cmsysProcess_State_Exited) { - retVal = cmsysProcess_GetExitValue(cp); - } else if (result == cmsysProcess_State_Exception) { - retVal = cmsysProcess_GetExitException(cp); - cmCTestLog(this->CTest, ERROR_MESSAGE, - "\tThere was an exception: " - << cmsysProcess_GetExceptionString(cp) << " " << retVal - << std::endl); - failed = true; - } else if (result == cmsysProcess_State_Expired) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "\tThere was a timeout" << std::endl); - failed = true; - } else if (result == cmsysProcess_State_Error) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "\tError executing ctest: " << cmsysProcess_GetErrorString(cp) - << std::endl); - failed = true; + switch (result.first) { + case cmUVProcessChain::ExceptionCode::None: + retVal = static_cast<int>(status.ExitStatus); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + cmCTestLog(this->CTest, ERROR_MESSAGE, + "\tError executing ctest: " << result.second << std::endl); + failed = true; + break; + default: + retVal = status.TermSignal; + cmCTestLog(this->CTest, ERROR_MESSAGE, + "\tThere was an exception: " << result.second << " " << retVal + << std::endl); + failed = true; } - cmsysProcess_Delete(cp); if (failed) { std::ostringstream message; message << "Error running command: ["; - message << result << "] "; + message << static_cast<int>(result.first) << "] "; for (const char* arg : argv) { if (arg) { message << arg << " "; diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 1934d8c..77af889 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -309,6 +309,9 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( // specify target ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str()); + // follow redirects + ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + // CURLAUTH_BASIC is default, and here we allow additional methods, // including more secure ones ::curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index e4b9239..eb3b4dd 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -1711,8 +1711,7 @@ std::string cmCTestTestHandler::FindExecutable( // now look in the paths we specified above for (unsigned int ai = 0; ai < attempted.size() && fullPath.empty(); ++ai) { // first check without exe extension - if (cmSystemTools::FileExists(attempted[ai]) && - !cmSystemTools::FileIsDirectory(attempted[ai])) { + if (cmSystemTools::FileExists(attempted[ai], true)) { fullPath = cmSystemTools::CollapseFullPath(attempted[ai]); resultingConfig = attemptedConfigs[ai]; } @@ -1721,8 +1720,7 @@ std::string cmCTestTestHandler::FindExecutable( failed.push_back(attempted[ai]); tempPath = cmStrCat(attempted[ai], cmSystemTools::GetExecutableExtension()); - if (cmSystemTools::FileExists(tempPath) && - !cmSystemTools::FileIsDirectory(tempPath)) { + if (cmSystemTools::FileExists(tempPath, true)) { fullPath = cmSystemTools::CollapseFullPath(tempPath); resultingConfig = attemptedConfigs[ai]; } else { diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx index 609ccba..cbbb5a5 100644 --- a/Source/CTest/cmCTestVC.cxx +++ b/Source/CTest/cmCTestVC.cxx @@ -7,10 +7,9 @@ #include <sstream> #include <vector> -#include "cmsys/Process.h" - #include "cmCTest.h" #include "cmSystemTools.h" +#include "cmUVProcessChain.h" #include "cmValue.h" #include "cmXMLWriter.h" @@ -55,18 +54,12 @@ bool cmCTestVC::InitialCheckout(const std::string& command) // Construct the initial checkout command line. std::vector<std::string> args = cmSystemTools::ParseArguments(command); - std::vector<char const*> vc_co; - vc_co.reserve(args.size() + 1); - for (std::string const& arg : args) { - vc_co.push_back(arg.c_str()); - } - vc_co.push_back(nullptr); // Run the initial checkout command and log its output. this->Log << "--- Begin Initial Checkout ---\n"; OutputLogger out(this->Log, "co-out> "); OutputLogger err(this->Log, "co-err> "); - bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str()); + bool result = this->RunChild(args, &out, &err, parent); this->Log << "--- End Initial Checkout ---\n"; if (!result) { cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -75,35 +68,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command) return result; } -bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out, - OutputParser* err, const char* workDir, - Encoding encoding) +bool cmCTestVC::RunChild(const std::vector<std::string>& cmd, + OutputParser* out, OutputParser* err, + std::string workDir, Encoding encoding) { this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n"; - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, cmd); - workDir = workDir ? workDir : this->SourceDirectory.c_str(); - cmsysProcess_SetWorkingDirectory(cp, workDir); - cmCTestVC::RunProcess(cp, out, err, encoding); - int result = cmsysProcess_GetExitValue(cp); - cmsysProcess_Delete(cp); - return result == 0; + cmUVProcessChainBuilder builder; + if (workDir.empty()) { + workDir = this->SourceDirectory; + } + builder.AddCommand(cmd).SetWorkingDirectory(workDir); + auto status = cmCTestVC::RunProcess(builder, out, err, encoding); + return status.front().SpawnResult == 0 && status.front().ExitStatus == 0; } -std::string cmCTestVC::ComputeCommandLine(char const* const* cmd) +std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd) { std::ostringstream line; const char* sep = ""; - for (const char* const* arg = cmd; *arg; ++arg) { - line << sep << "\"" << *arg << "\""; + for (auto const& arg : cmd) { + line << sep << "\"" << arg << "\""; sep = " "; } return line.str(); } -bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, - OutputParser* err, Encoding encoding) +bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd, + OutputParser* out, OutputParser* err, + Encoding encoding) { // Report the command line. this->UpdateCommandLine = this->ComputeCommandLine(cmd); @@ -113,7 +106,7 @@ bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out, } // Run the command. - return this->RunChild(cmd, out, err, nullptr, encoding); + return this->RunChild(cmd, out, err, "", encoding); } std::string cmCTestVC::GetNightlyTime() diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index 7b03d10..dd5456d 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h @@ -6,6 +6,7 @@ #include <iosfwd> #include <string> +#include <vector> #include "cmProcessOutput.h" #include "cmProcessTools.h" @@ -108,15 +109,15 @@ protected: }; /** Convert a list of arguments to a human-readable command line. */ - static std::string ComputeCommandLine(char const* const* cmd); + static std::string ComputeCommandLine(const std::vector<std::string>& cmd); /** Run a command line and send output to given parsers. */ - bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err, - const char* workDir = nullptr, + bool RunChild(const std::vector<std::string>& cmd, OutputParser* out, + OutputParser* err, std::string workDir = {}, Encoding encoding = cmProcessOutput::Auto); /** Run VC update command line and send output to given parsers. */ - bool RunUpdateCommand(char const* const* cmd, OutputParser* out, + bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out, OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 98d5eff..0262746 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -96,7 +96,7 @@ else() set(CMake_HAVE_CXX_FILESYSTEM FALSE) endif() -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l|sparc") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|aarch64)$") cm_check_cxx_feature(atomic_builtin) if(NOT CMake_HAVE_CXX_ATOMIC_BUILTIN) set(cm_check_cxx_feature_LINK_LIBRARIES atomic) diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx index 85b379b..f1d351a 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx @@ -667,6 +667,10 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> +#ifndef _WIN32 +# include <termios.h> +#endif + /*--------------------------------------------------------------------------*/ #define INITIAL 0 diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l index 2befa85..ac9cbaf 100644 --- a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l +++ b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l @@ -26,6 +26,10 @@ Modify cmCTestResourceGroupsLexer.cxx: #include <cstddef> +#ifndef _WIN32 +# include <termios.h> +#endif + /*--------------------------------------------------------------------------*/ %} diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx index 5972202..e2a8f92 100644 --- a/Source/cmBinUtilsLinuxELFLinker.cxx +++ b/Source/cmBinUtilsLinuxELFLinker.cxx @@ -73,6 +73,9 @@ bool cmBinUtilsLinuxELFLinker::Prepare() if (ldConfigTool == "ldconfig") { this->LDConfigTool = cm::make_unique<cmLDConfigLDConfigTool>(this->Archive); + if (!this->LDConfigTool->GetLDConfigPaths(this->LDConfigPaths)) { + return false; + } } else { std::ostringstream e; e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool; @@ -132,12 +135,8 @@ bool cmBinUtilsLinuxELFLinker::ScanDependencies( parentRpaths.end()); } - std::vector<std::string> ldConfigPaths; - if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) { - return false; - } - searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(), - ldConfigPaths.end()); + searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(), + this->LDConfigPaths.end()); for (auto const& dep : needed) { if (!this->Archive->IsPreExcluded(dep)) { diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index da680ca..0efb9a4 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -35,7 +35,6 @@ # include "cmGlobalVisualStudio10Generator.h" # include "cmGlobalVisualStudioVersionedGenerator.h" # include "cmVSSetupHelper.h" -# define HAVE_VS_SETUP_HELPER #endif namespace { @@ -378,9 +377,9 @@ std::map<std::string, std::string> GetOSReleaseVariables( return data; } -cm::optional<std::string> GetValue(cmExecutionStatus& status, - std::string const& key, - std::string const& variable) +cm::optional<std::string> GetDistribValue(cmExecutionStatus& status, + std::string const& key, + std::string const& variable) { const auto prefix = "DISTRIB_"_s; if (!cmHasPrefix(key, prefix)) { @@ -414,9 +413,89 @@ cm::optional<std::string> GetValue(cmExecutionStatus& status, return std::string{}; } -#ifdef HAVE_VS_SETUP_HELPER -cm::optional<std::string> GetValue(cmExecutionStatus& status, - std::string const& key) +#ifdef _WIN32 +std::string FindMSYSTEM_PREFIX(std::vector<std::string> prefixes) +{ + for (std::string const& prefix : prefixes) { + std::string out; + std::string err; + int ret; + // In a modern MSYSTEM environment we expect cygpath to be in PATH. + std::vector<std::string> cygpath_cmd{ "cygpath", "-w", prefix }; + if (cmSystemTools::RunSingleCommand(cygpath_cmd, &out, &err, &ret, nullptr, + cmSystemTools::OUTPUT_NONE)) { + if (ret == 0) { + out = cmTrimWhitespace(out); + cmSystemTools::ConvertToUnixSlashes(out); + if (cmSystemTools::FileIsDirectory(out)) { + return out; + } + } + } else { + // In a legacy MSYSTEM environment (MinGW/MSYS 1.0) there is no + // cygpath but we expect 'sh' to be in PATH. + std::vector<std::string> sh_cmd{ + "sh", "-c", cmStrCat("cd \"", prefix, "\" && cmd //c cd") + }; + if (cmSystemTools::RunSingleCommand(sh_cmd, &out, &err, &ret, nullptr, + cmSystemTools::OUTPUT_NONE)) { + if (ret == 0) { + out = cmTrimWhitespace(out); + cmSystemTools::ConvertToUnixSlashes(out); + if (cmSystemTools::FileIsDirectory(out)) { + return out; + } + } + } + } + } + return {}; +} + +std::string FallbackMSYSTEM_PREFIX(cm::string_view msystem) +{ + // These layouts are used by distributions such as + // * MSYS2: https://www.msys2.org/docs/environments/ + // * MinGW/MSYS 1.0: http://mingw.osdn.io/ + if (msystem == "MSYS"_s) { + static std::string const msystem_msys = FindMSYSTEM_PREFIX({ "/usr" }); + return msystem_msys; + } + if (msystem == "MINGW32"_s) { + static std::string const msystem_mingw32 = + FindMSYSTEM_PREFIX({ "/mingw32", "/mingw" }); + return msystem_mingw32; + } + if (msystem == "MINGW64"_s) { + static std::string const msystem_mingw64 = + FindMSYSTEM_PREFIX({ "/mingw64" }); + return msystem_mingw64; + } + if (msystem == "UCRT64"_s) { + static std::string const msystem_ucrt64 = + FindMSYSTEM_PREFIX({ "/ucrt64" }); + return msystem_ucrt64; + } + if (msystem == "CLANG32"_s) { + static std::string const msystem_clang32 = + FindMSYSTEM_PREFIX({ "/clang32" }); + return msystem_clang32; + } + if (msystem == "CLANG64"_s) { + static std::string const msystem_clang64 = + FindMSYSTEM_PREFIX({ "/clang64" }); + return msystem_clang64; + } + if (msystem == "CLANGARM64"_s) { + static std::string const msystem_clangarm64 = + FindMSYSTEM_PREFIX({ "/clangarm64" }); + return msystem_clangarm64; + } + return {}; +} + +cm::optional<std::string> GetWindowsValue(cmExecutionStatus& status, + std::string const& key) { auto* const gg = status.GetMakefile().GetGlobalGenerator(); for (auto vs : { 15, 16, 17 }) { @@ -447,6 +526,23 @@ cm::optional<std::string> GetValue(cmExecutionStatus& status, return vs10gen->FindMSBuildCommandEarly(&status.GetMakefile()); } + if (key == "MSYSTEM_PREFIX") { + // MSYSTEM_PREFIX is meaningful only under a MSYSTEM environment. + cm::optional<std::string> ms = cmSystemTools::GetEnvVar("MSYSTEM"); + if (!ms || ms->empty()) { + return std::string(); + } + // Prefer the MSYSTEM_PREFIX environment variable. + if (cm::optional<std::string> msp = + cmSystemTools::GetEnvVar("MSYSTEM_PREFIX")) { + cmSystemTools::ConvertToUnixSlashes(*msp); + if (cmSystemTools::FileIsDirectory(*msp)) { + return msp; + } + } + // Fall back to known distribution layouts. + return FallbackMSYSTEM_PREFIX(*ms); + } return {}; } #endif @@ -598,9 +694,9 @@ bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args, auto value = GetValueChained( [&]() { return GetValue(info, key); } - , [&]() { return GetValue(status, key, variable); } -#ifdef HAVE_VS_SETUP_HELPER - , [&]() { return GetValue(status, key); } + , [&]() { return GetDistribValue(status, key, variable); } +#ifdef _WIN32 + , [&]() { return GetWindowsValue(status, key); } #endif ); // clang-format on diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx index e0ff0c6..1b88a0c 100644 --- a/Source/cmCMakePresetsErrors.cxx +++ b/Source/cmCMakePresetsErrors.cxx @@ -302,4 +302,9 @@ void PRESET_MISSING_FIELD(const std::string& presetName, state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"", missingField, "\"")); } + +void SCHEMA_UNSUPPORTED(cmJSONState* state) +{ + state->AddError("File version must be 8 or higher for $schema support"); +} } diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h index 14aaed7..b755c25 100644 --- a/Source/cmCMakePresetsErrors.h +++ b/Source/cmCMakePresetsErrors.h @@ -113,4 +113,6 @@ JsonErrors::ErrorGenerator INVALID_ROOT_OBJECT( void PRESET_MISSING_FIELD(const std::string& presetName, const std::string& missingField, cmJSONState* state); + +void SCHEMA_UNSUPPORTED(cmJSONState* state); } diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h index f133efb..5c76e0e 100644 --- a/Source/cmCMakePresetsGraphInternal.h +++ b/Source/cmCMakePresetsGraphInternal.h @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include <cstddef> #include <memory> #include <string> #include <vector> @@ -179,4 +180,6 @@ bool PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out, bool EnvironmentMapHelper( std::map<std::string, cm::optional<std::string>>& out, const Json::Value* value, cmJSONState* state); + +cmJSONHelper<std::nullptr_t> SchemaHelper(); } diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index 8d63441..0d8ec63 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx @@ -38,7 +38,7 @@ using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander; using cmCMakePresetsGraphInternal::ExpandMacros; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 7; +constexpr int MAX_VERSION = 8; struct CMakeVersion { @@ -294,7 +294,9 @@ auto const RootPresetsHelper = .Bind<std::nullptr_t>("vendor"_s, nullptr, cmCMakePresetsGraphInternal::VendorHelper( cmCMakePresetsErrors::INVALID_ROOT), - false); + false) + .Bind<std::nullptr_t>("$schema"_s, nullptr, + cmCMakePresetsGraphInternal::SchemaHelper(), false); } namespace cmCMakePresetsGraphInternal { @@ -413,6 +415,13 @@ bool EnvironmentMapHelper( return helper(out, value, state); } + +cmJSONHelper<std::nullptr_t> SchemaHelper() +{ + return [](std::nullptr_t&, const Json::Value*, cmJSONState*) -> bool { + return true; + }; +} } bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename, @@ -488,6 +497,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename, return false; } + // Support for $schema added in version 8. + if (v < 8 && root.isMember("$schema")) { + cmCMakePresetsErrors::SCHEMA_UNSUPPORTED(&this->parseState); + return false; + } + RootPresets presets; if ((result = RootPresetsHelper(presets, &root, &parseState)) != true) { return result; diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 6ff6437..b7232f3 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <cctype> #include <chrono> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> @@ -24,13 +25,13 @@ #include <cmext/string_view> #include <cm3p/curl/curl.h> +#include <cm3p/uv.h> #include <cm3p/zlib.h> #include "cmsys/Base64.h" #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/SystemInformation.hxx" #if defined(_WIN32) @@ -64,6 +65,9 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #include "cmVersion.h" #include "cmVersionConfig.h" @@ -1000,8 +1004,7 @@ int cmCTest::ProcessSteps() for (kk = 0; kk < d.GetNumberOfFiles(); kk++) { const char* file = d.GetFile(kk); std::string fullname = notes_dir + "/" + file; - if (cmSystemTools::FileExists(fullname) && - !cmSystemTools::FileIsDirectory(fullname)) { + if (cmSystemTools::FileExists(fullname, true)) { if (!this->Impl->NotesFiles.empty()) { this->Impl->NotesFiles += ";"; } @@ -1074,9 +1077,9 @@ int cmCTest::GetTestModelFromString(const std::string& str) // ###################################################################### // ###################################################################### -int cmCTest::RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, Encoding encoding) +bool cmCTest::RunMakeCommand(const std::string& command, std::string& output, + int* retVal, const char* dir, cmDuration timeout, + std::ostream& ofs, Encoding encoding) { // First generate the command and arguments std::vector<std::string> args = cmSystemTools::ParseArguments(command); @@ -1085,107 +1088,107 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output, return false; } - std::vector<const char*> argv; - argv.reserve(args.size() + 1); - for (std::string const& a : args) { - argv.push_back(a.c_str()); - } - argv.push_back(nullptr); - output.clear(); cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:"); - for (char const* arg : argv) { - if (!arg) { - break; - } + for (auto const& arg : args) { cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\""); } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl); // Now create process object - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); + cmUVProcessChainBuilder builder; + builder.AddCommand(args).SetMergedBuiltinStreams(); + if (dir) { + builder.SetWorkingDirectory(dir); + } + auto chain = builder.Start(); + cm::uv_pipe_ptr outputStream; + outputStream.init(chain.GetLoop(), 0); + uv_pipe_open(outputStream, chain.OutputStream()); // Initialize tick's std::string::size_type tick = 0; std::string::size_type tick_len = 1024; std::string::size_type tick_line_len = 50; - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Each . represents " << tick_len << " bytes of output\n" " " << std::flush); - while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { - processOutput.DecodeText(data, length, strdata); - for (char& cc : strdata) { - if (cc == 0) { - cc = '\n'; + auto outputHandle = cmUVStreamRead( + outputStream, + [this, &processOutput, &output, &tick, &tick_len, &tick_line_len, + &ofs](std::vector<char> data) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata); + for (char& cc : strdata) { + if (cc == 0) { + cc = '\n'; + } } - } - output.append(strdata); - while (output.size() > (tick * tick_len)) { - tick++; - cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush); - if (tick % tick_line_len == 0 && tick > 0) { - cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, - " Size: " << int((double(output.size()) / 1024.0) + 1) - << "K\n " << std::flush); + output.append(strdata); + while (output.size() > (tick * tick_len)) { + tick++; + cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush); + if (tick % tick_line_len == 0 && tick > 0) { + cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, + " Size: " << int((double(output.size()) / 1024.0) + 1) + << "K\n " << std::flush); + } } - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); - } - } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - output.append(strdata); - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (ofs) { - ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); - } - } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (ofs) { + ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + } + }, + [this, &processOutput, &output, &ofs]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata); + if (!strdata.empty()) { + output.append(strdata); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (ofs) { + ofs << cmCTestLogWrite(strdata.c_str(), strdata.size()); + } + } + }); + + bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, " Size of output: " << int(double(output.size()) / 1024.0) << "K" << std::endl); - cmsysProcess_WaitForExit(cp, nullptr); - - int result = cmsysProcess_GetState(cp); - - if (result == cmsysProcess_State_Exited) { - *retVal = cmsysProcess_GetExitValue(cp); - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - "Command exited with the value: " << *retVal << std::endl); - } else if (result == cmsysProcess_State_Exception) { - *retVal = cmsysProcess_GetExitException(cp); - cmCTestLog(this, WARNING, - "There was an exception: " << *retVal << std::endl); - } else if (result == cmsysProcess_State_Expired) { + if (finished) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + *retVal = static_cast<int>(status.ExitStatus); + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + "Command exited with the value: " << *retVal << std::endl); + break; + case cmUVProcessChain::ExceptionCode::Spawn: + output += "\n*** ERROR executing: "; + output += exception.second; + output += "\n***The build process failed."; + cmCTestLog(this, ERROR_MESSAGE, + "There was an error: " << exception.second << std::endl); + break; + default: + *retVal = static_cast<int>(exception.first); + cmCTestLog(this, WARNING, + "There was an exception: " << *retVal << std::endl); + break; + } + } else { cmCTestLog(this, WARNING, "There was a timeout" << std::endl); - } else if (result == cmsysProcess_State_Error) { - output += "\n*** ERROR executing: "; - output += cmsysProcess_GetErrorString(cp); - output += "\n***The build process failed."; - cmCTestLog(this, ERROR_MESSAGE, - "There was an error: " << cmsysProcess_GetErrorString(cp) - << std::endl); } - cmsysProcess_Delete(cp); - - return result; + return true; } // ###################################################################### @@ -1193,9 +1196,10 @@ int cmCTest::RunMakeCommand(const std::string& command, std::string& output, // ###################################################################### // ###################################################################### -int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, - int* retVal, std::ostream* log, cmDuration testTimeOut, - std::vector<std::string>* environment, Encoding encoding) +bool cmCTest::RunTest(const std::vector<std::string>& argv, + std::string* output, int* retVal, std::ostream* log, + cmDuration testTimeOut, + std::vector<std::string>* environment, Encoding encoding) { bool modifyEnv = (environment && !environment->empty()); @@ -1234,19 +1238,16 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, inst.SetStreams(&oss, &oss); std::vector<std::string> args; - for (char const* i : argv) { - if (i) { - // make sure we pass the timeout in for any build and test - // invocations. Since --build-generator is required this is a - // good place to check for it, and to add the arguments in - if (strcmp(i, "--build-generator") == 0 && - timeout != cmCTest::MaxDuration() && - timeout > cmDuration::zero()) { - args.emplace_back("--test-timeout"); - args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout))); - } - args.emplace_back(i); + for (auto const& i : argv) { + // make sure we pass the timeout in for any build and test + // invocations. Since --build-generator is required this is a + // good place to check for it, and to add the arguments in + if (i == "--build-generator" && timeout != cmCTest::MaxDuration() && + timeout > cmDuration::zero()) { + args.emplace_back("--test-timeout"); + args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout))); } + args.emplace_back(i); } if (log) { *log << "* Run internal CTest" << std::endl; @@ -1272,7 +1273,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, << std::endl); } - return cmsysProcess_State_Exited; + return true; } std::vector<char> tempOutput; if (output) { @@ -1285,41 +1286,43 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, cmSystemTools::AppendEnv(*environment); } - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); + cmUVProcessChainBuilder builder; + builder.AddCommand(argv).SetMergedBuiltinStreams(); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); - if (cmSystemTools::GetRunCommandHideConsole()) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - } + auto chain = builder.Start(); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); - - char* data; - int length; cmProcessOutput processOutput(encoding); - std::string strdata; - while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) { - processOutput.DecodeText(data, length, strdata); - if (output) { - cm::append(tempOutput, data, data + length); - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (log) { - log->write(strdata.c_str(), strdata.size()); - } - } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, - cmCTestLogWrite(strdata.c_str(), strdata.size())); - if (log) { - log->write(strdata.c_str(), strdata.size()); - } - } + cm::uv_pipe_ptr outputStream; + outputStream.init(chain.GetLoop(), 0); + uv_pipe_open(outputStream, chain.OutputStream()); + auto outputHandle = cmUVStreamRead( + outputStream, + [this, &processOutput, &output, &tempOutput, + &log](std::vector<char> data) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata); + if (output) { + cm::append(tempOutput, data.data(), data.data() + data.size()); + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (log) { + log->write(strdata.c_str(), strdata.size()); + } + }, + [this, &processOutput, &log]() { + std::string strdata; + processOutput.DecodeText(std::string(), strdata); + if (!strdata.empty()) { + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, + cmCTestLogWrite(strdata.c_str(), strdata.size())); + if (log) { + log->write(strdata.c_str(), strdata.size()); + } + } + }); - cmsysProcess_WaitForExit(cp, nullptr); + bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0)); processOutput.DecodeText(tempOutput, tempOutput); if (output && tempOutput.begin() != tempOutput.end()) { output->append(tempOutput.data(), tempOutput.size()); @@ -1327,33 +1330,41 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "-- Process completed" << std::endl); - int result = cmsysProcess_GetState(cp); + bool result = false; - if (result == cmsysProcess_State_Exited) { - *retVal = cmsysProcess_GetExitValue(cp); - if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { - this->OutputTestErrors(tempOutput); - } - } else if (result == cmsysProcess_State_Exception) { - if (this->Impl->OutputTestOutputOnTestFailure) { - this->OutputTestErrors(tempOutput); - } - *retVal = cmsysProcess_GetExitException(cp); - std::string outerr = cmStrCat("\n*** Exception executing: ", - cmsysProcess_GetExceptionString(cp)); - if (output) { - *output += outerr; - } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); - } else if (result == cmsysProcess_State_Error) { - std::string outerr = - cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp)); - if (output) { - *output += outerr; + if (complete) { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + *retVal = static_cast<int>(status.ExitStatus); + if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { + this->OutputTestErrors(tempOutput); + } + result = true; + break; + case cmUVProcessChain::ExceptionCode::Spawn: { + std::string outerr = + cmStrCat("\n*** ERROR executing: ", exception.second); + if (output) { + *output += outerr; + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); + } break; + default: { + if (this->Impl->OutputTestOutputOnTestFailure) { + this->OutputTestErrors(tempOutput); + } + *retVal = status.TermSignal; + std::string outerr = + cmStrCat("\n*** Exception executing: ", exception.second); + if (output) { + *output += outerr; + } + cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); + } break; } - cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl); } - cmsysProcess_Delete(cp); return result; } @@ -3471,49 +3482,70 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, stdOut->clear(); stdErr->clear(); - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - if (cmSystemTools::GetRunCommandHideConsole()) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + cmUVProcessChainBuilder builder; + builder.AddCommand(args) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + if (dir) { + builder.SetWorkingDirectory(dir); + } + auto chain = builder.Start(); + + cm::uv_timer_ptr timer; + bool timedOut = false; + if (timeout.count()) { + timer.init(chain.GetLoop(), &timedOut); + timer.start( + [](uv_timer_t* t) { + auto* timedOutPtr = static_cast<bool*>(t->data); + *timedOutPtr = true; + }, + static_cast<uint64_t>(timeout.count() * 1000.0), 0); } - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); std::vector<char> tempOutput; + bool outFinished = false; + cm::uv_pipe_ptr outStream; std::vector<char> tempError; - char* data; - int length; + bool errFinished = false; + cm::uv_pipe_ptr errStream; cmProcessOutput processOutput(encoding); - std::string strdata; - int res; - bool done = false; - while (!done) { - res = cmsysProcess_WaitForData(cp, &data, &length, nullptr); - switch (res) { - case cmsysProcess_Pipe_STDOUT: - cm::append(tempOutput, data, data + length); - break; - case cmsysProcess_Pipe_STDERR: - cm::append(tempError, data, data + length); - break; - default: - done = true; - } - if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) && - this->Impl->ExtraVerbose) { - processOutput.DecodeText(data, length, strdata); - cmSystemTools::Stdout(strdata); - } + auto startRead = [this, &chain, &processOutput]( + cm::uv_pipe_ptr& pipe, int stream, + std::vector<char>& temp, + bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> { + pipe.init(chain.GetLoop(), 0); + uv_pipe_open(pipe, stream); + return cmUVStreamRead( + pipe, + [this, &temp, &processOutput](std::vector<char> data) { + cm::append(temp, data); + if (this->Impl->ExtraVerbose) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata); + cmSystemTools::Stdout(strdata); + } + }, + [&finished]() { finished = true; }); + }; + auto outputHandle = + startRead(outStream, chain.OutputStream(), tempOutput, outFinished); + auto errorHandle = + startRead(errStream, chain.ErrorStream(), tempError, errFinished); + while (!timedOut && !(outFinished && errFinished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); } if (this->Impl->ExtraVerbose) { + std::string strdata; processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { cmSystemTools::Stdout(strdata); } } - cmsysProcess_WaitForExit(cp, nullptr); + while (!timedOut && !chain.Finished()) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } if (!tempOutput.empty()) { processOutput.DecodeText(tempOutput, tempOutput); stdOut->append(tempOutput.data(), tempOutput.size()); @@ -3524,32 +3556,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, } bool result = true; - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - } else { - if (cmsysProcess_GetExitValue(cp) != 0) { - result = false; - } - } - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) { - const char* exception_str = cmsysProcess_GetExceptionString(cp); - cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl); - stdErr->append(exception_str, strlen(exception_str)); - result = false; - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) { - const char* error_str = cmsysProcess_GetErrorString(cp); - cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl); - stdErr->append(error_str, strlen(error_str)); - result = false; - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) { + if (timedOut) { const char* error_str = "Process terminated due to timeout\n"; cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl); stdErr->append(error_str, strlen(error_str)); result = false; + } else { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (retVal) { + *retVal = static_cast<int>(status.ExitStatus); + } else { + if (status.ExitStatus != 0) { + result = false; + } + } + break; + default: { + cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl); + stdErr->append(exception.second); + result = false; + } break; + } } - cmsysProcess_Delete(cp); return result; } diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 9a8d5a6..1644d84 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -254,10 +254,10 @@ public: * Run command specialized for make and configure. Returns process status * and retVal is return value or exception. */ - int RunMakeCommand(const std::string& command, std::string& output, - int* retVal, const char* dir, cmDuration timeout, - std::ostream& ofs, - Encoding encoding = cmProcessOutput::Auto); + bool RunMakeCommand(const std::string& command, std::string& output, + int* retVal, const char* dir, cmDuration timeout, + std::ostream& ofs, + Encoding encoding = cmProcessOutput::Auto); /** Return the current tag */ std::string GetCurrentTag(); @@ -303,10 +303,10 @@ public: * environment variables prior to running the test. After running the test, * environment variables are restored to their previous values. */ - int RunTest(std::vector<const char*> args, std::string* output, int* retVal, - std::ostream* logfile, cmDuration testTimeOut, - std::vector<std::string>* environment, - Encoding encoding = cmProcessOutput::Auto); + bool RunTest(const std::vector<std::string>& args, std::string* output, + int* retVal, std::ostream* logfile, cmDuration testTimeOut, + std::vector<std::string>* environment, + Encoding encoding = cmProcessOutput::Auto); /** * Get the handler object diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 2d6ce98..b13576a 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -330,10 +330,7 @@ std::string cmCommonTargetGenerator::GenerateCodeCheckRules( auto evaluatedProp = cmGeneratorExpression::Evaluate( *value, this->GeneratorTarget->GetLocalGenerator(), config, this->GeneratorTarget, nullptr, this->GeneratorTarget, lang); - if (!evaluatedProp.empty()) { - return evaluatedProp; - } - return *value; + return evaluatedProp; }; std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY"); tidy = evaluateProp(tidy_prop); diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index ccd20a0..4cf3042 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -538,11 +538,15 @@ cmComputeLinkInformation::GetSharedLibrariesLinked() const bool cmComputeLinkInformation::Compute() { - // Skip targets that do not link. + // Skip targets that do not link or have link-like information consumers may + // need (namely modules). if (!(this->Target->GetType() == cmStateEnums::EXECUTABLE || this->Target->GetType() == cmStateEnums::SHARED_LIBRARY || this->Target->GetType() == cmStateEnums::MODULE_LIBRARY || - this->Target->GetType() == cmStateEnums::STATIC_LIBRARY)) { + this->Target->GetType() == cmStateEnums::STATIC_LIBRARY || + (this->Target->CanCompileSources() && + (this->Target->HaveCxx20ModuleSources() || + this->Target->HaveFortranSources())))) { return false; } diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 2ec62d9..ee40bd5 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -78,6 +78,7 @@ std::string const kCMAKE_EXECUTABLE_ENABLE_EXPORTS = std::string const kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS = "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS"; std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES"; +std::string const kCMAKE_HIP_PLATFORM = "CMAKE_HIP_PLATFORM"; std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY"; std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS"; std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX"; @@ -731,10 +732,16 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( // The link and compile lines for ABI detection step need to not use // response files so we can extract implicit includes given to // the underlying host compiler - if (testLangs.find("CUDA") != testLangs.end()) { - fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n"); - fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n"); - fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n"); + static std::array<std::string, 2> const noRSP{ { "CUDA", "HIP" } }; + for (std::string const& lang : noRSP) { + if (testLangs.find(lang) != testLangs.end()) { + fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n", + lang.c_str()); + fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n", + lang.c_str()); + fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n", + lang.c_str()); + } } } fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); @@ -1081,6 +1088,7 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode( vars.insert(kCMAKE_EXECUTABLE_ENABLE_EXPORTS); vars.insert(kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS); vars.insert(kCMAKE_HIP_ARCHITECTURES); + vars.insert(kCMAKE_HIP_PLATFORM); vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY); vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS); vars.insert(kCMAKE_ISPC_HEADER_SUFFIX); diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 2c1480a..634b63b 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -174,12 +174,6 @@ cmCustomCommandGenerator::cmCustomCommandGenerator( , EmulatorsWithArguments(cc.GetCommandLines().size()) , ComputeInternalDepfile(std::move(computeInternalDepfile)) { - if (!this->ComputeInternalDepfile) { - this->ComputeInternalDepfile = - [this](const std::string& cfg, const std::string& file) -> std::string { - return this->GetInternalDepfileName(cfg, file); - }; - } cmGeneratorExpression ge(*lg->GetCMakeInstance(), cc.GetBacktrace()); cmGeneratorTarget const* target{ lg->FindGeneratorTargetToUse( @@ -445,7 +439,7 @@ std::string cmCustomCommandGenerator::GetFullDepfile() const } std::string cmCustomCommandGenerator::GetInternalDepfileName( - const std::string& /*config*/, const std::string& depfile) + const std::string& /*config*/, const std::string& depfile) const { cmCryptoHash hash(cmCryptoHash::AlgoSHA256); std::string extension; @@ -469,7 +463,10 @@ std::string cmCustomCommandGenerator::GetInternalDepfile() const return ""; } - return this->ComputeInternalDepfile(this->OutputConfig, depfile); + if (this->ComputeInternalDepfile) { + return this->ComputeInternalDepfile(this->OutputConfig, depfile); + } + return this->GetInternalDepfileName(this->OutputConfig, depfile); } cm::optional<std::string> cmCustomCommandGenerator::GetComment() const diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 4453654..43bdd10 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -20,7 +20,8 @@ class cmLocalGenerator; class cmCustomCommandGenerator { - std::string GetInternalDepfileName(const std::string&, const std::string&); + std::string GetInternalDepfileName(const std::string&, + const std::string&) const; cmCustomCommand const* CC; std::string OutputConfig; diff --git a/Source/cmDebuggerWindowsPipeConnection.cxx b/Source/cmDebuggerWindowsPipeConnection.cxx index 1c6a2a7..5f6f848 100644 --- a/Source/cmDebuggerWindowsPipeConnection.cxx +++ b/Source/cmDebuggerWindowsPipeConnection.cxx @@ -221,13 +221,28 @@ void cmDebuggerPipeClient_WIN32::WaitForConnection() NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hPipe == INVALID_HANDLE_VALUE) { auto err = GetLastError(); - throw std::runtime_error("CreateFile failed with " + err); + throw std::runtime_error(std::string("CreateFile failed for pipe ") + + GetErrorMessage(err)); } pipes = std::make_unique<DuplexPipe_WIN32>(hPipe); } } +std::string cmDebuggerPipeClient_WIN32::GetErrorMessage(DWORD errorCode) +{ + LPSTR message = nullptr; + DWORD size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message, 0, nullptr); + std::string errorMessage = + this->PipeName + ": " + std::string(message, size); + LocalFree(message); + return errorMessage; +} + bool cmDebuggerPipeClient_WIN32::isOpen() { return pipes != nullptr; diff --git a/Source/cmDebuggerWindowsPipeConnection.h b/Source/cmDebuggerWindowsPipeConnection.h index 88ed1de..6a57435 100644 --- a/Source/cmDebuggerWindowsPipeConnection.h +++ b/Source/cmDebuggerWindowsPipeConnection.h @@ -90,6 +90,8 @@ public: bool write(void const* buffer, size_t n) override; private: + std::string GetErrorMessage(DWORD errorCode); + std::string const PipeName; std::unique_ptr<DuplexPipe_WIN32> pipes; }; diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx index dfab975..75f88b2 100644 --- a/Source/cmDyndepCollation.cxx +++ b/Source/cmDyndepCollation.cxx @@ -122,10 +122,11 @@ Json::Value CollationInformationCxxModules( auto lookup = sf_map.find(file); if (lookup == sf_map.end()) { gt->Makefile->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + MessageType::FATAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file\n ", file, - R"(" which is not in any of its "FILE_SET BASE_DIRS".)")); + "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not " + "scheduled for compilation.")); continue; } diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 3b98219..9b5f2b4 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -2,8 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExecuteProcessCommand.h" -#include <algorithm> #include <cctype> /* isspace */ +#include <cstdint> #include <cstdio> #include <iostream> #include <map> @@ -16,7 +16,9 @@ #include <cmext/algorithm> #include <cmext/string_view> -#include "cmsys/Process.h" +#include <cm3p/uv.h> + +#include "cm_fileno.hxx" #include "cmArgumentParser.h" #include "cmExecutionStatus.h" @@ -26,6 +28,9 @@ #include "cmProcessOutput.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" namespace { bool cmExecuteProcessCommandIsWhitespace(char c) @@ -36,7 +41,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c) void cmExecuteProcessCommandFixText(std::vector<char>& output, bool strip_trailing_whitespace); void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - int length); + std::size_t length); } // cmExecuteProcessCommand @@ -143,57 +148,61 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Create a process instance. - std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( - cmsysProcess_New(), cmsysProcess_Delete); - cmsysProcess* cp = cp_ptr.get(); + cmUVProcessChainBuilder builder; // Set the command sequence. for (std::vector<std::string> const& cmd : arguments.Commands) { - std::vector<const char*> argv(cmd.size() + 1); - std::transform(cmd.begin(), cmd.end(), argv.begin(), - [](std::string const& s) { return s.c_str(); }); - argv.back() = nullptr; - cmsysProcess_AddCommand(cp, argv.data()); + builder.AddCommand(cmd); } // Set the process working directory. if (!arguments.WorkingDirectory.empty()) { - cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str()); + builder.SetWorkingDirectory(arguments.WorkingDirectory); } - // Always hide the process window. - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); - // Check the output variables. - bool merge_output = false; + std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose); if (!arguments.InputFile.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, - arguments.InputFile.c_str()); + inputFile.reset(cmsys::SystemTools::Fopen(arguments.InputFile, "rb")); + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, + cm_fileno(inputFile.get())); + } else { + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, + cm_fileno(stdin)); } + + std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose); if (!arguments.OutputFile.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT, - arguments.OutputFile.c_str()); + outputFile.reset(cmsys::SystemTools::Fopen(arguments.OutputFile, "wb")); + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(outputFile.get())); + } else { + if (arguments.OutputVariable == arguments.ErrorVariable && + !arguments.ErrorVariable.empty()) { + builder.SetMergedBuiltinStreams(); + } else { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + } } + + std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose); if (!arguments.ErrorFile.empty()) { if (arguments.ErrorFile == arguments.OutputFile) { - merge_output = true; + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(outputFile.get())); } else { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, - arguments.ErrorFile.c_str()); + errorFile.reset(cmsys::SystemTools::Fopen(arguments.ErrorFile, "wb")); + builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(errorFile.get())); } - } - if (!arguments.OutputVariable.empty() && - arguments.OutputVariable == arguments.ErrorVariable) { - merge_output = true; - } - if (merge_output) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); + } else if (arguments.ErrorVariable.empty() || + (!arguments.ErrorVariable.empty() && + arguments.OutputVariable != arguments.ErrorVariable)) { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); } // Set the timeout if any. - if (timeout >= 0) { - cmsysProcess_SetTimeout(cp, timeout); - } + int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0); bool echo_stdout = false; bool echo_stderr = false; @@ -241,36 +250,86 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } // Start the process. - cmsysProcess_Execute(cp); + auto chain = builder.Start(); + + bool timedOut = false; + cm::uv_timer_ptr timer; + + if (timeoutMillis >= 0) { + timer.init(chain.GetLoop(), &timedOut); + timer.start( + [](uv_timer_t* handle) { + auto* timeoutPtr = static_cast<bool*>(handle->data); + *timeoutPtr = true; + }, + timeoutMillis, 0); + } // Read the process output. - std::vector<char> tempOutput; - std::vector<char> tempError; - int length; - char* data; - int p; + struct ReadData + { + bool Finished = false; + std::vector<char> Output; + cm::uv_pipe_ptr Stream; + }; + ReadData outputData; + ReadData errorData; cmProcessOutput processOutput( cmProcessOutput::FindEncoding(arguments.Encoding)); std::string strdata; - while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - // Put the output in the right place. - if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) { - if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) { - processOutput.DecodeText(data, length, strdata, 1); - cmSystemTools::Stdout(strdata); - } - if (!arguments.OutputVariable.empty()) { - cmExecuteProcessCommandAppend(tempOutput, data, length); - } - } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) { - if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) { - processOutput.DecodeText(data, length, strdata, 2); - cmSystemTools::Stderr(strdata); - } - if (!arguments.ErrorVariable.empty()) { - cmExecuteProcessCommandAppend(tempError, data, length); - } - } + + std::unique_ptr<cmUVStreamReadHandle> outputHandle; + if (chain.OutputStream() >= 0) { + outputData.Stream.init(chain.GetLoop(), 0); + uv_pipe_open(outputData.Stream, chain.OutputStream()); + outputHandle = cmUVStreamRead( + outputData.Stream, + [&arguments, &processOutput, &outputData, + &strdata](std::vector<char> data) { + if (!arguments.OutputQuiet) { + if (arguments.OutputVariable.empty() || + arguments.EchoOutputVariable) { + processOutput.DecodeText(data.data(), data.size(), strdata, 1); + cmSystemTools::Stdout(strdata); + } + if (!arguments.OutputVariable.empty()) { + cmExecuteProcessCommandAppend(outputData.Output, data.data(), + data.size()); + } + } + }, + [&outputData]() { outputData.Finished = true; }); + } else { + outputData.Finished = true; + } + std::unique_ptr<cmUVStreamReadHandle> errorHandle; + if (chain.ErrorStream() >= 0 && + chain.ErrorStream() != chain.OutputStream()) { + errorData.Stream.init(chain.GetLoop(), 0); + uv_pipe_open(errorData.Stream, chain.ErrorStream()); + errorHandle = cmUVStreamRead( + errorData.Stream, + [&arguments, &processOutput, &errorData, + &strdata](std::vector<char> data) { + if (!arguments.ErrorQuiet) { + if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) { + processOutput.DecodeText(data.data(), data.size(), strdata, 2); + cmSystemTools::Stderr(strdata); + } + if (!arguments.ErrorVariable.empty()) { + cmExecuteProcessCommandAppend(errorData.Output, data.data(), + data.size()); + } + } + }, + [&errorData]() { errorData.Finished = true; }); + } else { + errorData.Finished = true; + } + + while (chain.Valid() && !timedOut && + !(chain.Finished() && outputData.Finished && errorData.Finished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); } if (!arguments.OutputQuiet && (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) { @@ -287,151 +346,102 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } - // All output has been read. Wait for the process to exit. - cmsysProcess_WaitForExit(cp, nullptr); - processOutput.DecodeText(tempOutput, tempOutput); - processOutput.DecodeText(tempError, tempError); + // All output has been read. + processOutput.DecodeText(outputData.Output, outputData.Output); + processOutput.DecodeText(errorData.Output, errorData.Output); // Fix the text in the output strings. - cmExecuteProcessCommandFixText(tempOutput, + cmExecuteProcessCommandFixText(outputData.Output, arguments.OutputStripTrailingWhitespace); - cmExecuteProcessCommandFixText(tempError, + cmExecuteProcessCommandFixText(errorData.Output, arguments.ErrorStripTrailingWhitespace); // Store the output obtained. - if (!arguments.OutputVariable.empty() && !tempOutput.empty()) { + if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) { status.GetMakefile().AddDefinition(arguments.OutputVariable, - tempOutput.data()); + outputData.Output.data()); } - if (!merge_output && !arguments.ErrorVariable.empty() && - !tempError.empty()) { + if (arguments.ErrorVariable != arguments.OutputVariable && + !arguments.ErrorVariable.empty() && !errorData.Output.empty()) { status.GetMakefile().AddDefinition(arguments.ErrorVariable, - tempError.data()); + errorData.Output.data()); } // Store the result of running the process. if (!arguments.ResultVariable.empty()) { - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - int v = cmsysProcess_GetExitValue(cp); - char buf[16]; - snprintf(buf, sizeof(buf), "%d", v); - status.GetMakefile().AddDefinition(arguments.ResultVariable, buf); - } break; - case cmsysProcess_State_Exception: + if (timedOut) { + status.GetMakefile().AddDefinition(arguments.ResultVariable, + "Process terminated due to timeout"); + } else { + auto const* lastStatus = chain.GetStatus().back(); + auto exception = lastStatus->GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { status.GetMakefile().AddDefinition( - arguments.ResultVariable, cmsysProcess_GetExceptionString(cp)); - break; - case cmsysProcess_State_Error: + arguments.ResultVariable, + std::to_string(static_cast<int>(lastStatus->ExitStatus))); + } else { status.GetMakefile().AddDefinition(arguments.ResultVariable, - cmsysProcess_GetErrorString(cp)); - break; - case cmsysProcess_State_Expired: - status.GetMakefile().AddDefinition( - arguments.ResultVariable, "Process terminated due to timeout"); - break; + exception.second); + } } } // Store the result of running the processes. if (!arguments.ResultsVariable.empty()) { - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - std::vector<std::string> res; - for (size_t i = 0; i < arguments.Commands.size(); ++i) { - switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) { - case kwsysProcess_StateByIndex_Exited: { - int exitCode = - cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i)); - char buf[16]; - snprintf(buf, sizeof(buf), "%d", exitCode); - res.emplace_back(buf); - } break; - case kwsysProcess_StateByIndex_Exception: - res.emplace_back(cmsysProcess_GetExceptionStringByIndex( - cp, static_cast<int>(i))); - break; - case kwsysProcess_StateByIndex_Error: - default: - res.emplace_back("Error getting the child return code"); - break; - } + if (timedOut) { + status.GetMakefile().AddDefinition(arguments.ResultsVariable, + "Process terminated due to timeout"); + } else { + std::vector<std::string> res; + for (auto const* processStatus : chain.GetStatus()) { + auto exception = processStatus->GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { + res.emplace_back( + std::to_string(static_cast<int>(processStatus->ExitStatus))); + } else { + res.emplace_back(exception.second); } - status.GetMakefile().AddDefinition(arguments.ResultsVariable, - cmList::to_string(res)); - } break; - case cmsysProcess_State_Exception: - status.GetMakefile().AddDefinition( - arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp)); - break; - case cmsysProcess_State_Error: - status.GetMakefile().AddDefinition(arguments.ResultsVariable, - cmsysProcess_GetErrorString(cp)); - break; - case cmsysProcess_State_Expired: - status.GetMakefile().AddDefinition( - arguments.ResultsVariable, "Process terminated due to timeout"); - break; + } + status.GetMakefile().AddDefinition(arguments.ResultsVariable, + cmList::to_string(res)); } } - auto queryProcessStatusByIndex = [&cp](int index) -> std::string { - std::string processStatus; - switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) { - case kwsysProcess_StateByIndex_Exited: { - int exitCode = cmsysProcess_GetExitValueByIndex(cp, index); - if (exitCode) { - processStatus = "Child return code: " + std::to_string(exitCode); - } - } break; - case kwsysProcess_StateByIndex_Exception: { - processStatus = cmStrCat( - "Abnormal exit with child return code: ", - cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index))); - break; + auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string { + auto const& processStatus = chain.GetStatus(index); + auto exception = processStatus.GetException(); + if (exception.first == cmUVProcessChain::ExceptionCode::None) { + if (processStatus.ExitStatus) { + return cmStrCat("Child return code: ", processStatus.ExitStatus); } - case kwsysProcess_StateByIndex_Error: - default: - processStatus = "Error getting the child return code"; - break; + return ""; } - return processStatus; + return cmStrCat("Abnormal exit with child return code: ", + exception.second); }; if (arguments.CommandErrorIsFatal == "ANY"_s) { bool ret = true; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - std::map<int, std::string> failureIndices; - for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) { - std::string processStatus = queryProcessStatusByIndex(i); - if (!processStatus.empty()) { - failureIndices[i] = processStatus; - } - if (!failureIndices.empty()) { - std::ostringstream oss; - oss << "failed command indexes:\n"; - for (auto const& e : failureIndices) { - oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; - } - status.SetError(oss.str()); - ret = false; - } + if (timedOut) { + status.SetError("Process terminated due to timeout"); + ret = false; + } else { + std::map<std::size_t, std::string> failureIndices; + auto statuses = chain.GetStatus(); + for (std::size_t i = 0; i < statuses.size(); ++i) { + std::string processStatus = queryProcessStatusByIndex(i); + if (!processStatus.empty()) { + failureIndices[i] = processStatus; } - } break; - case cmsysProcess_State_Exception: - status.SetError( - cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp))); - ret = false; - break; - case cmsysProcess_State_Error: - status.SetError(cmStrCat("error getting child return code: ", - cmsysProcess_GetErrorString(cp))); - ret = false; - break; - case cmsysProcess_State_Expired: - status.SetError("Process terminated due to timeout"); + } + if (!failureIndices.empty()) { + std::ostringstream oss; + oss << "failed command indexes:\n"; + for (auto const& e : failureIndices) { + oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; + } + status.SetError(oss.str()); ret = false; - break; + } } if (!ret) { @@ -442,29 +452,23 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, if (arguments.CommandErrorIsFatal == "LAST"_s) { bool ret = true; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { + if (timedOut) { + status.SetError("Process terminated due to timeout"); + ret = false; + } else { + auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1); + auto exception = lastStatus.GetException(); + if (exception.first != cmUVProcessChain::ExceptionCode::None) { + status.SetError(cmStrCat("Abnormal exit: ", exception.second)); + ret = false; + } else { int lastIndex = static_cast<int>(arguments.Commands.size() - 1); const std::string processStatus = queryProcessStatusByIndex(lastIndex); if (!processStatus.empty()) { status.SetError("last command failed"); ret = false; } - } break; - case cmsysProcess_State_Exception: - status.SetError( - cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp))); - ret = false; - break; - case cmsysProcess_State_Error: - status.SetError(cmStrCat("Error getting child return code: ", - cmsysProcess_GetErrorString(cp))); - ret = false; - break; - case cmsysProcess_State_Expired: - status.SetError("Process terminated due to timeout"); - ret = false; - break; + } } if (!ret) { cmSystemTools::SetFatalErrorOccurred(); @@ -507,7 +511,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output, } void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data, - int length) + std::size_t length) { #if defined(__APPLE__) // HACK on Apple to work around bug with inserting at the diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 5a12297..dae061b 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -1256,6 +1256,18 @@ void cmExportFileGenerator::GenerateImportedFileChecksCode( os << ")\n\n"; } +enum class PropertyType +{ + Strings, + Paths, +}; + +struct ModulePropertyTable +{ + cm::static_string_view Name; + PropertyType Type; +}; + bool cmExportFileGenerator::PopulateCxxModuleExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, cmGeneratorExpression::PreprocessContext ctx, std::string& errorMessage) @@ -1279,14 +1291,14 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } } - const cm::static_string_view exportedModuleProperties[] = { - "INCLUDE_DIRECTORIES"_s, - "COMPILE_DEFINITIONS"_s, - "COMPILE_OPTIONS"_s, - "COMPILE_FEATURES"_s, + const ModulePropertyTable exportedModuleProperties[] = { + { "INCLUDE_DIRECTORIES"_s, PropertyType::Paths }, + { "COMPILE_DEFINITIONS"_s, PropertyType::Strings }, + { "COMPILE_OPTIONS"_s, PropertyType::Strings }, + { "COMPILE_FEATURES"_s, PropertyType::Strings }, }; - for (auto const& propName : exportedModuleProperties) { - auto const propNameStr = std::string(propName); + for (auto const& propEntry : exportedModuleProperties) { + auto const propNameStr = std::string(propEntry.Name); cmValue prop = gte->Target->GetComputedProperty( propNameStr, *gte->Target->GetMakefile()); if (!prop) { @@ -1294,9 +1306,14 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } if (prop) { auto const exportedPropName = - cmStrCat("IMPORTED_CXX_MODULES_", propName); + cmStrCat("IMPORTED_CXX_MODULES_", propEntry.Name); properties[exportedPropName] = cmGeneratorExpression::Preprocess(*prop, ctx); + if (ctx == cmGeneratorExpression::InstallInterface && + propEntry.Type == PropertyType::Paths) { + this->ReplaceInstallPrefix(properties[exportedPropName]); + prefixItems(properties[exportedPropName]); + } } } diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 49a0e89..93bed9a 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -3020,8 +3020,7 @@ bool HandleCreateLinkCommand(std::vector<std::string> const& args, } // Check if the new file already exists and remove it. - if ((cmSystemTools::FileExists(newFileName) || - cmSystemTools::FileIsSymlink(newFileName)) && + if (cmSystemTools::PathExists(newFileName) && !cmSystemTools::RemoveFile(newFileName)) { std::ostringstream e; e << "Failed to create link '" << newFileName diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 9f78418..8840cdc 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -329,9 +329,6 @@ void cmFindBase::FillSystemEnvironmentPath() // Add LIB or INCLUDE if (!this->EnvironmentPath.empty()) { paths.AddEnvPath(this->EnvironmentPath); -#if defined(_WIN32) || defined(__CYGWIN__) - paths.AddEnvPrefixPath("PATH", true); -#endif } // Add PATH paths.AddEnvPath("PATH"); diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 1919b01..782d6c8 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -23,6 +23,7 @@ class cmGeneratorTarget; SELECT(F, EvaluatingSystemIncludeDirectories, SYSTEM_INCLUDE_DIRECTORIES) \ SELECT(F, EvaluatingCompileDefinitions, COMPILE_DEFINITIONS) \ SELECT(F, EvaluatingCompileOptions, COMPILE_OPTIONS) \ + SELECT(F, EvaluatingAutoMocMacroNames, AUTOMOC_MACRO_NAMES) \ SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \ SELECT(F, EvaluatingSources, SOURCES) \ SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \ diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index b239408..50334ef 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -153,10 +153,12 @@ std::string GeneratorExpressionContent::EvaluateParameters( return std::string(); } std::string parameter; - for (const auto& pExprEval : *pit) { - parameter += pExprEval->Evaluate(context, dagChecker); - if (context->HadError) { - return std::string(); + if (node->ShouldEvaluateNextParameter(parameters, parameter)) { + for (const auto& pExprEval : *pit) { + parameter += pExprEval->Evaluate(context, dagChecker); + if (context->HadError) { + return std::string(); + } } } parameters.push_back(std::move(parameter)); diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 745a749..57beb72 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -128,6 +128,16 @@ struct BooleanOpNode : public cmGeneratorExpressionNode int NumExpectedParameters() const override { return OneOrMoreParameters; } + bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters, + std::string& def_value) const override + { + if (!parameters.empty() && parameters[0] == failureVal) { + def_value = failureVal; + return false; + } + return true; + } + std::string Evaluate(const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, @@ -195,6 +205,13 @@ static const struct IfNode : public cmGeneratorExpressionNode int NumExpectedParameters() const override { return 3; } + bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters, + std::string&) const override + { + return (parameters.empty() || + parameters[0] != cmStrCat(parameters.size() - 1, "")); + } + std::string Evaluate(const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h index f068b02..7a76289 100644 --- a/Source/cmGeneratorExpressionNode.h +++ b/Source/cmGeneratorExpressionNode.h @@ -33,6 +33,12 @@ struct cmGeneratorExpressionNode virtual int NumExpectedParameters() const { return 1; } + virtual bool ShouldEvaluateNextParameter(const std::vector<std::string>&, + std::string&) const + { + return true; + } + virtual std::string Evaluate( const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 70f51b0..04d7bb1 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -797,10 +797,10 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, *dirs, lg, config, headTarget, dagChecker, depTgt, language)); } - if (depTgt->Target->IsFrameworkOnApple()) { + if (depTgt->Target->IsFrameworkOnApple() || + depTgt->IsImportedFrameworkFolderOnApple(config)) { if (auto fwDescriptor = depTgt->GetGlobalGenerator()->SplitFrameworkPath( - depTgt->GetLocation(config), - cmGlobalGenerator::FrameworkFormat::Strict)) { + depTgt->GetLocation(config))) { result.push_back(fwDescriptor->Directory); result.push_back(fwDescriptor->GetFrameworkPath()); } @@ -1292,13 +1292,12 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory( const std::string& dir, const std::string& config, const std::string& language) const { - assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); std::string config_upper; if (!config.empty()) { config_upper = cmSystemTools::UpperCase(config); } - std::string key = cmStrCat(config_upper, "/", language); + std::string key = cmStrCat(config_upper, '/', language); auto iter = this->SystemIncludesCache.find(key); if (iter == this->SystemIncludesCache.end()) { @@ -3387,12 +3386,12 @@ void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags, } void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, - const std::string& config, + std::string const& config, std::string& flags) const { - std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES"); + std::string arch = this->GetSafeProperty("CUDA_ARCHITECTURES"); - if (property.empty()) { + if (arch.empty()) { switch (this->GetPolicyStatusCMP0104()) { case cmPolicies::WARN: if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) { @@ -3414,48 +3413,60 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, } // If CUDA_ARCHITECTURES is false we don't add any architectures. - if (cmIsOff(property)) { + if (cmIsOff(arch)) { return; } - std::string const& compiler = - this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); - const bool ipoEnabled = this->IsIPOEnabled("CUDA", config); + return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "CUDA", + std::move(arch), flags); +} + +void cmGeneratorTarget::AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink, + std::string const& config, + std::string const& lang, + std::string arch, + std::string& flags) const +{ + std::string const& compiler = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_ID")); + const bool ipoEnabled = this->IsIPOEnabled(lang, config); // Check for special modes: `all`, `all-major`. - if (property == "all" || property == "all-major") { + if (arch == "all" || arch == "all-major") { if (compiler == "NVIDIA" && - cmSystemTools::VersionCompare( - cmSystemTools::OP_GREATER_EQUAL, - this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"), - "11.5")) { - flags = cmStrCat(flags, " -arch=", property); + cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, + this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", lang, "_COMPILER_VERSION")), + "11.5")) { + flags = cmStrCat(flags, " -arch=", arch); return; } - if (property == "all") { - property = - *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL"); - } else if (property == "all-major") { - property = - *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR"); + if (arch == "all") { + arch = *this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL")); + } else if (arch == "all-major") { + arch = *this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL_MAJOR")); } - } else if (property == "native") { - cmValue native = - this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_NATIVE"); + } else if (arch == "native") { + cmValue native = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_ARCHITECTURES_NATIVE")); if (native.IsEmpty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - "CUDA_ARCHITECTURES is set to \"native\", but no GPU was detected."); + cmStrCat(lang, + "_ARCHITECTURES is set to \"native\", but no NVIDIA GPU was " + "detected.")); } if (compiler == "NVIDIA" && - cmSystemTools::VersionCompare( - cmSystemTools::OP_GREATER_EQUAL, - this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"), - "11.6")) { - flags = cmStrCat(flags, " -arch=", property); + cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, + this->Makefile->GetDefinition(cmStrCat( + "CMAKE_", lang, "_COMPILER_VERSION")), + "11.6")) { + flags = cmStrCat(flags, " -arch=", arch); return; } - property = *native; + arch = *native; } struct CudaArchitecture @@ -3467,7 +3478,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, std::vector<CudaArchitecture> architectures; { - cmList options(property); + cmList options(arch); for (auto& option : options) { CudaArchitecture architecture; @@ -3500,8 +3511,8 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, if (compiler == "NVIDIA") { if (ipoEnabled && compileOrLink == cmBuildStep::Link) { - if (cmValue cudaIPOFlags = - this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) { + if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_IPO"))) { flags += *cudaIPOFlags; } } @@ -3549,10 +3560,10 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const { - const std::string& property = this->GetSafeProperty("ISPC_INSTRUCTION_SETS"); + const std::string& arch = this->GetSafeProperty("ISPC_INSTRUCTION_SETS"); // If ISPC_TARGET is false we don't add any architectures. - if (cmIsOff(property)) { + if (cmIsOff(arch)) { return; } @@ -3560,29 +3571,36 @@ void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID"); if (compiler == "Intel") { - cmList targets(property); + cmList targets(arch); if (!targets.empty()) { flags += cmStrCat(" --target=", cmWrap("", targets, "", ",")); } } } -void cmGeneratorTarget::AddHIPArchitectureFlags(std::string& flags) const +void cmGeneratorTarget::AddHIPArchitectureFlags(cmBuildStep compileOrLink, + std::string const& config, + std::string& flags) const { - const std::string& property = this->GetSafeProperty("HIP_ARCHITECTURES"); + std::string arch = this->GetSafeProperty("HIP_ARCHITECTURES"); - if (property.empty()) { + if (arch.empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "HIP_ARCHITECTURES is empty for target \"" + this->GetName() + "\"."); } // If HIP_ARCHITECTURES is false we don't add any architectures. - if (cmIsOff(property)) { + if (cmIsOff(arch)) { return; } - cmList options(property); + if (this->Makefile->GetSafeDefinition("CMAKE_HIP_PLATFORM") == "nvidia") { + return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "HIP", + std::move(arch), flags); + } + + cmList options(arch); for (std::string& option : options) { flags += " --offload-arch=" + option; @@ -4268,9 +4286,6 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom); } - filename = cmStrCat( - generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), "/"); - const std::map<std::string, std::string> languageToExtension = { { "C", ".h" }, { "CXX", ".hxx" }, @@ -4278,8 +4293,7 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, { "OBJCXX", ".objcxx.hxx" } }; - filename = - cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(), ".dir"); + filename = generatorTarget->GetSupportDirectory(); if (this->GetGlobalGenerator()->IsMultiConfig()) { filename = cmStrCat(filename, "/", config); @@ -4372,9 +4386,7 @@ std::string cmGeneratorTarget::GetPchSource(const std::string& config, this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom); } - filename = - cmStrCat(generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), - "/CMakeFiles/", generatorTarget->GetName(), ".dir/cmake_pch"); + filename = cmStrCat(generatorTarget->GetSupportDirectory(), "/cmake_pch"); // For GCC the source extension will be transformed into .h[xx].gch if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) { @@ -9042,6 +9054,15 @@ bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const }); } +bool cmGeneratorTarget::HaveFortranSources() const +{ + auto sources = cmGeneratorTarget::GetAllConfigSources(); + return std::any_of(sources.begin(), sources.end(), + [](AllConfigSource const& sf) -> bool { + return sf.Source->GetLanguage() == "Fortran"_s; + }); +} + bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const { auto const& fs_names = this->Target->GetAllFileSetNames(); @@ -9100,25 +9121,34 @@ void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", this->GetName(), - "\" target has C++ module sources but the \"CXX\" language " - "has not been enabled")); + cmStrCat("The target named \"", this->GetName(), + "\" has C++ sources that export modules but the \"CXX\" " + "language has not been enabled")); break; case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag: this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", this->GetName(), - "\" target has C++ module sources but its experimental " - "support has not been requested")); + cmStrCat("The target named \"", this->GetName(), + "\" has C++ sources that export modules but its " + "experimental support has not been requested")); break; - case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: + case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: { + cmStandardLevelResolver standardResolver(this->Makefile); + auto effStandard = + standardResolver.GetEffectiveStandard(this, "CXX", config); + if (effStandard.empty()) { + effStandard = "; no C++ standard found"; + } else { + effStandard = cmStrCat("; found \"cxx_std_", effStandard, '"'); + } this->Makefile->IssueMessage( MessageType::FATAL_ERROR, cmStrCat( - "The \"", this->GetName(), - "\" target has C++ module sources but is not using at least " - "\"cxx_std_20\"")); - break; + "The target named \"", this->GetName(), + "\" has C++ sources that export modules but does not include " + "\"cxx_std_20\" (or newer) among its `target_compile_features`", + effStandard)); + } break; case cmGeneratorTarget::Cxx20SupportLevel::Supported: // All is well. break; diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index f347133..751f907 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -496,11 +496,17 @@ public: cmSourceFile const& sf) const; void AddCUDAArchitectureFlags(cmBuildStep compileOrLink, - const std::string& config, + std::string const& config, std::string& flags) const; + void AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink, + std::string const& config, + std::string const& lang, std::string arch, + std::string& flags) const; void AddCUDAToolkitFlags(std::string& flags) const; - void AddHIPArchitectureFlags(std::string& flags) const; + void AddHIPArchitectureFlags(cmBuildStep compileOrLink, + std::string const& config, + std::string& flags) const; void AddISPCTargetFlags(std::string& flags) const; @@ -1262,6 +1268,7 @@ public: cmGeneratorTarget const* t2) const; }; + bool HaveFortranSources() const; bool HaveFortranSources(std::string const& config) const; // C++20 module support queries. diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index d5099ee..933e754 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2690,13 +2690,9 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path, return cm::nullopt; } -bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, - std::string const& reason) const +static bool RaiseCMP0037Message(cmake* cm, cmTarget* tgt, + std::string const& reason) { - cmTarget* tgt = this->FindTarget(targetName); - if (!tgt) { - return true; - } MessageType messageType = MessageType::AUTHOR_WARNING; std::ostringstream e; bool issueMessage = false; @@ -2715,13 +2711,12 @@ bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, break; } if (issueMessage) { - e << "The target name \"" << targetName << "\" is reserved " << reason + e << "The target name \"" << tgt->GetName() << "\" is reserved " << reason << "."; if (messageType == MessageType::AUTHOR_WARNING) { e << " It may result in undefined behavior."; } - this->GetCMakeInstance()->IssueMessage(messageType, e.str(), - tgt->GetBacktrace()); + cm->IssueMessage(messageType, e.str(), tgt->GetBacktrace()); if (messageType == MessageType::FATAL_ERROR) { return false; } @@ -2729,6 +2724,16 @@ bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, return true; } +bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, + std::string const& reason) const +{ + cmTarget* tgt = this->FindTarget(targetName); + if (!tgt) { + return true; + } + return RaiseCMP0037Message(this->GetCMakeInstance(), tgt, reason); +} + void cmGlobalGenerator::CreateDefaultGlobalTargets( std::vector<GlobalTargetInfo>& targets) { @@ -3142,10 +3147,11 @@ bool cmGlobalGenerator::IsReservedTarget(std::string const& name) // by one or more of the cmake generators. // Adding additional targets to this list will require a policy! - const char* reservedTargets[] = { "all", "ALL_BUILD", "help", - "install", "INSTALL", "preinstall", - "clean", "edit_cache", "rebuild_cache", - "ZERO_CHECK" }; + static const cm::static_string_view reservedTargets[] = { + "all"_s, "ALL_BUILD"_s, "help"_s, "install"_s, + "INSTALL"_s, "preinstall"_s, "clean"_s, "edit_cache"_s, + "rebuild_cache"_s, "ZERO_CHECK"_s + }; return cm::contains(reservedTargets, name); } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 54c3737..7368a6a 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -554,6 +554,15 @@ cmGlobalNinjaGenerator::cmGlobalNinjaGenerator(cmake* cm) { #ifdef _WIN32 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"; + } #endif this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake"; } @@ -1269,6 +1278,13 @@ std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget( return cmStrCat("cmake_object_order_depends_target_", target->GetName()); } +std::string cmGlobalNinjaGenerator::OrderDependsTargetForTargetPrivate( + cmGeneratorTarget const* target, const std::string& config) const +{ + return cmStrCat(this->OrderDependsTargetForTarget(target, config), + "_private"); +} + void cmGlobalNinjaGenerator::AppendTargetOutputs( cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, cmNinjaTargetDepends depends) const @@ -3167,3 +3183,10 @@ std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTarget( return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_', cmSystemTools::UpperCase(config)); } + +std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTargetPrivate( + cmGeneratorTarget const* target, const std::string& config) const +{ + return cmStrCat(this->OrderDependsTargetForTarget(target, config), + "_private"); +} diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 4b026eb..56a922c 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -282,6 +282,10 @@ public: }; MapToNinjaPathImpl MapToNinjaPath() { return { this }; } +#ifdef _WIN32 + std::string const& GetComspec() const { return this->Comspec; } +#endif + // -- Additional clean files void AddAdditionalCleanFile(std::string fileName, const std::string& config); const char* GetAdditionalCleanTargetName() const @@ -345,6 +349,9 @@ public: virtual std::string OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const; + virtual std::string OrderDependsTargetForTargetPrivate( + cmGeneratorTarget const* target, const std::string& config) const; + void AppendTargetOutputs(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, cmNinjaTargetDepends depends) const; @@ -592,6 +599,11 @@ private: codecvt_Encoding NinjaExpectedEncoding = codecvt_Encoding::None; +#ifdef _WIN32 + // Windows Command shell. + std::string Comspec; +#endif + bool DiagnosedCxxModuleNinjaSupport = false; void InitOutputPathPrefix(); @@ -729,6 +741,9 @@ public: std::string OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const override; + std::string OrderDependsTargetForTargetPrivate( + cmGeneratorTarget const* target, const std::string& config) const override; + protected: bool OpenBuildFileStreams() override; void CloseBuildFileStreams() override; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index b2657a7..6953ec6 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -438,8 +438,8 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) { root->GetMakefile()->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", target->GetName(), - "\" target contains C++ module sources which are not " + cmStrCat("The target named \"", target->GetName(), + "\" contains C++ sources that export modules which is not " "supported by the generator")); } diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 9834b64..90b8839 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -407,8 +407,10 @@ bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField( mf->IssueMessage(MessageType::FATAL_ERROR, e); return false; } - if (this->XcodeBuildSystem == BuildSystem::Twelve && - this->XcodeVersion < 120) { + if ((this->XcodeBuildSystem == BuildSystem::Twelve && + this->XcodeVersion < 120) || + (this->XcodeBuildSystem == BuildSystem::One && + this->XcodeVersion >= 140)) { /* clang-format off */ std::string const& e = cmStrCat( "Generator\n" @@ -1385,8 +1387,8 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( if (gtgt->HaveCxx20ModuleSources()) { gtgt->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", gtgt->GetName(), - "\" target contains C++ module sources which are not " + cmStrCat("The target named \"", gtgt->GetName(), + "\" contains C++ sources that export modules which is not " "supported by the generator")); } diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 7f5f15b..1b47ec4 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -1837,8 +1837,7 @@ bool HandleDirectoryMode(std::vector<std::string> const& args, } // Make sure the name is a directory. - if (cmSystemTools::FileExists(dir) && - !cmSystemTools::FileIsDirectory(dir)) { + if (cmSystemTools::FileExists(dir, true)) { status.SetError(cmStrCat(args[0], " given non-directory \"", args[i], "\" to install.")); return false; diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx index 1f5005b..a29e33f 100644 --- a/Source/cmLinkLineComputer.cxx +++ b/Source/cmLinkLineComputer.cxx @@ -192,24 +192,16 @@ std::string cmLinkLineComputer::ComputeRPath(cmComputeLinkInformation& cli) } std::string cmLinkLineComputer::ComputeFrameworkPath( - cmComputeLinkInformation& cli, cmValue fwSearchFlag, cmValue sysFwSearchFlag) + cmComputeLinkInformation& cli, cmValue fwSearchFlag) { - if (!fwSearchFlag && !sysFwSearchFlag) { + if (!fwSearchFlag) { return std::string{}; } std::string frameworkPath; - auto const& fwDirs = cli.GetFrameworkPaths(); - for (auto const& fd : fwDirs) { - if (sysFwSearchFlag && - cli.GetTarget()->IsSystemIncludeDirectory(fd, cli.GetConfig(), - cli.GetLinkLanguage())) { - frameworkPath += sysFwSearchFlag; - } else { - frameworkPath += fwSearchFlag; - } - frameworkPath += this->ConvertToOutputFormat(fd); - frameworkPath += " "; + for (auto const& fd : cli.GetFrameworkPaths()) { + frameworkPath += + cmStrCat(fwSearchFlag, this->ConvertToOutputFormat(fd), ' '); } return frameworkPath; } diff --git a/Source/cmLinkLineComputer.h b/Source/cmLinkLineComputer.h index 4e285f2..afd3944 100644 --- a/Source/cmLinkLineComputer.h +++ b/Source/cmLinkLineComputer.h @@ -44,8 +44,7 @@ public: std::vector<BT<std::string>>& linkPath); std::string ComputeFrameworkPath(cmComputeLinkInformation& cli, - cmValue fwSearchFlag, - cmValue sysFwSearchFlag); + cmValue fwSearchFlag); std::string ComputeLinkLibraries(cmComputeLinkInformation& cli, std::string const& stdLibString); diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index ded6466..28aa5d9 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -101,9 +101,7 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries( ItemVector const& items = cli.GetItems(); std::string config = cli.GetConfig(); bool skipItemAfterFramework = false; - // Note: - // Any modification of this algorithm should be reflected also in - // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions + for (auto const& item : items) { if (skipItemAfterFramework) { skipItemAfterFramework = false; @@ -132,11 +130,13 @@ void cmLinkLineDeviceComputer::ComputeLinkLibraries( BT<std::string> linkLib; if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) { - // nvcc understands absolute paths to libraries ending in '.a' or '.lib'. - // These should be passed to nvlink. Other extensions need to be left - // out because nvlink may not understand or need them. Even though it - // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'. - if (cmHasLiteralSuffix(item.Value.Value, ".a") || + // nvcc understands absolute paths to libraries ending in '.o', .a', or + // '.lib'. These should be passed to nvlink. Other extensions need to be + // left out because nvlink may not understand or need them. Even though + // it can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'. + if (cmHasLiteralSuffix(item.Value.Value, ".o") || + cmHasLiteralSuffix(item.Value.Value, ".obj") || + cmHasLiteralSuffix(item.Value.Value, ".a") || cmHasLiteralSuffix(item.Value.Value, ".lib")) { linkLib.Value = item .GetFormattedItem(this->ConvertToOutputFormat( diff --git a/Source/cmList.h b/Source/cmList.h index 0f875e7..1b94c2f 100644 --- a/Source/cmList.h +++ b/Source/cmList.h @@ -747,8 +747,10 @@ public: ExpandElements expandElements = ExpandElements::Yes, EmptyElements emptyElements = EmptyElements::No) { - this->insert(this->begin() + this->ComputeInsertIndex(index), first, last, - expandElements, emptyElements); + auto const offset = + static_cast<difference_type>(this->ComputeInsertIndex(index)); + this->insert(this->begin() + offset, first, last, expandElements, + emptyElements); return *this; } template <typename InputIterator> @@ -1186,13 +1188,13 @@ private: auto size = container.size(); insertPos = cmList::Insert(container, insertPos, *first, expandElements, emptyElements); - insertPos += container.size() - size; + insertPos += static_cast<decltype(delta)>(container.size() - size); } } else { for (; first != last; ++first) { if (!first->empty() || emptyElements == EmptyElements::Yes) { insertPos = container.insert(insertPos, *first); - insertPos++; + ++insertPos; } } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 6472031..fe8d502 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -87,6 +87,8 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER", "CMAKE_TAPI", "CMAKE_CUDA_HOST_COMPILER", "CMAKE_CUDA_HOST_LINK_LAUNCHER", + "CMAKE_HIP_HOST_COMPILER", + "CMAKE_HIP_HOST_LINK_LAUNCHER", "CMAKE_CL_SHOWINCLUDES_PREFIX" }; cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) @@ -1850,11 +1852,8 @@ void cmLocalGenerator::OutputLinkLibraries( // Append the framework search path flags. cmValue fwSearchFlag = this->Makefile->GetDefinition( cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG")); - cmValue sysFwSearchFlag = this->Makefile->GetDefinition( - cmStrCat("CMAKE_", linkLanguage, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); - frameworkPath = - linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag, sysFwSearchFlag); + frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag); linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator, linkPath); linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries); @@ -2058,7 +2057,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"); } } else if (lang == "HIP") { - target->AddHIPArchitectureFlags(flags); + target->AddHIPArchitectureFlags(compileOrLink, config, flags); } // Add VFS Overlay for Clang compilers @@ -2770,10 +2769,10 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) } if (editAndContinueDebugInfo || msvc2008OrLess) { - this->CopyPchCompilePdb(config, target, *ReuseFrom, + this->CopyPchCompilePdb(config, lang, target, *ReuseFrom, reuseTarget, { ".pdb", ".idb" }); } else if (programDatabaseDebugInfo) { - this->CopyPchCompilePdb(config, target, *ReuseFrom, + this->CopyPchCompilePdb(config, lang, target, *ReuseFrom, reuseTarget, { ".pdb" }); } } @@ -2830,9 +2829,9 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) } void cmLocalGenerator::CopyPchCompilePdb( - const std::string& config, cmGeneratorTarget* target, - const std::string& ReuseFrom, cmGeneratorTarget* reuseTarget, - const std::vector<std::string>& extensions) + const std::string& config, const std::string& language, + cmGeneratorTarget* target, const std::string& ReuseFrom, + cmGeneratorTarget* reuseTarget, const std::vector<std::string>& extensions) { const std::string pdb_prefix = this->GetGlobalGenerator()->IsMultiConfig() ? cmStrCat(config, "/") : ""; @@ -2916,6 +2915,7 @@ void cmLocalGenerator::CopyPchCompilePdb( cc->SetCommandLines(commandLines); cc->SetComment(no_message); cc->SetStdPipesUTF8(true); + cc->AppendDepends({ reuseTarget->GetPchFile(config, language) }); if (this->GetGlobalGenerator()->IsVisualStudio()) { cc->SetByproducts(outputs); diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index eddc033..a920cfe 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -649,7 +649,9 @@ private: bool AllAppleArchSysrootsAreTheSame(const std::vector<std::string>& archs, cmValue sysroot); - void CopyPchCompilePdb(const std::string& config, cmGeneratorTarget* target, + void CopyPchCompilePdb(const std::string& config, + const std::string& language, + cmGeneratorTarget* target, const std::string& ReuseFrom, cmGeneratorTarget* reuseTarget, std::vector<std::string> const& extensions); diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 2316fb3..bc3da6e 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -538,13 +538,14 @@ std::string cmLocalNinjaGenerator::BuildCommandLine( std::ostringstream cmd; #ifdef _WIN32 + cmGlobalNinjaGenerator const* gg = this->GetGlobalNinjaGenerator(); bool const needCMD = cmdLines.size() > 1 || (customStep.empty() && RuleNeedsCMD(cmdLines[0])); for (auto li = cmdLines.begin(); li != cmdLines.end(); ++li) { if (li != cmdLines.begin()) { cmd << " && "; } else if (needCMD) { - cmd << "cmd.exe /C \""; + cmd << gg->GetComspec() << " /C \""; } // Put current cmdLine in brackets if it contains "||" because it has // higher precedence than "&&" in cmd.exe diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 5e3bf61..caa5e67 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -207,9 +207,9 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() if (this->GeneratorTarget->HaveCxx20ModuleSources()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", this->GeneratorTarget->GetName(), - "\" target contains C++ module sources which are not supported " - "by the generator")); + cmStrCat("The target named \"", this->GeneratorTarget->GetName(), + "\" contains C++ sources that export modules which is not " + "supported by the generator")); } // -- Write the custom commands for this target diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 09f8495..193cc0f 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -38,6 +38,7 @@ #include "cmNinjaNormalTargetGenerator.h" #include "cmNinjaUtilityTargetGenerator.h" #include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" @@ -49,6 +50,8 @@ #include "cmValue.h" #include "cmake.h" +class cmCustomCommand; + std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( cmGeneratorTarget* target) { @@ -167,6 +170,13 @@ std::string cmNinjaTargetGenerator::OrderDependsTargetForTarget( this->GeneratorTarget, config); } +std::string cmNinjaTargetGenerator::OrderDependsTargetForTargetPrivate( + const std::string& config) +{ + return this->GetGlobalGenerator()->OrderDependsTargetForTargetPrivate( + this->GeneratorTarget, config); +} + // TODO: Most of the code is picked up from // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink), // void cmMakefileTargetGenerator::WriteTargetLanguageFlags() @@ -972,16 +982,15 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) << " target " << this->GetTargetName() << "\n\n"; + std::vector<cmCustomCommand const*> customCommands; { - std::vector<cmSourceFile const*> customCommands; - this->GeneratorTarget->GetCustomCommands(customCommands, config); - for (cmSourceFile const* sf : customCommands) { + std::vector<cmSourceFile const*> customCommandSources; + this->GeneratorTarget->GetCustomCommands(customCommandSources, config); + for (cmSourceFile const* sf : customCommandSources) { cmCustomCommand const* cc = sf->GetCustomCommand(); this->GetLocalGenerator()->AddCustomCommandTarget( cc, this->GetGeneratorTarget()); - // Record the custom commands for this target. The container is used - // in WriteObjectBuildStatement when called in a loop below. - this->Configs[config].CustomCommands.push_back(cc); + customCommands.push_back(cc); } } { @@ -1019,6 +1028,45 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( cmStrCat("Order-only phony target for ", this->GetTargetName()); build.Outputs.push_back(this->OrderDependsTargetForTarget(config)); + // Gather order-only dependencies on custom command outputs. + std::vector<std::string> ccouts; + std::vector<std::string> ccouts_private; + bool usePrivateGeneratedSources = false; + if (this->GeneratorTarget->Target->HasFileSets()) { + switch (this->GetGeneratorTarget()->GetPolicyStatusCMP0154()) { + case cmPolicies::WARN: + case cmPolicies::OLD: + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + usePrivateGeneratedSources = true; + break; + } + } + for (cmCustomCommand const* cc : customCommands) { + cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator()); + const std::vector<std::string>& ccoutputs = ccg.GetOutputs(); + const std::vector<std::string>& ccbyproducts = ccg.GetByproducts(); + ccouts.insert(ccouts.end(), ccoutputs.begin(), ccoutputs.end()); + ccouts.insert(ccouts.end(), ccbyproducts.begin(), ccbyproducts.end()); + if (usePrivateGeneratedSources) { + auto it = ccouts.begin(); + while (it != ccouts.end()) { + cmFileSet const* fileset = + this->GeneratorTarget->GetFileSetForSource( + config, this->Makefile->GetOrCreateGeneratedSource(*it)); + if (fileset && + fileset->GetVisibility() != cmFileSetVisibility::Private) { + ++it; + } else { + ccouts_private.push_back(*it); + it = ccouts.erase(it); + } + } + } + } + cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps; this->GetLocalGenerator()->AppendTargetDepends( this->GeneratorTarget, orderOnlyDeps, config, fileConfig, @@ -1028,17 +1076,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles); // Add order-only dependencies on custom command outputs. - for (cmCustomCommand const* cc : this->Configs[config].CustomCommands) { - cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator()); - const std::vector<std::string>& ccoutputs = ccg.GetOutputs(); - const std::vector<std::string>& ccbyproducts = ccg.GetByproducts(); - std::transform(ccoutputs.begin(), ccoutputs.end(), - std::back_inserter(orderOnlyDeps), - this->MapToNinjaPath()); - std::transform(ccbyproducts.begin(), ccbyproducts.end(), - std::back_inserter(orderOnlyDeps), - this->MapToNinjaPath()); - } + std::transform(ccouts.begin(), ccouts.end(), + std::back_inserter(orderOnlyDeps), this->MapToNinjaPath()); std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end()); orderOnlyDeps.erase( @@ -1059,8 +1098,32 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), build); - } + // Add order-only dependencies on custom command outputs that are + // private to this target. + this->HasPrivateGeneratedSources = !ccouts_private.empty(); + if (this->HasPrivateGeneratedSources) { + cmNinjaBuild buildPrivate("phony"); + cmNinjaDeps& orderOnlyDepsPrivate = buildPrivate.OrderOnlyDeps; + orderOnlyDepsPrivate.push_back( + this->OrderDependsTargetForTarget(config)); + + buildPrivate.Outputs.push_back( + this->OrderDependsTargetForTargetPrivate(config)); + + std::transform(ccouts_private.begin(), ccouts_private.end(), + std::back_inserter(orderOnlyDepsPrivate), + this->MapToNinjaPath()); + + std::sort(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()); + orderOnlyDepsPrivate.erase( + std::unique(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()), + orderOnlyDepsPrivate.end()); + + this->GetGlobalGenerator()->WriteBuild( + this->GetImplFileStream(fileConfig), buildPrivate); + } + } { std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, config); @@ -1387,7 +1450,13 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->MapToNinjaPath()); } - objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); + if (this->HasPrivateGeneratedSources) { + objBuild.OrderOnlyDeps.push_back( + this->OrderDependsTargetForTargetPrivate(config)); + } else { + objBuild.OrderOnlyDeps.push_back( + this->OrderDependsTargetForTarget(config)); + } // If the source file is GENERATED and does not have a custom command // (either attached to this source file or another one), assume that one of diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 49e7018..3f56113 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -19,7 +19,6 @@ #include "cmNinjaTypes.h" #include "cmOSXBundleGenerator.h" -class cmCustomCommand; class cmGeneratedFileStream; class cmGeneratorTarget; class cmLocalNinjaGenerator; @@ -82,6 +81,7 @@ protected: bool CompileWithDefines(std::string const& lang) const; std::string OrderDependsTargetForTarget(const std::string& config); + std::string OrderDependsTargetForTargetPrivate(const std::string& config); std::string ComputeOrderDependsForTarget(); @@ -229,6 +229,7 @@ protected: private: cmLocalNinjaGenerator* LocalGenerator; + bool HasPrivateGeneratedSources = false; struct ScanningFiles { @@ -251,7 +252,6 @@ private: mutable ImportedCxxModuleLookup ImportedCxxModules; // Swift Support Json::Value SwiftOutputMap; - std::vector<cmCustomCommand const*> CustomCommands; cmNinjaDeps ExtraFiles; std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator; }; diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx index 6a9a74c..75d1501 100644 --- a/Source/cmOutputRequiredFilesCommand.cxx +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -414,8 +414,7 @@ protected: path += "/"; } path += fname; - if (cmSystemTools::FileExists(path, true) && - !cmSystemTools::FileIsDirectory(path)) { + if (cmSystemTools::FileExists(path, true)) { std::string fp = cmSystemTools::CollapseFullPath(path); this->DirectoryToFileToPathMap[extraPath][fname] = fp; return fp; @@ -428,8 +427,7 @@ protected: path = path + "/"; } path = path + fname; - if (cmSystemTools::FileExists(path, true) && - !cmSystemTools::FileIsDirectory(path)) { + if (cmSystemTools::FileExists(path, true)) { std::string fp = cmSystemTools::CollapseFullPath(path); this->DirectoryToFileToPathMap[extraPath][fname] = fp; return fp; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 26c141d..e894073 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -465,7 +465,11 @@ class cmMakefile; "file(REAL_PATH) resolves symlinks before collapsing ../ components.", 3, \ 28, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0153, "The exec_program command should not be called.", \ - 3, 28, 0, cmPolicies::WARN) + 3, 28, 0, cmPolicies::WARN) \ + SELECT( \ + POLICY, CMP0154, \ + "Generated files are private by default in targets using file sets.", 3, \ + 28, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -503,7 +507,8 @@ class cmMakefile; F(CMP0113) \ F(CMP0119) \ F(CMP0131) \ - F(CMP0142) + F(CMP0142) \ + F(CMP0154) #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \ F(CMP0116) \ diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx index 9e7854b..1dd1dce 100644 --- a/Source/cmProcessTools.cxx +++ b/Source/cmProcessTools.cxx @@ -2,48 +2,68 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmProcessTools.h" +#include <algorithm> +#include <iterator> #include <ostream> -#include "cmsys/Process.h" +#include <cm3p/uv.h> #include "cmProcessOutput.h" +#include "cmUVHandlePtr.h" +#include "cmUVStream.h" -void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out, - OutputParser* err, Encoding encoding) +std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess( + cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err, + Encoding encoding) { - cmsysProcess_Execute(cp); - char* data = nullptr; - int length = 0; - int p; cmProcessOutput processOutput(encoding); + + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + + auto chain = builder.Start(); + std::string strdata; - while ((out || err) && - (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { - if (out && p == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata, 1); - if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { - out = nullptr; + cm::uv_pipe_ptr outputPipe; + outputPipe.init(chain.GetLoop(), 0); + uv_pipe_open(outputPipe, chain.OutputStream()); + auto outputHandle = cmUVStreamRead( + outputPipe, + [&out, &processOutput, &strdata](std::vector<char> data) { + if (out) { + processOutput.DecodeText(data.data(), data.size(), strdata, 1); + if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { + out = nullptr; + } } - } else if (err && p == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 2); - if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { - err = nullptr; + }, + [&out]() { out = nullptr; }); + cm::uv_pipe_ptr errorPipe; + errorPipe.init(chain.GetLoop(), 0); + uv_pipe_open(errorPipe, chain.ErrorStream()); + auto errorHandle = cmUVStreamRead( + errorPipe, + [&err, &processOutput, &strdata](std::vector<char> data) { + if (err) { + processOutput.DecodeText(data.data(), data.size(), strdata, 2); + if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) { + err = nullptr; + } } - } + }, + [&err]() { err = nullptr; }); + while (out || err || !chain.Finished()) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); } - if (out) { - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - out->Process(strdata.c_str(), static_cast<int>(strdata.size())); - } - } - if (err) { - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - err->Process(strdata.c_str(), static_cast<int>(strdata.size())); - } - } - cmsysProcess_WaitForExit(cp, nullptr); + + std::vector<cmUVProcessChain::Status> result; + auto status = chain.GetStatus(); + std::transform( + status.begin(), status.end(), std::back_inserter(result), + [](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status { + return *s; + }); + return result; } cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR) diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h index 74ec5e0..2bdabea 100644 --- a/Source/cmProcessTools.h +++ b/Source/cmProcessTools.h @@ -7,8 +7,10 @@ #include <cstring> #include <iosfwd> #include <string> +#include <vector> #include "cmProcessOutput.h" +#include "cmUVProcessChain.h" /** \class cmProcessTools * \brief Helper classes for process output parsing @@ -81,7 +83,7 @@ public: }; /** Run a process and send output to given parsers. */ - static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out, - OutputParser* err = nullptr, - Encoding encoding = cmProcessOutput::Auto); + static std::vector<cmUVProcessChain::Status> RunProcess( + cmUVProcessChainBuilder& builder, OutputParser* out, + OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); }; diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index d111422..2e750b6 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -6,6 +6,7 @@ #include <memory> #include <string> +#include <unordered_map> #include <vector> #include <cm/string_view> @@ -16,6 +17,22 @@ class cmQtAutoGen { public: + /** String value with per configuration variants. */ + class ConfigString + { + public: + std::string Default; + std::unordered_map<std::string, std::string> Config; + }; + + /** String values with per configuration variants. */ + template <typename C> + class ConfigStrings + { + public: + C Default; + std::unordered_map<std::string, C> Config; + }; /** Integer version. */ struct IntegerVersion { diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 1da8847..214df25 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -213,24 +213,81 @@ void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc( } } -cmQtAutoGen::CompilerFeaturesHandle +cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> cmQtAutoGenGlobalInitializer::GetCompilerFeatures( - std::string const& generator, std::string const& executable, - std::string& error) + std::string const& generator, cmQtAutoGen::ConfigString const& executable, + std::string& error, bool const isMultiConfig) { + cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res; + if (isMultiConfig) { + for (auto const& config : executable.Config) { + auto const exe = config.second; + // Check if we have cached features + { + auto it = this->CompilerFeatures_.Config[config.first].find(exe); + if (it != this->CompilerFeatures_.Config[config.first].end()) { + res.Config[config.first] = it->second; + continue; + } + } + + // Check if the executable exists + if (!cmSystemTools::FileExists(exe, true)) { + error = cmStrCat("The \"", generator, "\" executable ", + cmQtAutoGen::Quoted(exe), " does not exist."); + res.Config[config.first] = {}; + continue; + } + + // Test the executable + std::string stdOut; + { + std::string stdErr; + std::vector<std::string> command; + command.emplace_back(exe); + command.emplace_back("-h"); + int retVal = 0; + const bool runResult = cmSystemTools::RunSingleCommand( + command, &stdOut, &stdErr, &retVal, nullptr, + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), + cmProcessOutput::Auto); + if (!runResult) { + error = cmStrCat("Test run of \"", generator, "\" executable ", + cmQtAutoGen::Quoted(exe), " failed.\n", + cmQtAutoGen::QuotedCommand(command), '\n', stdOut, + '\n', stdErr); + res.Config[config.first] = {}; + continue; + } + } + + // Create valid handle + res.Config[config.first] = + std::make_shared<cmQtAutoGen::CompilerFeatures>(); + res.Config[config.first]->HelpOutput = std::move(stdOut); + + // Register compiler features + this->CompilerFeatures_.Config[config.first].emplace( + exe, res.Config[config.first]); + } + return res; + } + // Check if we have cached features { - auto it = this->CompilerFeatures_.find(executable); - if (it != this->CompilerFeatures_.end()) { - return it->second; + auto it = this->CompilerFeatures_.Default.find(executable.Default); + if (it != this->CompilerFeatures_.Default.end()) { + res.Default = it->second; + return res; } } // Check if the executable exists - if (!cmSystemTools::FileExists(executable, true)) { - error = cmStrCat("The \"", generator, "\" executable ", - cmQtAutoGen::Quoted(executable), " does not exist."); - return cmQtAutoGen::CompilerFeaturesHandle(); + if (!cmSystemTools::FileExists(executable.Default, true)) { + error = + cmStrCat("The \"", generator, "\" executable ", + cmQtAutoGen::Quoted(executable.Default), " does not exist."); + return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>(); } // Test the executable @@ -238,7 +295,7 @@ cmQtAutoGenGlobalInitializer::GetCompilerFeatures( { std::string stdErr; std::vector<std::string> command; - command.emplace_back(executable); + command.emplace_back(executable.Default); command.emplace_back("-h"); int retVal = 0; const bool runResult = cmSystemTools::RunSingleCommand( @@ -246,20 +303,18 @@ cmQtAutoGenGlobalInitializer::GetCompilerFeatures( cmDuration::zero(), cmProcessOutput::Auto); if (!runResult) { error = cmStrCat("Test run of \"", generator, "\" executable ", - cmQtAutoGen::Quoted(executable), " failed.\n", + cmQtAutoGen::Quoted(executable.Default), " failed.\n", cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n', stdErr); - return cmQtAutoGen::CompilerFeaturesHandle(); + return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>(); } } - // Create valid handle - cmQtAutoGen::CompilerFeaturesHandle res = - std::make_shared<cmQtAutoGen::CompilerFeatures>(); - res->HelpOutput = std::move(stdOut); + res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>(); + res.Default->HelpOutput = std::move(stdOut); // Register compiler features - this->CompilerFeatures_.emplace(executable, res); + this->CompilerFeatures_.Default.emplace(executable.Default, res.Default); return res; } diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h index e8569a5..5ea5997 100644 --- a/Source/cmQtAutoGenGlobalInitializer.h +++ b/Source/cmQtAutoGenGlobalInitializer.h @@ -66,14 +66,16 @@ private: void AddToGlobalAutoRcc(cmLocalGenerator* localGen, std::string const& targetName); - cmQtAutoGen::CompilerFeaturesHandle GetCompilerFeatures( - std::string const& generator, std::string const& executable, - std::string& error); + cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> + GetCompilerFeatures(std::string const& generator, + cmQtAutoGen::ConfigString const& executable, + std::string& error, bool isMultiConfig); std::vector<std::unique_ptr<cmQtAutoGenInitializer>> Initializers_; std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_; std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_; - std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle> + cmQtAutoGen::ConfigStrings< + std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>> CompilerFeatures_; Keywords const Keywords_; }; diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index c9f65f6..a33c5db 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGenInitializer.h" +#include <array> #include <cstddef> #include <deque> #include <initializer_list> @@ -17,6 +18,7 @@ #include <cm/algorithm> #include <cm/iterator> #include <cm/memory> +#include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -301,15 +303,22 @@ bool InfoWriter::Save(std::string const& filename) return fileStream.Close(); } -void AddAutogenExecutableToDependencies( - cmQtAutoGenInitializer::GenVarsT const& genVars, - std::vector<std::string>& dependencies) +cmQtAutoGen::ConfigStrings<std::vector<std::string>> generateListOptions( + cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> const& + executableFeatures, + bool IsMultiConfig) { - if (genVars.ExecutableTarget != nullptr) { - dependencies.push_back(genVars.ExecutableTarget->Target->GetName()); - } else if (!genVars.Executable.empty()) { - dependencies.push_back(genVars.Executable); + cmQtAutoGen::ConfigStrings<std::vector<std::string>> tempListOptions; + if (IsMultiConfig) { + for (auto const& executableFeature : executableFeatures.Config) { + tempListOptions.Config[executableFeature.first] = + executableFeature.second->ListOptions; + } + } else { + tempListOptions.Default = executableFeatures.Default->ListOptions; } + + return tempListOptions; } } // End of unnamed namespace @@ -332,6 +341,36 @@ cmQtAutoGenInitializer::cmQtAutoGenInitializer( this->Uic.Enabled = uicEnabled; this->Rcc.Enabled = rccEnabled; this->Rcc.GlobalTarget = globalAutoRccTarget; + this->CrossConfig = + !this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty(); +} + +void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies( + cmQtAutoGenInitializer::GenVarsT const& genVars, + std::vector<std::string>& dependencies) const +{ + if (genVars.ExecutableTarget != nullptr) { + dependencies.push_back(genVars.ExecutableTarget->Target->GetName()); + } else if (this->MultiConfig) { + cm::string_view const& configGenexWithCommandConfig = + "$<COMMAND_CONFIG:$<$<CONFIG:"; + cm::string_view const& configGenex = "$<$<CONFIG:"; + cm::string_view const& configGenexEnd = ">"; + cm::string_view const& configGenexEndWithCommandConfig = ">>"; + auto genexBegin = + this->CrossConfig ? configGenexWithCommandConfig : configGenex; + auto genexEnd = + this->CrossConfig ? configGenexEndWithCommandConfig : configGenexEnd; + for (auto const& config : genVars.Executable.Config) { + auto executableWithConfig = + cmStrCat(genexBegin, config.first, ">:", config.second, genexEnd); + dependencies.emplace_back(std::move(executableWithConfig)); + } + } else { + if (!genVars.Executable.Default.empty()) { + dependencies.push_back(genVars.Executable.Default); + } + } } bool cmQtAutoGenInitializer::InitCustomTargets() @@ -777,18 +816,30 @@ bool cmQtAutoGenInitializer::InitRcc() return false; } // Evaluate test output on demand - CompilerFeatures& features = *this->Rcc.ExecutableFeatures; - if (!features.Evaluated) { - // Look for list options - if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { - if (features.HelpOutput.find("--list") != std::string::npos) { - features.ListOptions.emplace_back("--list"); - } else if (features.HelpOutput.find("-list") != std::string::npos) { - features.ListOptions.emplace_back("-list"); + auto& features = this->Rcc.ExecutableFeatures; + auto checkAndAddOptions = [this](CompilerFeaturesHandle& feature) { + if (!feature->Evaluated) { + // Look for list options + if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { + static std::array<std::string, 2> const listOptions{ { "--list", + "-list" } }; + for (std::string const& opt : listOptions) { + if (feature->HelpOutput.find(opt) != std::string::npos) { + feature->ListOptions.emplace_back(opt); + break; + } + } } + // Evaluation finished + feature->Evaluated = true; } - // Evaluation finished - features.Evaluated = true; + }; + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + checkAndAddOptions(features.Config[config]); + } + } else { + checkAndAddOptions(features.Default); } } @@ -904,13 +955,13 @@ bool cmQtAutoGenInitializer::InitScanFiles() if (muf.MocIt || muf.UicIt) { // Search for the default header file and a private header std::string const& srcFullPath = muf.SF->ResolveFullPath(); - std::string basePath = cmStrCat( + std::string const basePath = cmStrCat( cmQtAutoGen::SubDirPrefix(srcFullPath), cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath)); for (auto const& suffix : suffixes) { std::string const suffixedPath = cmStrCat(basePath, suffix); for (auto const& ext : exts) { - std::string fullPath = cmStrCat(suffixedPath, '.', ext); + std::string const fullPath = cmStrCat(suffixedPath, '.', ext); auto constexpr locationKind = cmSourceFileLocationKind::Known; cmSourceFile* sf = @@ -1026,8 +1077,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) { if (this->CMP0071Accept) { // Let the autogen target depend on the GENERATED files - if (this->MultiConfig && - this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) { + if (this->MultiConfig && !this->CrossConfig) { for (MUFile const* muf : this->AutogenTarget.FilesGenerated) { if (muf->Configs.empty()) { this->AutogenTarget.DependFiles.insert(muf->FullPath); @@ -1125,8 +1175,13 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Path checksum qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile); // Output file name - qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, - "/qrc_", qrc.QrcName, ".cpp"); + if (this->MultiConfig && !this->GlobalGen->IsXcode()) { + qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, + "_$<CONFIG>", "/qrc_", qrc.QrcName, ".cpp"); + } else { + qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, + "/qrc_", qrc.QrcName, ".cpp"); + } std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_", qrc.QrcName, '_', qrc.QrcPathChecksum); qrc.LockFile = cmStrCat(base, "_Lock.lock"); @@ -1158,11 +1213,25 @@ bool cmQtAutoGenInitializer::InitScanFiles() for (Qrc& qrc : this->Rcc.Qrcs) { if (!qrc.Generated) { std::string error; - RccLister const lister(this->Rcc.Executable, - this->Rcc.ExecutableFeatures->ListOptions); - if (!lister.list(qrc.QrcFile, qrc.Resources, error)) { - cmSystemTools::Error(error); - return false; + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + RccLister const lister( + this->Rcc.Executable.Config[config], + this->Rcc.ExecutableFeatures.Config[config]->ListOptions); + if (!lister.list(qrc.QrcFile, qrc.Resources.Config[config], + error)) { + cmSystemTools::Error(error); + return false; + } + } + } else { + RccLister const lister( + this->Rcc.Executable.Default, + this->Rcc.ExecutableFeatures.Default->ListOptions); + if (!lister.list(qrc.QrcFile, qrc.Resources.Default, error)) { + cmSystemTools::Error(error); + return false; + } } } } @@ -1177,27 +1246,20 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile); // Determine whether to use a depfile for the AUTOGEN target. - const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) && - this->GlobalGen->GetName().find("Ninja") != std::string::npos; + bool const useDepfile = [this]() -> bool { + auto const& gen = this->GlobalGen->GetName(); + return this->QtVersion >= IntegerVersion(5, 15) && + (gen.find("Ninja") != std::string::npos || + gen.find("Make") != std::string::npos); + }(); // Files provided by the autogen target std::vector<std::string> autogenByproducts; std::vector<std::string> timestampByproducts; if (this->Moc.Enabled) { this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true); - if (useNinjaDepfile) { - if (this->MultiConfig && - !this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) { - // Make all mocs_compilation_<CONFIG>.cpp files byproducts of the - // ${target}_autogen/timestamp custom command. - // We cannot just use Moc.CompilationFileGenex here, because that - // custom command runs cmake_autogen for each configuration. - for (const auto& p : this->Moc.CompilationFile.Config) { - timestampByproducts.push_back(p.second); - } - } else { - timestampByproducts.push_back(this->Moc.CompilationFileGenex); - } + if (useDepfile) { + timestampByproducts.push_back(this->Moc.CompilationFileGenex); } else { autogenByproducts.push_back(this->Moc.CompilationFileGenex); } @@ -1230,28 +1292,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Compose command lines // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp // instead of fiddling with the include directories - std::vector<std::string> configs; - this->GlobalGen->GetQtAutoGenConfigs(configs); + bool constexpr stdPipesUTF8 = true; cmCustomCommandLines commandLines; - if (this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) { - std::string autugenInfoFileconfig; - if (this->MultiConfig) { - autugenInfoFileconfig = "$<CONFIG>"; - } else { - autugenInfoFileconfig = configs[0]; - } - commandLines.push_back(cmMakeCommandLine( - { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen", - this->AutogenTarget.InfoFile, autugenInfoFileconfig })); - - } else { - for (auto const& config : configs) { - commandLines.push_back(cmMakeCommandLine( - { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen", - this->AutogenTarget.InfoFile, config })); - } - } + AddCMakeProcessToCommandLines(this->AutogenTarget.InfoFile, "cmake_autogen", + commandLines); // Use PRE_BUILD on demand bool usePRE_BUILD = false; @@ -1365,7 +1410,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() this->AutogenTarget.DependFiles.begin(), this->AutogenTarget.DependFiles.end()); - if (useNinjaDepfile) { + if (useDepfile) { // Create a custom command that generates a timestamp file and // has a depfile assigned. The depfile is created by JobDepFilesMergeT. // @@ -1419,18 +1464,47 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() AddAutogenExecutableToDependencies(this->Moc, dependencies); AddAutogenExecutableToDependencies(this->Uic, dependencies); - + std::string outputFile; + std::string depFile; // Create the custom command that outputs the timestamp file. - const char timestampFileName[] = "timestamp"; - const std::string outputFile = - cmStrCat(this->Dir.Build, "/", timestampFileName); - this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps"); - this->AutogenTarget.DepFileRuleName = - cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName); - commandLines.push_back(cmMakeCommandLine( - { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); - - this->AddGeneratedSource(outputFile, this->Moc); + if (this->MultiConfig) { + // create timestamp file with $<CONFIG> in the name so that + // every cmake_autogen target has its own timestamp file + std::string const configView = "$<CONFIG>"; + std::string const timestampFileWithoutConfig = "timestamp_"; + std::string const depFileWithoutConfig = + cmStrCat(this->Dir.Build, "/deps_"); + std::string const timestampFileName = + timestampFileWithoutConfig + configView; + outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName); + auto const depFileWithConfig = + cmStrCat(depFileWithoutConfig, configView); + depFile = depFileWithConfig; + commandLines.push_back(cmMakeCommandLine( + { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); + + ConfigString outputFileWithConfig; + for (std::string const& config : this->ConfigsList) { + auto tempTimestampFileName = timestampFileWithoutConfig + config; + auto tempDepFile = depFileWithoutConfig + config; + outputFileWithConfig.Config[config] = tempTimestampFileName; + this->AutogenTarget.DepFileRuleName.Config[config] = + cmStrCat(this->Dir.RelativeBuild, "/", tempTimestampFileName); + this->AutogenTarget.DepFile.Config[config] = tempDepFile; + } + this->AddGeneratedSource(outputFileWithConfig, this->Moc); + } else { + cm::string_view const timestampFileName = "timestamp"; + outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName); + this->AutogenTarget.DepFile.Default = + cmStrCat(this->Dir.Build, "/deps"); + depFile = this->AutogenTarget.DepFile.Default; + this->AutogenTarget.DepFileRuleName.Default = + cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName); + commandLines.push_back(cmMakeCommandLine( + { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); + this->AddGeneratedSource(outputFile, this->Moc); + } cc = cm::make_unique<cmCustomCommand>(); cc->SetOutputs(outputFile); cc->SetByproducts(timestampByproducts); @@ -1439,14 +1513,11 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() cc->SetComment(autogenComment.c_str()); cc->SetWorkingDirectory(this->Dir.Work.c_str()); cc->SetEscapeOldStyle(false); - cc->SetDepfile(this->AutogenTarget.DepFile); + cc->SetDepfile(depFile); cc->SetStdPipesUTF8(stdPipesUTF8); this->LocalGen->AddCustomCommandToOutput(std::move(cc)); - - // Alter variables for the autogen target which now merely wraps the - // custom command dependencies.clear(); - dependencies.push_back(outputFile); + dependencies.emplace_back(std::move(outputFile)); commandLines.clear(); autogenComment.clear(); } @@ -1472,7 +1543,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() autogenTarget->AddUtility(depName.Value.first, false, this->Makefile); } } - if (!useNinjaDepfile) { + if (!useDepfile) { // Add additional autogen target dependencies to autogen target for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) { autogenTarget->AddUtility(depTarget->GetName(), false, this->Makefile); @@ -1498,6 +1569,35 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() return true; } +void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines( + std::string const& infoFile, std::string const& processName, + cmCustomCommandLines& commandLines) +{ + if (this->CrossConfig) { + commandLines.push_back(cmMakeCommandLine( + { cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile, + "$<CONFIG>", "$<COMMAND_CONFIG:$<CONFIG>>" })); + } else if (this->MultiConfig && this->GlobalGen->IsXcode()) { + for (std::string const& config : this->ConfigsList) { + commandLines.push_back( + cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", + processName, infoFile, config })); + } + } else { + std::string autoInfoFileConfig; + if (this->MultiConfig) { + autoInfoFileConfig = "$<CONFIG>"; + } else { + std::vector<std::string> configs; + this->GlobalGen->GetQtAutoGenConfigs(configs); + autoInfoFileConfig = configs[0]; + } + commandLines.push_back( + cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName, + infoFile, autoInfoFileConfig })); + } +} + bool cmQtAutoGenInitializer::InitRccTargets() { for (Qrc const& qrc : this->Rcc.Qrcs) { @@ -1518,19 +1618,9 @@ bool cmQtAutoGenInitializer::InitRccTargets() ccDepends.push_back(qrc.InfoFile); cmCustomCommandLines commandLines; - if (this->MultiConfig) { - // Build for all configurations - for (std::string const& config : this->ConfigsList) { - commandLines.push_back( - cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", - "cmake_autorcc", qrc.InfoFile, config })); - } - } else { - commandLines.push_back( - cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", - "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" })); - } - std::string ccComment = + AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines); + + std::string const ccComment = cmStrCat("Automatic RCC for ", FileProjectRelativePath(this->Makefile, qrc.QrcFile)); @@ -1578,18 +1668,30 @@ bool cmQtAutoGenInitializer::InitRccTargets() } else { // Create custom rcc command { - std::vector<std::string> ccByproducts; - // Add the resource files to the dependencies - for (std::string const& fileName : qrc.Resources) { - // Add resource file to the custom command dependencies - ccDepends.push_back(fileName); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + // Add resource file to the custom command dependencies + auto resourceFilesWithConfig = cmStrCat( + "$<$<CONFIG:", config, + ">:", cmList{ qrc.Resources.Config.at(config) }.to_string(), + ">"); + ccDepends.emplace_back(std::move(resourceFilesWithConfig)); + } + } else { + for (std::string const& fileName : qrc.Resources.Default) { + // Add resource file to the custom command dependencies + ccDepends.push_back(fileName); + } } + if (!this->Rcc.ExecutableTargetName.empty()) { ccDepends.push_back(this->Rcc.ExecutableTargetName); } + + AddAutogenExecutableToDependencies(this->Rcc, ccDepends); + cc->SetOutputs(ccOutput); - cc->SetByproducts(ccByproducts); cc->SetDepends(ccDepends); this->LocalGen->AddCustomCommandToOutput(std::move(cc)); } @@ -1688,6 +1790,7 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() // General info.SetBool("MULTI_CONFIG", this->MultiConfig); + info.SetBool("CROSS_CONFIG", this->CrossConfig); info.SetUInt("PARALLEL", this->AutogenTarget.Parallel); info.SetUInt("VERBOSITY", this->Verbosity); @@ -1701,14 +1804,14 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major); info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor); - info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable); - info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable); + info.SetConfig("QT_MOC_EXECUTABLE", this->Moc.Executable); + info.SetConfig("QT_UIC_EXECUTABLE", this->Uic.Executable); info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile); info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); - info.Set("DEP_FILE", this->AutogenTarget.DepFile); - info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName); + info.SetConfig("DEP_FILE", this->AutogenTarget.DepFile); + info.SetConfig("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName); info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles()); info.SetArray("HEADER_EXTENSIONS", this->Makefile->GetCMakeInstance()->GetHeaderExtensions()); @@ -1835,7 +1938,9 @@ bool cmQtAutoGenInitializer::SetupWriteRccInfo() // General info.SetBool("MULTI_CONFIG", this->MultiConfig); + info.SetBool("CROSS_CONFIG", this->CrossConfig); info.SetUInt("VERBOSITY", this->Verbosity); + info.Set("GENERATOR", this->GlobalGen->GetName()); // Files info.Set("LOCK_FILE", qrc.LockFile); @@ -1850,16 +1955,17 @@ bool cmQtAutoGenInitializer::SetupWriteRccInfo() info.SetConfig("INCLUDE_DIR", this->Dir.Include); // rcc executable - info.Set("RCC_EXECUTABLE", this->Rcc.Executable); - info.SetArray("RCC_LIST_OPTIONS", - this->Rcc.ExecutableFeatures->ListOptions); + info.SetConfig("RCC_EXECUTABLE", this->Rcc.Executable); + info.SetConfigArray( + "RCC_LIST_OPTIONS", + generateListOptions(this->Rcc.ExecutableFeatures, this->MultiConfig)); // qrc file info.Set("SOURCE", qrc.QrcFile); info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum); info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile)); info.SetArray("OPTIONS", qrc.Options); - info.SetArray("INPUTS", qrc.Resources); + info.SetConfigArray("INPUTS", qrc.Resources); info.Save(qrc.InfoFile); } @@ -2203,16 +2309,32 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val); - genVars.Executable = cge->Evaluate(this->LocalGen, ""); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + genVars.Executable.Config[config] = + cge->Evaluate(this->LocalGen, config); + } + } else { + genVars.Executable.Default = cge->Evaluate(this->LocalGen, ""); + } } - if (genVars.Executable.empty() && !ignoreMissingTarget) { + + if (genVars.Executable.Default.empty() && + genVars.Executable.Config.empty() && !ignoreMissingTarget) { print_err(prop + " evaluates to an empty value"); return false; } // Create empty compiler features. - genVars.ExecutableFeatures = - std::make_shared<cmQtAutoGen::CompilerFeatures>(); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + genVars.ExecutableFeatures.Config[config] = + std::make_shared<cmQtAutoGen::CompilerFeatures>(); + } + } else { + genVars.ExecutableFeatures.Default = + std::make_shared<cmQtAutoGen::CompilerFeatures>(); + } return true; } } @@ -2237,15 +2359,39 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, genVars.ExecutableTargetName = targetName; genVars.ExecutableTarget = genTarget; if (genTarget->IsImported()) { - genVars.Executable = genTarget->ImportedGetLocation(""); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + genVars.Executable.Config[config] = + genTarget->ImportedGetLocation(config); + } + } else { + genVars.Executable.Default = + genTarget->ImportedGetLocation(this->ConfigDefault); + } + } else { - genVars.Executable = genTarget->GetLocation(""); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + genVars.Executable.Config[config] = genTarget->GetLocation(config); + } + } else { + genVars.Executable.Default = + genTarget->GetLocation(this->ConfigDefault); + } } } else { if (ignoreMissingTarget) { // Create empty compiler features. - genVars.ExecutableFeatures = - std::make_shared<cmQtAutoGen::CompilerFeatures>(); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + genVars.ExecutableFeatures.Config[config] = + std::make_shared<cmQtAutoGen::CompilerFeatures>(); + } + } else { + genVars.ExecutableFeatures.Default = + std::make_shared<cmQtAutoGen::CompilerFeatures>(); + } + return true; } print_err(cmStrCat("Could not find ", executable, " executable target ", @@ -2258,10 +2404,21 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, { std::string err; genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures( - executable, genVars.Executable, err); - if (!genVars.ExecutableFeatures) { - print_err(err); - return false; + executable, genVars.Executable, err, this->MultiConfig); + if (this->MultiConfig) { + for (auto const& config : this->ConfigsList) { + if (!genVars.ExecutableFeatures.Config[config]) { + if (!genVars.ExecutableFeatures.Config[config]) { + print_err(err); + return false; + } + } + } + } else { + if (!genVars.ExecutableFeatures.Default) { + print_err(err); + return false; + } } } diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 6d5261a..ee37d2f 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -18,6 +18,7 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" +class cmCustomCommandLines; class cmGeneratorTarget; class cmGlobalGenerator; class cmLocalGenerator; @@ -32,23 +33,6 @@ class cmTarget; class cmQtAutoGenInitializer : public cmQtAutoGen { public: - /** String value with per configuration variants. */ - class ConfigString - { - public: - std::string Default; - std::unordered_map<std::string, std::string> Config; - }; - - /** String values with per configuration variants. */ - template <typename C> - class ConfigStrings - { - public: - C Default; - std::unordered_map<std::string, C> Config; - }; - /** rcc job. */ class Qrc { @@ -63,7 +47,7 @@ public: bool Generated = false; bool Unique = false; std::vector<std::string> Options; - std::vector<std::string> Resources; + ConfigStrings<std::vector<std::string>> Resources; }; /** moc and/or uic file. */ @@ -90,8 +74,8 @@ public: // Executable std::string ExecutableTargetName; cmGeneratorTarget* ExecutableTarget = nullptr; - std::string Executable; - CompilerFeaturesHandle ExecutableFeatures; + ConfigString Executable; + ConfigStrings<CompilerFeaturesHandle> ExecutableFeatures; GenVarsT(GenT gen) : Gen(gen) @@ -141,6 +125,9 @@ private: GenVarsT const& genVars, bool prepend = false); void AddToSourceGroup(std::string const& fileName, cm::string_view genNameUpper); + void AddCMakeProcessToCommandLines(std::string const& infoFile, + std::string const& processName, + cmCustomCommandLines& commandLines); void AddCleanFile(std::string const& fileName); void ConfigFileNames(ConfigString& configString, cm::string_view prefix, @@ -155,6 +142,9 @@ private: bool ignoreMissingTarget) const; void handleSkipPch(cmSourceFile* sf); + void AddAutogenExecutableToDependencies( + cmQtAutoGenInitializer::GenVarsT const& genVars, + std::vector<std::string>& dependencies) const; cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr; cmGeneratorTarget* GenTarget = nullptr; @@ -167,6 +157,7 @@ private: IntegerVersion QtVersion; unsigned int Verbosity = 0; bool MultiConfig = false; + bool CrossConfig = false; bool CMP0071Accept = false; bool CMP0071Warn = false; bool CMP0100Accept = false; @@ -201,8 +192,8 @@ private: bool DependOrigin = false; std::set<std::string> DependFiles; std::set<cmTarget*> DependTargets; - std::string DepFile; - std::string DepFileRuleName; + ConfigString DepFile; + ConfigString DepFileRuleName; // Sources to process std::unordered_map<cmSourceFile*, MUFileHandle> Headers; std::unordered_map<cmSourceFile*, MUFileHandle> Sources; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index c048312..ebdec12 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -430,10 +430,12 @@ std::string cmQtAutoGenerator::MessagePath(cm::string_view path) const return cmQtAutoGen::Quoted(res); } -bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config) +bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig) { // Info config this->InfoConfig_ = std::string(config); + this->ExecutableConfig_ = std::string(executableConfig); // Info file this->InfoFile_ = std::string(infoFile); diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 5c3a8ad..4b15fc7 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -90,6 +90,10 @@ public: std::string const& InfoDir() const { return this->InfoDir_; } cmFileTime const& InfoFileTime() const { return this->InfoFileTime_; } std::string const& InfoConfig() const { return this->InfoConfig_; } + std::string const& ExecutableConfig() const + { + return this->ExecutableConfig_; + } // -- Info file parsing /** Info file reader class. */ @@ -151,7 +155,8 @@ public: std::string MessagePath(cm::string_view path) const; // -- Run - bool Run(cm::string_view infoFile, cm::string_view config); + bool Run(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig); protected: // -- Abstract processing interface @@ -170,6 +175,7 @@ private: std::string InfoDir_; cmFileTime InfoFileTime_; std::string InfoConfig_; + std::string ExecutableConfig_; // -- Directories ProjectDirsT ProjectDirs_; }; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index ece657d..68d2897 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -170,6 +170,7 @@ public: // -- Attributes // - Config bool MultiConfig = false; + bool CrossConfig = false; IntegerVersion QtVersion = { 4, 0 }; unsigned int ThreadCount = 0; // - Directories @@ -2372,6 +2373,7 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info) { // -- Required settings if (!info.GetBool("MULTI_CONFIG", this->BaseConst_.MultiConfig, true) || + !info.GetBool("CROSS_CONFIG", this->BaseConst_.CrossConfig, true) || !info.GetUInt("QT_VERSION_MAJOR", this->BaseConst_.QtVersion.Major, true) || !info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor, @@ -2384,19 +2386,34 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info) true) || !info.GetStringConfig("PARSE_CACHE_FILE", this->BaseConst_.ParseCacheFile, true) || - !info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) || - !info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName, - false) || + !info.GetStringConfig("DEP_FILE", this->BaseConst_.DepFile, false) || + !info.GetStringConfig("DEP_FILE_RULE_NAME", + this->BaseConst_.DepFileRuleName, false) || !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) || !info.GetArray("CMAKE_LIST_FILES", this->BaseConst_.ListFiles, true) || !info.GetArray("HEADER_EXTENSIONS", this->BaseConst_.HeaderExtensions, - true) || - !info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable, - false) || - !info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable, - false)) { + true)) { return false; } + if (this->BaseConst_.CrossConfig) { + std::string const mocExecutableWithConfig = + "QT_MOC_EXECUTABLE_" + this->ExecutableConfig(); + std::string const uicExecutableWithConfig = + "QT_UIC_EXECUTABLE_" + this->ExecutableConfig(); + if (!info.GetString(mocExecutableWithConfig, this->MocConst_.Executable, + false) || + !info.GetString(uicExecutableWithConfig, this->UicConst_.Executable, + false)) { + return false; + } + } else { + if (!info.GetStringConfig("QT_MOC_EXECUTABLE", this->MocConst_.Executable, + false) || + !info.GetStringConfig("QT_UIC_EXECUTABLE", this->UicConst_.Executable, + false)) { + return false; + } + } // -- Checks if (!this->BaseConst_.CMakeExecutableTime.Load( @@ -3063,7 +3080,8 @@ std::string cmQtAutoMocUicT::AbsoluteIncludePath( } // End of unnamed namespace -bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config) +bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig) { - return cmQtAutoMocUicT().Run(infoFile, config); + return cmQtAutoMocUicT().Run(infoFile, config, executableConfig); } diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index 20f9d6e..5cb4ff1 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -10,4 +10,5 @@ * Process AUTOMOC and AUTOUIC * @return true on success */ -bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config); +bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig); diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx index e288645..576f611 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -35,6 +35,11 @@ public: private: // -- Utility bool IsMultiConfig() const { return this->MultiConfig_; } + std::string const& GetGenerator() const { return this->Generator_; } + bool IsXcode() const + { + return this->GetGenerator().find("Xcode") != std::string::npos; + } std::string MultiConfigOutput() const; // -- Abstract processing interface @@ -53,6 +58,8 @@ private: // -- Config settings bool MultiConfig_ = false; + bool CrossConfig_ = false; + std::string Generator_; // -- Directories std::string AutogenBuildDir_; std::string IncludeDir_; @@ -92,26 +99,45 @@ bool cmQtAutoRccT::InitFromInfo(InfoT const& info) { // -- Required settings if (!info.GetBool("MULTI_CONFIG", this->MultiConfig_, true) || + !info.GetString("GENERATOR", this->Generator_, true) || + !info.GetBool("CROSS_CONFIG", this->CrossConfig_, true) || !info.GetString("BUILD_DIR", this->AutogenBuildDir_, true) || !info.GetStringConfig("INCLUDE_DIR", this->IncludeDir_, true) || - !info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) || - !info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) || + !info.GetArrayConfig("RCC_LIST_OPTIONS", this->RccListOptions_, false) || !info.GetString("LOCK_FILE", this->LockFile_, true) || !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) || !info.GetString("SOURCE", this->QrcFile_, true) || !info.GetString("OUTPUT_CHECKSUM", this->RccPathChecksum_, true) || !info.GetString("OUTPUT_NAME", this->RccFileName_, true) || !info.GetArray("OPTIONS", this->Options_, false) || - !info.GetArray("INPUTS", this->Inputs_, false)) { + !info.GetArrayConfig("INPUTS", this->Inputs_, false)) { return false; } + if (this->CrossConfig_) { + std::string const rccExecutableWithConfig = + "RCC_EXECUTABLE_" + this->ExecutableConfig(); + if (!info.GetString(rccExecutableWithConfig, this->RccExecutable_, true)) { + return false; + } + } else { + if (!info.GetStringConfig("RCC_EXECUTABLE", this->RccExecutable_, true)) { + return false; + } + } + // -- Derive information this->QrcFileName_ = cmSystemTools::GetFilenameName(this->QrcFile_); this->QrcFileDir_ = cmSystemTools::GetFilenamePath(this->QrcFile_); - this->RccFilePublic_ = - cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/', - this->RccFileName_); + if (IsMultiConfig() && !this->IsXcode()) { + this->RccFilePublic_ = + cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, "_", + this->InfoConfig(), '/', this->RccFileName_); + } else { + this->RccFilePublic_ = + cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/', + this->RccFileName_); + } // rcc output file name if (this->IsMultiConfig()) { @@ -520,7 +546,8 @@ bool cmQtAutoRccT::GenerateWrapper() } // End of unnamed namespace -bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config) +bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig) { - return cmQtAutoRccT().Run(infoFile, config); + return cmQtAutoRccT().Run(infoFile, config, executableConfig); } diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index d525efa..9c0a4e9 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -10,4 +10,5 @@ * Process AUTORCC * @return true on success */ -bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config); +bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config, + cm::string_view executableConfig); diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index f6e8bc6..de42bc6 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -13,7 +13,9 @@ #include <vector> #include <cm/iterator> +#include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -71,6 +73,8 @@ struct StandardLevelComputer assert(this->Levels.size() == this->LevelsAsStrings.size()); } + // Note that the logic here is shadowed in `GetEffectiveStandard`; if one is + // changed, the other needs changed as well. std::string GetCompileOptionDef(cmMakefile* makefile, cmGeneratorTarget const* target, std::string const& config) const @@ -107,7 +111,7 @@ struct StandardLevelComputer if (cmp0128 == cmPolicies::NEW) { // Add extension flag if compiler's default doesn't match. if (ext != defaultExt) { - return cmStrCat("CMAKE_", this->Language, *defaultStd, "_", type, + return cmStrCat("CMAKE_", this->Language, *defaultStd, '_', type, "_COMPILE_OPTION"); } } else { @@ -130,7 +134,7 @@ struct StandardLevelComputer cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128), "\nFor compatibility with older versions of CMake, " "compiler extensions won't be ", - state, ".")); + state, '.')); } } @@ -144,7 +148,7 @@ struct StandardLevelComputer if (target->GetLanguageStandardRequired(this->Language)) { std::string option_flag = cmStrCat( - "CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION"); + "CMAKE_", this->Language, *standardProp, '_', type, "_COMPILE_OPTION"); cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag); if (!opt) { @@ -155,8 +159,8 @@ struct StandardLevelComputer << this->Language << *standardProp << "\" " << (ext ? "(with compiler extensions)" : "") << ". But the current compiler \"" - << makefile->GetSafeDefinition("CMAKE_" + this->Language + - "_COMPILER_ID") + << makefile->GetSafeDefinition( + cmStrCat("CMAKE_", this->Language, "_COMPILER_ID")) << "\" does not support this, or " "CMake does not know the flags to enable it."; @@ -185,7 +189,7 @@ struct StandardLevelComputer } std::string standardStr(*standardProp); - if (this->Language == "CUDA" && standardStr == "98") { + if (this->Language == "CUDA"_s && standardStr == "98"_s) { standardStr = "03"; } @@ -194,7 +198,7 @@ struct StandardLevelComputer if (stdIt == cm::cend(stds)) { std::string e = cmStrCat(this->Language, "_STANDARD is set to invalid value '", - standardStr, "'"); + standardStr, '\''); makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, target->GetBacktrace()); return std::string{}; @@ -205,7 +209,7 @@ struct StandardLevelComputer if (defaultStdIt == cm::cend(stds)) { std::string e = cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT is set to invalid value '", - *defaultStd, "'"); + *defaultStd, '\''); makefile->IssueMessage(MessageType::INTERNAL_ERROR, e); return std::string{}; } @@ -216,7 +220,7 @@ struct StandardLevelComputer (cmp0128 == cmPolicies::NEW && (stdIt < defaultStdIt || ext != defaultExt))) { auto offset = std::distance(cm::cbegin(stds), stdIt); - return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type, + return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type, "_COMPILE_OPTION"); } @@ -226,7 +230,7 @@ struct StandardLevelComputer for (; defaultStdIt < stdIt; --stdIt) { auto offset = std::distance(cm::cbegin(stds), stdIt); std::string option_flag = - cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type, + cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type, "_COMPILE_OPTION"); if (target->Target->GetMakefile()->GetDefinition(option_flag)) { return option_flag; @@ -236,6 +240,105 @@ struct StandardLevelComputer return std::string{}; } + std::string GetEffectiveStandard(cmMakefile* makefile, + cmGeneratorTarget const* target, + std::string const& config) const + { + const auto& stds = this->Levels; + const auto& stdsStrings = this->LevelsAsStrings; + + cmValue defaultStd = makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT")); + if (!cmNonempty(defaultStd)) { + // this compiler has no notion of language standard levels + return std::string{}; + } + + cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus( + cmPolicies::CMP0128) }; + bool const defaultExt{ cmIsOn(*makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) }; + bool ext = true; + + if (cmp0128 == cmPolicies::NEW) { + ext = defaultExt; + } + + if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) { + ext = cmIsOn(*extPropValue); + } + + std::string const type{ ext ? "EXTENSION" : "STANDARD" }; + + cmValue standardProp = target->GetLanguageStandard(this->Language, config); + if (!standardProp) { + if (cmp0128 == cmPolicies::NEW) { + // Add extension flag if compiler's default doesn't match. + if (ext != defaultExt) { + return *defaultStd; + } + } else { + if (ext) { + return *defaultStd; + } + } + return std::string{}; + } + + if (target->GetLanguageStandardRequired(this->Language)) { + return *standardProp; + } + + // If the request matches the compiler's defaults we don't need to add + // anything. + if (*standardProp == *defaultStd && ext == defaultExt) { + if (cmp0128 == cmPolicies::NEW) { + return std::string{}; + } + } + + std::string standardStr(*standardProp); + if (this->Language == "CUDA"_s && standardStr == "98"_s) { + standardStr = "03"; + } + + auto stdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr)); + if (stdIt == cm::cend(stds)) { + return std::string{}; + } + + auto defaultStdIt = + std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd)); + if (defaultStdIt == cm::cend(stds)) { + return std::string{}; + } + + // If the standard requested is older than the compiler's default or the + // extension mode doesn't match then we need to use a flag. + if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) || + (cmp0128 == cmPolicies::NEW && + (stdIt < defaultStdIt || ext != defaultExt))) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + return stdsStrings[offset]; + } + + // The compiler's default is at least as new as the requested standard, + // and the requested standard is not required. Decay to the newest + // standard for which a flag is defined. + for (; defaultStdIt < stdIt; --stdIt) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + std::string option_flag = + cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type, + "_COMPILE_OPTION"); + if (target->Target->GetMakefile()->GetDefinition(option_flag)) { + return stdsStrings[offset]; + } + } + + return std::string{}; + } + bool GetNewRequiredStandard(cmMakefile* makefile, std::string const& targetName, const std::string& feature, @@ -418,6 +521,18 @@ std::string cmStandardLevelResolver::GetCompileOptionDef( return mapping->second.GetCompileOptionDef(this->Makefile, target, config); } +std::string cmStandardLevelResolver::GetEffectiveStandard( + cmGeneratorTarget const* target, std::string const& lang, + std::string const& config) const +{ + const auto& mapping = StandardComputerMapping.find(lang); + if (mapping == cm::cend(StandardComputerMapping)) { + return std::string{}; + } + + return mapping->second.GetEffectiveStandard(this->Makefile, target, config); +} + bool cmStandardLevelResolver::AddRequiredTargetFeature( cmTarget* target, const std::string& feature, std::string* error) const { @@ -474,11 +589,12 @@ bool cmStandardLevelResolver::CheckCompileFeaturesAvailable( std::ostringstream e; e << "The compiler feature \"" << feature << "\" is not known to " << lang << " compiler\n\"" - << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID") + << this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_ID")) << "\"\nversion " - << this->Makefile->GetSafeDefinition("CMAKE_" + lang + - "_COMPILER_VERSION") - << "."; + << this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")) + << '.'; if (error) { *error = e.str(); } else { @@ -561,8 +677,8 @@ cmValue cmStandardLevelResolver::CompileFeaturesAvailable( return nullptr; } - cmValue featuresKnown = - this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); + cmValue featuresKnown = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, "_COMPILE_FEATURES")); if (!cmNonempty(featuresKnown)) { std::ostringstream e; @@ -572,11 +688,12 @@ cmValue cmStandardLevelResolver::CompileFeaturesAvailable( e << "No"; } e << " known features for " << lang << " compiler\n\"" - << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID") + << this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_ID")) << "\"\nversion " - << this->Makefile->GetSafeDefinition("CMAKE_" + lang + - "_COMPILER_VERSION") - << "."; + << this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")) + << '.'; if (error) { *error = e.str(); } else { diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h index 4226456..03adf3f 100644 --- a/Source/cmStandardLevelResolver.h +++ b/Source/cmStandardLevelResolver.h @@ -22,6 +22,9 @@ public: std::string GetCompileOptionDef(cmGeneratorTarget const* target, std::string const& lang, std::string const& config) const; + std::string GetEffectiveStandard(cmGeneratorTarget const* target, + std::string const& lang, + std::string const& config) const; bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, std::string* error = nullptr) const; diff --git a/Source/cmString.hxx b/Source/cmString.hxx index 86b21c8..1994c2c 100644 --- a/Source/cmString.hxx +++ b/Source/cmString.hxx @@ -493,8 +493,8 @@ public: char ch) { std::string out; - out.reserve((first - this->view_.begin()) + count2 + - (this->view_.end() - last)); + out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 + + static_cast<size_type>(this->view_.end() - last)); out.append(this->view_.begin(), first); out.append(count2, ch); out.append(last, this->view_.end()); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 2bdc928..fe421ba 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -25,12 +25,17 @@ #include <cm3p/uv.h> +#include "cm_fileno.hxx" + #include "cmDuration.h" #include "cmELF.h" #include "cmMessageMetadata.h" #include "cmProcessOutput.h" #include "cmRange.h" #include "cmStringAlgorithms.h" +#include "cmUVHandlePtr.h" +#include "cmUVProcessChain.h" +#include "cmUVStream.h" #include "cmValue.h" #if !defined(CMAKE_BOOTSTRAP) @@ -59,12 +64,14 @@ #include <cassert> #include <cctype> #include <cerrno> +#include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <functional> #include <iostream> +#include <memory> #include <sstream> #include <utility> #include <vector> @@ -568,85 +575,113 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, const char* dir, OutputOption outputflag, cmDuration timeout, Encoding encoding) { - std::vector<const char*> argv; - argv.reserve(command.size() + 1); - for (std::string const& cmd : command) { - argv.push_back(cmd.c_str()); - } - argv.push_back(nullptr); - - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, argv.data()); - cmsysProcess_SetWorkingDirectory(cp, dir); - if (cmSystemTools::GetRunCommandHideConsole()) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + cmUVProcessChainBuilder builder; + builder.AddCommand(command); + if (dir) { + builder.SetWorkingDirectory(dir); } if (outputflag == OUTPUT_PASSTHROUGH) { - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); captureStdOut = nullptr; captureStdErr = nullptr; + builder + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderr)); } else if (outputflag == OUTPUT_MERGE || (captureStdErr && captureStdErr == captureStdOut)) { - cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1); + builder.SetMergedBuiltinStreams(); captureStdErr = nullptr; + } else { + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); } assert(!captureStdErr || captureStdErr != captureStdOut); - cmsysProcess_SetTimeout(cp, timeout.count()); - cmsysProcess_Execute(cp); + auto chain = builder.Start(); + bool timedOut = false; + cm::uv_timer_ptr timer; + if (timeout.count()) { + timer.init(chain.GetLoop(), &timedOut); + timer.start( + [](uv_timer_t* t) { + auto* timedOutPtr = static_cast<bool*>(t->data); + *timedOutPtr = true; + }, + static_cast<uint64_t>(timeout.count() * 1000.0), 0); + } std::vector<char> tempStdOut; std::vector<char> tempStdErr; - char* data; - int length; - int pipe; + cm::uv_pipe_ptr outStream; + bool outFinished = true; + cm::uv_pipe_ptr errStream; + bool errFinished = true; cmProcessOutput processOutput(encoding); - std::string strdata; + std::unique_ptr<cmUVStreamReadHandle> outputHandle; + std::unique_ptr<cmUVStreamReadHandle> errorHandle; if (outputflag != OUTPUT_PASSTHROUGH && (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) { - while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) > - 0) { - // Translate NULL characters in the output into valid text. - for (int i = 0; i < length; ++i) { - if (data[i] == '\0') { - data[i] = ' '; - } - } + auto startRead = + [&outputflag, &processOutput, + &chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd, + std::vector<char>& tempStd, int id, + void (*outputFunc)(const std::string&), + bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> { + if (stream < 0) { + return nullptr; + } + + pipe.init(chain.GetLoop(), 0); + uv_pipe_open(pipe, stream); + + finished = false; + return cmUVStreamRead( + pipe, + [outputflag, &processOutput, captureStd, &tempStd, id, + outputFunc](std::vector<char> data) { + // Translate NULL characters in the output into valid text. + for (auto& c : data) { + if (c == '\0') { + c = ' '; + } + } - if (pipe == cmsysProcess_Pipe_STDOUT) { - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(data, length, strdata, 1); - cmSystemTools::Stdout(strdata); - } - if (captureStdOut) { - cm::append(tempStdOut, data, data + length); - } - } else if (pipe == cmsysProcess_Pipe_STDERR) { - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(data, length, strdata, 2); - cmSystemTools::Stderr(strdata); - } - if (captureStdErr) { - cm::append(tempStdErr, data, data + length); - } - } - } + if (outputflag != OUTPUT_NONE) { + std::string strdata; + processOutput.DecodeText(data.data(), data.size(), strdata, id); + outputFunc(strdata); + } + if (captureStd) { + cm::append(tempStd, data.data(), data.data() + data.size()); + } + }, + [&finished, outputflag, &processOutput, id, outputFunc]() { + finished = true; + if (outputflag != OUTPUT_NONE) { + std::string strdata; + processOutput.DecodeText(std::string(), strdata, id); + if (!strdata.empty()) { + outputFunc(strdata); + } + } + }); + }; - if (outputflag != OUTPUT_NONE) { - processOutput.DecodeText(std::string(), strdata, 1); - if (!strdata.empty()) { - cmSystemTools::Stdout(strdata); - } - processOutput.DecodeText(std::string(), strdata, 2); - if (!strdata.empty()) { - cmSystemTools::Stderr(strdata); - } + outputHandle = + startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1, + cmSystemTools::Stdout, outFinished); + if (chain.OutputStream() != chain.ErrorStream()) { + errorHandle = + startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2, + cmSystemTools::Stderr, errFinished); } } - cmsysProcess_WaitForExit(cp, nullptr); + while (!timedOut && !(chain.Finished() && outFinished && errFinished)) { + uv_run(&chain.GetLoop(), UV_RUN_ONCE); + } if (captureStdOut) { captureStdOut->assign(tempStdOut.begin(), tempStdOut.end()); @@ -658,37 +693,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, } bool result = true; - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - if (retVal) { - *retVal = cmsysProcess_GetExitValue(cp); - } else { - if (cmsysProcess_GetExitValue(cp) != 0) { - result = false; - } - } - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) { - const char* exception_str = cmsysProcess_GetExceptionString(cp); - if (outputflag != OUTPUT_NONE) { - std::cerr << exception_str << std::endl; - } - if (captureStdErr) { - captureStdErr->append(exception_str, strlen(exception_str)); - } else if (captureStdOut) { - captureStdOut->append(exception_str, strlen(exception_str)); - } - result = false; - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) { - const char* error_str = cmsysProcess_GetErrorString(cp); - if (outputflag != OUTPUT_NONE) { - std::cerr << error_str << std::endl; - } - if (captureStdErr) { - captureStdErr->append(error_str, strlen(error_str)); - } else if (captureStdOut) { - captureStdOut->append(error_str, strlen(error_str)); - } - result = false; - } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) { + if (timedOut) { const char* error_str = "Process terminated due to timeout\n"; if (outputflag != OUTPUT_NONE) { std::cerr << error_str << std::endl; @@ -697,9 +702,34 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, captureStdErr->append(error_str, strlen(error_str)); } result = false; + } else { + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (retVal) { + *retVal = static_cast<int>(status.ExitStatus); + } else { + if (status.ExitStatus != 0) { + result = false; + } + } + break; + default: { + if (outputflag != OUTPUT_NONE) { + std::cerr << exception.second << std::endl; + } + if (captureStdErr) { + captureStdErr->append(exception.second); + } else if (captureStdOut) { + captureStdOut->append(exception.second); + } + result = false; + } break; + } } - cmsysProcess_Delete(cp); return result; } @@ -2213,9 +2243,10 @@ bool cmSystemTools::ListTar(const std::string& outFileName, #endif } -int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, - cmDuration timeout, std::vector<char>& out, - std::vector<char>& err) +cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine( + uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe, + std::string& line, cmDuration timeout, std::vector<char>& out, + std::vector<char>& err) { line.clear(); auto outiter = out.begin(); @@ -2237,7 +2268,7 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, line.append(out.data(), length); } out.erase(out.begin(), outiter + 1); - return cmsysProcess_Pipe_STDOUT; + return WaitForLineResult::STDOUT; } } @@ -2255,33 +2286,66 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, line.append(err.data(), length); } err.erase(err.begin(), erriter + 1); - return cmsysProcess_Pipe_STDERR; + return WaitForLineResult::STDERR; } } // No newlines found. Wait for more data from the process. - int length; - char* data; - double timeoutAsDbl = timeout.count(); - int pipe = - cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl); - if (pipe == cmsysProcess_Pipe_Timeout) { + struct ReadData + { + uv_stream_t* Stream; + std::vector<char> Buffer; + bool Read = false; + bool Finished = false; + }; + auto startRead = + [](uv_stream_t* stream, + ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> { + data.Stream = stream; + return cmUVStreamRead( + stream, + [&data](std::vector<char> buf) { + data.Buffer = std::move(buf); + data.Read = true; + uv_read_stop(data.Stream); + }, + [&data]() { data.Finished = true; }); + }; + ReadData outData; + auto outHandle = startRead(outPipe, outData); + ReadData errData; + auto errHandle = startRead(errPipe, errData); + + cm::uv_timer_ptr timer; + bool timedOut = false; + timer.init(*loop, &timedOut); + timer.start( + [](uv_timer_t* handle) { + auto* timedOutPtr = static_cast<bool*>(handle->data); + *timedOutPtr = true; + }, + static_cast<uint64_t>(timeout.count() * 1000.0), 0); + + uv_run(loop, UV_RUN_ONCE); + if (timedOut) { // Timeout has been exceeded. - return pipe; + return WaitForLineResult::Timeout; } - if (pipe == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata, 1); + if (outData.Read) { + processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(), + strdata, 1); // Append to the stdout buffer. std::vector<char>::size_type size = out.size(); cm::append(out, strdata); outiter = out.begin() + size; - } else if (pipe == cmsysProcess_Pipe_STDERR) { - processOutput.DecodeText(data, length, strdata, 2); + } else if (errData.Read) { + processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(), + strdata, 2); // Append to the stderr buffer. std::vector<char>::size_type size = err.size(); cm::append(err, strdata); erriter = err.begin() + size; - } else if (pipe == cmsysProcess_Pipe_None) { + } else if (outData.Finished && errData.Finished) { // Both stdout and stderr pipes have broken. Return leftover data. processOutput.DecodeText(std::string(), strdata, 1); if (!strdata.empty()) { @@ -2298,14 +2362,20 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, if (!out.empty()) { line.append(out.data(), outiter - out.begin()); out.erase(out.begin(), out.end()); - return cmsysProcess_Pipe_STDOUT; + return WaitForLineResult::STDOUT; } if (!err.empty()) { line.append(err.data(), erriter - err.begin()); err.erase(err.begin(), err.end()); - return cmsysProcess_Pipe_STDERR; + return WaitForLineResult::STDERR; } - return cmsysProcess_Pipe_None; + return WaitForLineResult::None; + } + if (!outData.Finished) { + uv_read_stop(outPipe); + } + if (!errData.Finished) { + uv_read_stop(errPipe); } } } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 9563fd6..7e33e58 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -18,7 +18,8 @@ #include <cm/optional> #include <cm/string_view> -#include "cmsys/Process.h" +#include <cm3p/uv.h> + #include "cmsys/Status.hxx" // IWYU pragma: export #include "cmsys/SystemTools.hxx" // IWYU pragma: export @@ -339,10 +340,20 @@ public: */ static void ReportLastSystemError(const char* m); - /** a general output handler for cmsysProcess */ - static int WaitForLine(cmsysProcess* process, std::string& line, - cmDuration timeout, std::vector<char>& out, - std::vector<char>& err); + enum class WaitForLineResult + { + None, + STDOUT, + STDERR, + Timeout, + }; + + /** a general output handler for libuv */ + static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe, + uv_stream_t* errPipe, std::string& line, + cmDuration timeout, + std::vector<char>& out, + std::vector<char>& err); static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; } static bool GetForceUnixPaths() { return s_ForceUnixPaths; } diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ace93e8..abbf29e 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -3201,6 +3201,11 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const return result; } +bool cmTarget::HasFileSets() const +{ + return !this->impl->FileSets.empty(); +} + bool cmTargetInternals::CheckImportedLibName(std::string const& prop, std::string const& value) const { diff --git a/Source/cmTarget.h b/Source/cmTarget.h index b77ea0c..584856a 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -321,6 +321,8 @@ public: static std::string GetFileSetsPropertyName(const std::string& type); static std::string GetInterfaceFileSetsPropertyName(const std::string& type); + bool HasFileSets() const; + private: template <typename ValueType> void StoreProperty(const std::string& prop, ValueType value); diff --git a/Source/cmUVStream.h b/Source/cmUVStream.h index 5998256..db051b8 100644 --- a/Source/cmUVStream.h +++ b/Source/cmUVStream.h @@ -3,8 +3,11 @@ #pragma once #include <cassert> +#include <functional> #include <istream> +#include <cm/memory> + #include <cm3p/uv.h> #include "cmUVHandlePtr.h" @@ -104,28 +107,38 @@ void cmBasicUVPipeIStream<CharT, Traits>::close() using cmUVPipeIStream = cmBasicUVPipeIStream<char>; +class cmUVStreamReadHandle +{ +private: + std::vector<char> Buffer; + std::function<void(std::vector<char>)> OnRead; + std::function<void()> OnFinish; + + template <typename ReadCallback, typename FinishCallback> + friend std::unique_ptr<cmUVStreamReadHandle> cmUVStreamRead( + uv_stream_t* stream, ReadCallback onRead, FinishCallback onFinish); +}; + template <typename ReadCallback, typename FinishCallback> -void cmUVStreamRead(uv_stream_t* stream, ReadCallback onRead, - FinishCallback onFinish) +std::unique_ptr<cmUVStreamReadHandle> cmUVStreamRead(uv_stream_t* stream, + ReadCallback onRead, + FinishCallback onFinish) { - struct ReadData - { - std::vector<char> Buffer; - ReadCallback OnRead; - FinishCallback OnFinish; - }; - - stream->data = new ReadData{ {}, std::move(onRead), std::move(onFinish) }; + auto handle = cm::make_unique<cmUVStreamReadHandle>(); + handle->OnRead = std::move(onRead); + handle->OnFinish = std::move(onFinish); + + stream->data = handle.get(); uv_read_start( stream, [](uv_handle_t* s, std::size_t suggestedSize, uv_buf_t* buffer) { - auto* data = static_cast<ReadData*>(s->data); + auto* data = static_cast<cmUVStreamReadHandle*>(s->data); data->Buffer.resize(suggestedSize); buffer->base = data->Buffer.data(); buffer->len = suggestedSize; }, [](uv_stream_t* s, ssize_t nread, const uv_buf_t* buffer) { - auto* data = static_cast<ReadData*>(s->data); + auto* data = static_cast<cmUVStreamReadHandle*>(s->data); if (nread > 0) { (void)buffer; assert(buffer->base == data->Buffer.data()); @@ -134,7 +147,8 @@ void cmUVStreamRead(uv_stream_t* stream, ReadCallback onRead, } else if (nread < 0 /*|| nread == UV_EOF*/) { data->OnFinish(); uv_read_stop(s); - delete data; } }); + + return handle; } diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 1bd4c57..2a54a55 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -366,9 +366,9 @@ void cmVisualStudio10TargetGenerator::Generate() !this->GlobalGenerator->SupportsCxxModuleDyndep()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("The \"", this->GeneratorTarget->GetName(), - "\" target contains C++ module sources which are not supported " - "by the generator")); + cmStrCat("The target named \"", this->GeneratorTarget->GetName(), + "\" contains C++ sources that export modules which is not " + "supported by the generator")); } this->ProjectType = computeProjectType(this->GeneratorTarget); @@ -2551,6 +2551,12 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) break; } + std::string config; + if (!this->Configurations.empty()) { + config = this->Configurations[si.Configs[0]]; + } + auto const* fs = + this->GeneratorTarget->GetFileSetForSource(config, si.Source); if (tool) { // Compute set of configurations to exclude, if any. std::vector<size_t> const& include_configs = si.Configs; @@ -2616,6 +2622,13 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) if (si.Kind == cmGeneratorTarget::SourceKindObjectSource || si.Kind == cmGeneratorTarget::SourceKindUnityBatched) { this->OutputSourceSpecificFlags(e2, si.Source); + } else if (fs && fs->GetType() == "CXX_MODULES"_s) { + this->GeneratorTarget->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", this->GeneratorTarget->GetName(), + "\" has source file\n ", si.Source->GetFullPath(), + "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not " + "scheduled for compilation.")); } if (si.Source->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) { e2.Element("PrecompiledHeader", "NotUsing"); @@ -2625,6 +2638,13 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) } this->FinishWritingSource(e2, toolSettings); + } else if (fs && fs->GetType() == "CXX_MODULES"_s) { + this->GeneratorTarget->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Target \"", this->GeneratorTarget->GetName(), + "\" has source file\n ", si.Source->GetFullPath(), + "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not " + "scheduled for compilation.")); } } @@ -3844,22 +3864,41 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( } cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags); - // For static libraries that have device linking enabled compute - // the libraries - if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && - doDeviceLinking) { - cmComputeLinkInformation& cli = *pcli; - cmLinkLineDeviceComputer computer( - this->LocalGenerator, - this->LocalGenerator->GetStateSnapshot().GetDirectory()); - std::vector<BT<std::string>> btLibVec; - computer.ComputeLinkLibraries(cli, std::string{}, btLibVec); + if (doDeviceLinking) { std::vector<std::string> libVec; - for (auto const& item : btLibVec) { - libVec.emplace_back(item.Value); + auto const& kinded = this->GeneratorTarget->GetKindedSources(configName); + // CMake conversion uses full paths when possible to allow deeper trees. + // However, CUDA 8.0 msbuild rules fail on absolute paths so for CUDA + // we must use relative paths. + const bool forceRelative = true; + for (cmGeneratorTarget::SourceAndKind const& si : kinded.Sources) { + switch (si.Kind) { + case cmGeneratorTarget::SourceKindExternalObject: { + std::string path = + this->ConvertPath(si.Source.Value->GetFullPath(), forceRelative); + ConvertToWindowsSlash(path); + libVec.emplace_back(std::move(path)); + } break; + default: + break; + } + } + // For static libraries that have device linking enabled compute + // the libraries + if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { + cmComputeLinkInformation& cli = *pcli; + cmLinkLineDeviceComputer computer( + this->LocalGenerator, + this->LocalGenerator->GetStateSnapshot().GetDirectory()); + std::vector<BT<std::string>> btLibVec; + computer.ComputeLinkLibraries(cli, std::string{}, btLibVec); + for (auto const& item : btLibVec) { + libVec.emplace_back(item.Value); + } + } + if (!libVec.empty()) { + cudaLinkOptions.AddFlag("AdditionalDependencies", libVec); } - - cudaLinkOptions.AddFlag("AdditionalDependencies", libVec); } this->CudaLinkOptions[configName] = std::move(pOptions); @@ -3869,7 +3908,10 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( void cmVisualStudio10TargetGenerator::WriteCudaLinkOptions( Elem& e1, std::string const& configName) { - if (this->GeneratorTarget->GetType() > cmStateEnums::MODULE_LIBRARY) { + // We need to write link options for OBJECT libraries so that + // we override the default device link behavior ( enabled ) when + // building object libraries with ptx/optix-ir/etc + if (this->GeneratorTarget->GetType() > cmStateEnums::OBJECT_LIBRARY) { return; } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1d8a847..942c59b 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -785,6 +785,9 @@ void cmake::ReadListFile(const std::vector<std::string>& args, mf.SetArgcArgv(args); } + if (!cmSystemTools::FileExists(path, true)) { + cmSystemTools::Error("Not a file: " + path); + } if (!mf.ReadListFile(path)) { cmSystemTools::Error("Error processing file: " + path); } @@ -3264,7 +3267,7 @@ int cmake::CheckBuildSystem() // If any byproduct of makefile generation is missing we must re-run. cmList products{ mf.GetDefinition("CMAKE_MAKEFILE_PRODUCTS") }; for (auto const& p : products) { - if (!(cmSystemTools::FileExists(p) || cmSystemTools::FileIsSymlink(p))) { + if (!cmSystemTools::PathExists(p)) { if (verbose) { cmSystemTools::Stdout( cmStrCat("Re-run cmake, missing byproduct: ", p, '\n')); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 2ac9d8e..3dcfbf1 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -11,6 +11,8 @@ #include <cm3p/uv.h> #include <fcntl.h> +#include "cm_fileno.hxx" + #include "cmCommandLineArgument.h" #include "cmConsoleBuf.h" #include "cmCryptoHash.h" @@ -72,7 +74,6 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/Terminal.h" @@ -295,14 +296,8 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) } } - std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp( - cmsysProcess_New(), cmsysProcess_Delete); - std::vector<const char*> argv(command.size() + 1); - std::transform(command.begin(), command.end(), argv.begin(), - [](std::string const& s) { return s.c_str(); }); - argv.back() = nullptr; - cmsysProcess_SetCommand(cp.get(), argv.data()); - cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str()); + cmUVProcessChainBuilder builder; + builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir); cmsys::ofstream fout(depFile.c_str()); if (!fout) { @@ -313,22 +308,18 @@ int CLCompileAndDependencies(const std::vector<std::string>& args) CLOutputLogger errLogger(std::cerr); // Start the process. - cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger); + auto result = + cmProcessTools::RunProcess(builder, &includeParser, &errLogger); + auto const& subStatus = result.front(); int status = 0; // handle status of process - switch (cmsysProcess_GetState(cp.get())) { - case cmsysProcess_State_Exited: - status = cmsysProcess_GetExitValue(cp.get()); - break; - case cmsysProcess_State_Exception: - status = 1; - break; - case cmsysProcess_State_Error: - status = 2; - break; - default: - break; + if (subStatus.SpawnResult != 0) { + status = 2; + } else if (subStatus.TermSignal != 0) { + status = 1; + } else { + status = static_cast<int>(subStatus.ExitStatus); } if (status != 0) { @@ -1027,8 +1018,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // Complain if the -f option was not given and // either file does not exist or // file could not be removed and still exists - bool file_exists_or_forced_remove = cmSystemTools::FileExists(arg) || - cmSystemTools::FileIsSymlink(arg) || force; + bool file_exists_or_forced_remove = + cmSystemTools::PathExists(arg) || force; if (cmSystemTools::FileIsDirectory(arg)) { if (!cmRemoveDirectory(arg, recursive)) { return_value = 1; @@ -1116,7 +1107,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, int ret = 0; auto time_start = std::chrono::steady_clock::now(); - cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret); + cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr, + cmSystemTools::OUTPUT_PASSTHROUGH); auto time_finish = std::chrono::steady_clock::now(); std::chrono::duration<double> time_elapsed = time_finish - time_start; @@ -1247,8 +1239,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // supporting them. if (args[1] == "create_symlink" && args.size() == 4) { std::string const& destinationFileName = args[3]; - if ((cmSystemTools::FileExists(destinationFileName) || - cmSystemTools::FileIsSymlink(destinationFileName)) && + if (cmSystemTools::PathExists(destinationFileName) && !cmSystemTools::RemoveFile(destinationFileName)) { std::string emsg = cmSystemTools::GetLastSystemError(); std::cerr << "failed to create symbolic link '" << destinationFileName @@ -1274,8 +1265,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, return 1; } - if ((cmSystemTools::FileExists(destinationFileName) || - cmSystemTools::FileIsSymlink(destinationFileName)) && + if (cmSystemTools::PathExists(destinationFileName) && !cmSystemTools::RemoveFile(destinationFileName)) { std::string emsg = cmSystemTools::GetLastSystemError(); std::cerr << "failed to create hard link '" << destinationFileName @@ -1443,13 +1433,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { cm::string_view const infoFile = args[2]; cm::string_view const config = args[3]; - return cmQtAutoMocUic(infoFile, config) ? 0 : 1; + cm::string_view const executableConfig = + (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view(); + return cmQtAutoMocUic(infoFile, config, executableConfig) ? 0 : 1; } if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) { cm::string_view const infoFile = args[2]; cm::string_view const config = (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view(); - return cmQtAutoRcc(infoFile, config) ? 0 : 1; + cm::string_view const executableConfig = + (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view(); + return cmQtAutoRcc(infoFile, config, executableConfig) ? 0 : 1; } #endif @@ -1754,7 +1748,7 @@ int cmcmd::SymlinkExecutable(std::vector<std::string> const& args) cmsys::Status cmcmd::SymlinkInternal(std::string const& file, std::string const& link) { - if (cmSystemTools::FileExists(link) || cmSystemTools::FileIsSymlink(link)) { + if (cmSystemTools::PathExists(link)) { cmSystemTools::RemoveFile(link); } std::string linktext = cmSystemTools::GetFilenameName(file); @@ -1892,21 +1886,6 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) } } - // Allocate a process instance. - cmsysProcess* cp = cmsysProcess_New(); - if (!cp) { - std::cerr << "Error allocating process instance in link script." - << std::endl; - return 1; - } - - // Children should share stdout and stderr with this process. - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); - cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); - - // Run the command lines verbatim. - cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1); - // Read command lines from the script. cmsys::ifstream fin(args[2].c_str()); if (!fin) { @@ -1924,9 +1903,24 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) continue; } + // Allocate a process instance. + cmUVProcessChainBuilder builder; + + // Children should share stdout and stderr with this process. + builder + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, + cm_fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, + cm_fileno(stderr)); + // Setup this command line. - const char* cmd[2] = { command.c_str(), nullptr }; - cmsysProcess_SetCommand(cp, cmd); + std::vector<std::string> args2; +#ifdef _WIN32 + cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2); +#else + cmSystemTools::ParseUnixCommandLine(command.c_str(), args2); +#endif + builder.AddCommand(args2); // Report the command if verbose output is enabled. if (verbose) { @@ -1934,35 +1928,29 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) } // Run the command and wait for it to exit. - cmsysProcess_Execute(cp); - cmsysProcess_WaitForExit(cp, nullptr); + auto chain = builder.Start(); + chain.Wait(); // Report failure if any. - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Exited: { - int value = cmsysProcess_GetExitValue(cp); - if (value != 0) { - result = value; + auto const& status = chain.GetStatus(0); + auto exception = status.GetException(); + switch (exception.first) { + case cmUVProcessChain::ExceptionCode::None: + if (status.ExitStatus != 0) { + result = static_cast<int>(status.ExitStatus); } - } break; - case cmsysProcess_State_Exception: - std::cerr << "Error running link command: " - << cmsysProcess_GetExceptionString(cp) << std::endl; - result = 1; break; - case cmsysProcess_State_Error: - std::cerr << "Error running link command: " - << cmsysProcess_GetErrorString(cp) << std::endl; + case cmUVProcessChain::ExceptionCode::Spawn: + std::cerr << "Error running link command: " << exception.second; result = 2; break; default: + std::cerr << "Error running link command: " << exception.second; + result = 1; break; } } - // Free the process instance. - cmsysProcess_Delete(cp); - // Return the final resulting return value. return result; } diff --git a/Templates/MSBuild/FlagTables/v140_CL.json b/Templates/MSBuild/FlagTables/v140_CL.json index 58e22ba..d479af2 100644 --- a/Templates/MSBuild/FlagTables/v140_CL.json +++ b/Templates/MSBuild/FlagTables/v140_CL.json @@ -372,7 +372,7 @@ }, { "name": "EnableEnhancedInstructionSet", - "switch": "", + "switch": "arch:NotSet", "comment": "Not Set", "value": "NotSet", "flags": [] diff --git a/Templates/MSBuild/FlagTables/v141_CL.json b/Templates/MSBuild/FlagTables/v141_CL.json index 604e6b6..d79ea90 100644 --- a/Templates/MSBuild/FlagTables/v141_CL.json +++ b/Templates/MSBuild/FlagTables/v141_CL.json @@ -393,7 +393,7 @@ }, { "name": "EnableEnhancedInstructionSet", - "switch": "", + "switch": "arch:NotSet", "comment": "Not Set", "value": "NotSet", "flags": [] diff --git a/Templates/MSBuild/FlagTables/v142_CL.json b/Templates/MSBuild/FlagTables/v142_CL.json index 4f2dce8..3c86e22 100644 --- a/Templates/MSBuild/FlagTables/v142_CL.json +++ b/Templates/MSBuild/FlagTables/v142_CL.json @@ -407,7 +407,7 @@ }, { "name": "EnableEnhancedInstructionSet", - "switch": "", + "switch": "arch:NotSet", "comment": "Not Set", "value": "NotSet", "flags": [] diff --git a/Templates/MSBuild/FlagTables/v143_CL.json b/Templates/MSBuild/FlagTables/v143_CL.json index 119e171..a6b60aa 100644 --- a/Templates/MSBuild/FlagTables/v143_CL.json +++ b/Templates/MSBuild/FlagTables/v143_CL.json @@ -407,7 +407,7 @@ }, { "name": "EnableEnhancedInstructionSet", - "switch": "", + "switch": "arch:NotSet", "comment": "Not Set", "value": "NotSet", "flags": [] diff --git a/Tests/Assembler/CMakeLists.txt b/Tests/Assembler/CMakeLists.txt index 4635f03..56de257 100644 --- a/Tests/Assembler/CMakeLists.txt +++ b/Tests/Assembler/CMakeLists.txt @@ -12,7 +12,7 @@ set(SRCS) # and also generate assembler files from C: if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode|Ninja" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ";") - if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|LCC|Clang|AppleClang|HP|SunPro|XL)$") OR (CMAKE_C_COMPILER_ID MATCHES "Intel" AND UNIX) + if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|LCC|Clang|AppleClang|HP|SunPro|XL|OrangeC)$") OR (CMAKE_C_COMPILER_ID MATCHES "Intel" AND UNIX) AND NOT (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")) set(C_FLAGS "${CMAKE_C_FLAGS}") separate_arguments(C_FLAGS) diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt index dd4fe02..f20eb5d 100644 --- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt @@ -11,11 +11,11 @@ add_executable(target_compile_options "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" ) target_compile_options(target_compile_options - PRIVATE $<$<CXX_COMPILER_ID:AppleClang,IBMClang,Clang,GNU,LCC>:-DMY_PRIVATE_DEFINE> + PRIVATE $<$<CXX_COMPILER_ID:AppleClang,IBMClang,CrayClang,Clang,GNU,LCC>:-DMY_PRIVATE_DEFINE> PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC>:-DMY_PUBLIC_DEFINE> - PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC,Clang,AppleClang,IBMClang>:-DMY_MUTLI_COMP_PUBLIC_DEFINE> + PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:-DMY_MUTLI_COMP_PUBLIC_DEFINE> INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC>:-DMY_INTERFACE_DEFINE> - INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,IBMClang>:-DMY_MULTI_COMP_INTERFACE_DEFINE> + INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:-DMY_MULTI_COMP_INTERFACE_DEFINE> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC") @@ -51,7 +51,7 @@ if(CMAKE_GENERATOR MATCHES "Visual Studio") endif() target_compile_options(consumer - PRIVATE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,IBMClang>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>> + PRIVATE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>> ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC") diff --git a/Tests/CMakeGUI/CMakeGUITest.cmake b/Tests/CMakeGUI/CMakeGUITest.cmake index 2c6baf3..9e2c8cc 100644 --- a/Tests/CMakeGUI/CMakeGUITest.cmake +++ b/Tests/CMakeGUI/CMakeGUITest.cmake @@ -59,6 +59,12 @@ function(run_cmake_gui_test name) set(ENV{CMake_GUI_TEST_NAME} "${name}") set(ENV{CMake_GUI_CONFIG_DIR} "${_workdir}/config") + if(DEFINED ENV{PWD}) + set(_old_pwd "$ENV{PWD}") + else() + set(_old_pwd) + endif() + set(ENV{PWD} "${_workdir}") execute_process( COMMAND "${CMakeGUITest_COMMAND}" ${_rcgt_ARGS} WORKING_DIRECTORY "${_workdir}" @@ -66,6 +72,11 @@ function(run_cmake_gui_test name) OUTPUT_VARIABLE _output ERROR_VARIABLE _error ) + if(DEFINED _old_pwd) + set(ENV{PWD} "${_old_pwd}") + else() + set(ENV{PWD}) + endif() if(_result) set(_fail 1) string(REPLACE "\n" "\n " _formatted_output "${_output}") diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx index fcc45b0..b53cac4 100644 --- a/Tests/CMakeLib/testUVProcessChainHelper.cxx +++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx @@ -7,10 +7,6 @@ #include <string> #include <thread> -#ifdef _WIN32 -# include <windows.h> -#endif - #include "cmSystemTools.h" static std::string getStdin() diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx index f3977d4..af06a8e 100644 --- a/Tests/CMakeLib/testUVStreambuf.cxx +++ b/Tests/CMakeLib/testUVStreambuf.cxx @@ -503,7 +503,7 @@ bool testUVStreamRead() std::string output; bool finished = false; - cmUVStreamRead( + auto handle = cmUVStreamRead( pipeSource, [&output](std::vector<char> data) { cm::append(output, data); }, [&output, &finished]() { @@ -524,6 +524,55 @@ bool testUVStreamRead() return true; } +bool testUVStreamReadLeak() +{ + int pipe[] = { -1, -1 }; + if (cmGetPipes(pipe) < 0) { + std::cout << "cmGetPipes() returned an error" << std::endl; + return false; + } + + cm::uv_loop_ptr loop; + loop.init(); + cm::uv_pipe_ptr pipeSink; + pipeSink.init(*loop, 0); + uv_pipe_open(pipeSink, pipe[1]); + + std::string str = "Hello world!"; + uv_write_t writeReq; + uv_buf_t buf; + buf.base = &str.front(); + buf.len = str.length(); + uv_write(&writeReq, pipeSink, &buf, 1, nullptr); + uv_run(loop, UV_RUN_DEFAULT); + pipeSink.reset(); + + cm::uv_pipe_ptr pipeSource; + pipeSource.init(*loop, 0); + uv_pipe_open(pipeSource, pipe[0]); + + std::string output; + bool finished = false; + auto handle = cmUVStreamRead( + pipeSource, + [&output](std::vector<char> data) { cm::append(output, data); }, + [&output, &finished]() { + if (output != "Hello world!") { + std::cout << "Output was \"" << output + << "\", should be \"Hello world!\"" << std::endl; + return; + } + finished = true; + }); + + if (finished) { + std::cout << "finished was set" << std::endl; + return false; + } + + return true; +} + int testUVStreambuf(int argc, char** const argv) { if (argc < 2) { @@ -547,7 +596,12 @@ int testUVStreambuf(int argc, char** const argv) } if (!testUVStreamRead()) { - std::cout << "While executing testUVPipeIStream().\n"; + std::cout << "While executing testUVStreamRead().\n"; + return -1; + } + + if (!testUVStreamReadLeak()) { + std::cout << "While executing testUVStreamReadLeak().\n"; return -1; } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index d9fa0e1..915c974 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -117,8 +117,8 @@ if(BUILD_TESTING) if(WIN32 OR CYGWIN) set(CMake_TEST_RESOURCES TRUE) endif() - # for borland and watcom there is no resource support - if(WATCOM OR BORLAND) + # For some Windows toolchains there is no resource support. + if(WATCOM OR BORLAND OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") set(CMake_TEST_RESOURCES FALSE) endif() @@ -142,6 +142,10 @@ if(BUILD_TESTING) ) endif() + if(_isMultiConfig) + set(test_options -C Debug) + endif() + # Look for git to use for tests. find_program(GIT_EXECUTABLE NAMES git) @@ -2090,7 +2094,10 @@ if(BUILD_TESTING) ) endif() - if(MAKE_SUPPORTS_SPACES AND NOT CMAKE_GENERATOR STREQUAL "Xcode" AND NOT CMAKE_GENERATOR STREQUAL "Watcom WMake") + if(MAKE_SUPPORTS_SPACES + AND NOT CMAKE_GENERATOR STREQUAL "Xcode" + AND NOT CMAKE_GENERATOR STREQUAL "Watcom WMake" + ) add_test(SubDirSpaces ${CMAKE_CTEST_COMMAND} --build-and-test "${CMake_SOURCE_DIR}/Tests/SubDirSpaces" @@ -2765,29 +2772,6 @@ if(BUILD_TESTING) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateP4_DIR}") endif() - configure_file( - "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoBuild.cmake.in" - "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake" - @ONLY ESCAPE_QUOTES) - add_test(CTestTestNoBuild ${CMAKE_CTEST_COMMAND} - -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake" -V - --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut1.log" - ) - set_tests_properties(CTestTestNoBuild PROPERTIES - FAIL_REGULAR_EXPRESSION "Error" WILL_FAIL true) - - configure_file( - "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoExe.cmake.in" - "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake" - @ONLY ESCAPE_QUOTES) - add_test(CTestTestNoExe ${CMAKE_CTEST_COMMAND} - -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake" -V - --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut2.log" - ) - set_tests_properties(CTestTestNoExe PROPERTIES DEPENDS CTestTestNoBuild - PASS_REGULAR_EXPRESSION "Could not find executable" - FAIL_REGULAR_EXPRESSION "SegFault") - if(NOT CMake_TEST_NO_NETWORK) configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestUpload/test.cmake.in" @@ -3019,8 +3003,8 @@ if(BUILD_TESTING) -S "${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake" -V --output-log "${CMake_BINARY_DIR}/Tests/CTestTestCrash/testOutput.log" ) - # with watcom the SEGFAULT is not found, it just fails - if(CMAKE_GENERATOR MATCHES "Watcom WMake") + # With Watcom and OrangeC the SEGFAULT is not found, it just fails + if(CMAKE_GENERATOR MATCHES "Watcom WMake" OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") set_tests_properties(CTestTestCrash PROPERTIES PASS_REGULAR_EXPRESSION "Failed") else() diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt index 0c76158..f005843 100644 --- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt +++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt @@ -13,7 +13,7 @@ endif() unset(expect_Fortran) set(expect_NoSuchLanguage 0) -set(LANGUAGES C CXX Fortran CUDA NoSuchLanguage) +set(LANGUAGES C CXX Fortran CUDA HIP NoSuchLanguage) if(APPLE) list(APPEND LANGUAGES OBJC OBJCXX) endif() diff --git a/Tests/CTestTestFailure/CMakeLists.txt b/Tests/CTestTestFailure/CMakeLists.txt deleted file mode 100644 index b6c1e7a..0000000 --- a/Tests/CTestTestFailure/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required (VERSION 3.5) -project(CTestTestFailure) -include(CTest) - -add_executable (NoBuild badCode.cxx) -target_link_libraries (NoBuild ${EXTRA_LIBS}) - -add_test (TestNoExe NoBuild) diff --git a/Tests/CTestTestFailure/CTestConfig.cmake b/Tests/CTestTestFailure/CTestConfig.cmake deleted file mode 100644 index 5bc1e9e..0000000 --- a/Tests/CTestTestFailure/CTestConfig.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set (CTEST_NIGHTLY_START_TIME "21:00:00 EDT") -set(CTEST_DROP_METHOD "http") -set(CTEST_DROP_SITE "open.cdash.org") -set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard") diff --git a/Tests/CTestTestFailure/testNoBuild.cmake.in b/Tests/CTestTestFailure/testNoBuild.cmake.in deleted file mode 100644 index 505916e..0000000 --- a/Tests/CTestTestFailure/testNoBuild.cmake.in +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -# Settings: -set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") -set(CTEST_SITE "@SITE@") -set(CTEST_BUILD_NAME "CTestTest-@BUILDNAME@-NoBuild") - -set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestTestFailure") -set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestTestFailure") -set(CTEST_CVS_COMMAND "@CVSCOMMAND@") -set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") -set(CTEST_CMAKE_GENERATOR_PLATFORM "@CMAKE_GENERATOR_PLATFORM@") -set(CTEST_CMAKE_GENERATOR_TOOLSET "@CMAKE_GENERATOR_TOOLSET@") -set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") -set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") -set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") - -#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY}) - -CTEST_START(Experimental) -#CTEST_UPDATE(SOURCE "${CTEST_SOURCE_DIRECTORY}" RETURN_VALUE res) -CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) -CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) diff --git a/Tests/CTestTestFailure/testNoExe.cmake.in b/Tests/CTestTestFailure/testNoExe.cmake.in deleted file mode 100644 index e3d7742..0000000 --- a/Tests/CTestTestFailure/testNoExe.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -# Settings: -set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest") -set(CTEST_SITE "@SITE@") -set(CTEST_BUILD_NAME "CTestTest-@BUILDNAME@-NoExe") - -set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestTestFailure") -set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestTestFailure") -set(CTEST_CVS_COMMAND "@CVSCOMMAND@") -set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@") -set(CTEST_CMAKE_GENERATOR_PLATFORM "@CMAKE_GENERATOR_PLATFORM@") -set(CTEST_CMAKE_GENERATOR_TOOLSET "@CMAKE_GENERATOR_TOOLSET@") -set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") -set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@") -set(CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") - -#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY}) - -CTEST_START(Experimental) -CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res) diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt index 17f4408..d9bb447 100644 --- a/Tests/CompileFeatures/CMakeLists.txt +++ b/Tests/CompileFeatures/CMakeLists.txt @@ -18,7 +18,7 @@ macro(run_test feature lang) endif() endmacro() -if(NOT CMAKE_C_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$") +if(NOT CMAKE_C_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$") get_property(c_features GLOBAL PROPERTY CMAKE_C_KNOWN_FEATURES) list(FILTER c_features EXCLUDE REGEX "^c_std_[0-9][0-9]") foreach(feature ${c_features}) @@ -26,7 +26,7 @@ if(NOT CMAKE_C_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLL endforeach() endif() -if(NOT CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$") +if(NOT CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$") get_property(cxx_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES) list(FILTER cxx_features EXCLUDE REGEX "^cxx_std_[0-9][0-9]") foreach(feature ${cxx_features}) diff --git a/Tests/Complex/CMakeLists.txt b/Tests/Complex/CMakeLists.txt index d3a184d..fcc2471 100644 --- a/Tests/Complex/CMakeLists.txt +++ b/Tests/Complex/CMakeLists.txt @@ -244,6 +244,12 @@ configure_file( ${Complex_SOURCE_DIR}/Library/dummy ${Complex_BINARY_DIR}/Library/dummylib.lib COPYONLY) +if(CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + configure_file( + ${Complex_SOURCE_DIR}/Library/dummy + ${Complex_BINARY_DIR}/Library/dummylib.l + COPYONLY) +endif() foreach (ext ${CMAKE_SHLIB_SUFFIX};.so;.a;.sl ${CMAKE_SHARED_LIBRARY_SUFFIX}.2 ${CMAKE_STATIC_LIBRARY_SUFFIX}.2) diff --git a/Tests/Complex/Executable/CMakeLists.txt b/Tests/Complex/Executable/CMakeLists.txt index 2a79629..c8668b9 100644 --- a/Tests/Complex/Executable/CMakeLists.txt +++ b/Tests/Complex/Executable/CMakeLists.txt @@ -45,12 +45,8 @@ add_executable(complex.file complex.file.cxx complex_nobuild.cxx if (UNIX) target_link_libraries(complex ${CMAKE_DL_LIBS}) -else() - if (NOT BORLAND) - if(NOT MINGW) - target_link_libraries(complex rpcrt4.lib) - endif() - endif() +elseif(NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + target_link_libraries(complex rpcrt4.lib) endif () # Test linking to static lib when a shared lib has the same name. diff --git a/Tests/Complex/Library/CMakeLists.txt b/Tests/Complex/Library/CMakeLists.txt index df874ef..d216486 100644 --- a/Tests/Complex/Library/CMakeLists.txt +++ b/Tests/Complex/Library/CMakeLists.txt @@ -23,19 +23,9 @@ set(LibrarySources ${LibrarySources} remove(LibrarySources create_file.cxx GENERATED nonexisting_file) add_library(CMakeTestLibrary ${LibrarySources}) -if(WIN32) - if(NOT CYGWIN) - if(NOT BORLAND) - if(NOT MINGW) - target_link_libraries(CMakeTestLibrary - debug - user32.lib) - target_link_libraries(CMakeTestLibrary - optimized - kernel32.lib) - endif() - endif() - endif() +if(WIN32 AND NOT CYGWIN AND NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + target_link_libraries(CMakeTestLibrary debug user32.lib) + target_link_libraries(CMakeTestLibrary optimized kernel32.lib) endif() # diff --git a/Tests/ComplexOneConfig/CMakeLists.txt b/Tests/ComplexOneConfig/CMakeLists.txt index dd996e1..e4ae6ba 100644 --- a/Tests/ComplexOneConfig/CMakeLists.txt +++ b/Tests/ComplexOneConfig/CMakeLists.txt @@ -231,6 +231,12 @@ configure_file( ${Complex_SOURCE_DIR}/Library/dummy ${Complex_BINARY_DIR}/Library/dummylib.lib COPYONLY) +if(CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + configure_file( + ${Complex_SOURCE_DIR}/Library/dummy + ${Complex_BINARY_DIR}/Library/dummylib.l + COPYONLY) +endif() foreach (ext ${CMAKE_SHLIB_SUFFIX};.so;.a;.sl) configure_file( ${Complex_SOURCE_DIR}/Library/dummy diff --git a/Tests/ComplexOneConfig/Executable/CMakeLists.txt b/Tests/ComplexOneConfig/Executable/CMakeLists.txt index f935aed..a8ab17b 100644 --- a/Tests/ComplexOneConfig/Executable/CMakeLists.txt +++ b/Tests/ComplexOneConfig/Executable/CMakeLists.txt @@ -45,12 +45,8 @@ add_executable(complex.file complex.file.cxx complex_nobuild.cxx if (UNIX) target_link_libraries(complex ${CMAKE_DL_LIBS}) -else() - if (NOT BORLAND) - if(NOT MINGW) - target_link_libraries(complex rpcrt4.lib) - endif() - endif() +elseif(NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + target_link_libraries(complex rpcrt4.lib) endif () # Test linking to static lib when a shared lib has the same name. diff --git a/Tests/ComplexOneConfig/Library/CMakeLists.txt b/Tests/ComplexOneConfig/Library/CMakeLists.txt index df874ef..d216486 100644 --- a/Tests/ComplexOneConfig/Library/CMakeLists.txt +++ b/Tests/ComplexOneConfig/Library/CMakeLists.txt @@ -23,19 +23,9 @@ set(LibrarySources ${LibrarySources} remove(LibrarySources create_file.cxx GENERATED nonexisting_file) add_library(CMakeTestLibrary ${LibrarySources}) -if(WIN32) - if(NOT CYGWIN) - if(NOT BORLAND) - if(NOT MINGW) - target_link_libraries(CMakeTestLibrary - debug - user32.lib) - target_link_libraries(CMakeTestLibrary - optimized - kernel32.lib) - endif() - endif() - endif() +if(WIN32 AND NOT CYGWIN AND NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + target_link_libraries(CMakeTestLibrary debug user32.lib) + target_link_libraries(CMakeTestLibrary optimized kernel32.lib) endif() # diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt index aa25c4c..b7ce5a1 100644 --- a/Tests/CudaOnly/CMakeLists.txt +++ b/Tests/CudaOnly/CMakeLists.txt @@ -20,6 +20,7 @@ add_cuda_test_macro(CudaOnly.CircularLinkLine CudaOnlyCircularLinkLine) add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols) add_cuda_test_macro(CudaOnly.SeparateCompilation main/CudaOnlySeparateCompilation) add_cuda_test_macro(CudaOnly.SeparateCompilationPTX CudaOnlySeparateCompilationPTX) +add_cuda_test_macro(CudaOnly.SeparateCompilationTargetObjects CudaOnlySeparateCompilationTargetObjects) if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang") # Clang doesn't have flags for selecting the runtime. diff --git a/Tests/CudaOnly/Fatbin/CMakeLists.txt b/Tests/CudaOnly/Fatbin/CMakeLists.txt index db0dc22..9974404 100644 --- a/Tests/CudaOnly/Fatbin/CMakeLists.txt +++ b/Tests/CudaOnly/Fatbin/CMakeLists.txt @@ -10,6 +10,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelB.cu ${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelC.cu) set_property(TARGET CudaFATBIN PROPERTY CUDA_FATBIN_COMPILATION ON) +set_property(TARGET CudaFATBIN PROPERTY CUDA_SEPARABLE_COMPILATION ON) # Will use `cuModuleLoadFatBinary` to load the fatbinaries add_executable(CudaOnlyFatbin main.cu) diff --git a/Tests/CudaOnly/OptixIR/CMakeLists.txt b/Tests/CudaOnly/OptixIR/CMakeLists.txt index afeabda..f408d40 100644 --- a/Tests/CudaOnly/OptixIR/CMakeLists.txt +++ b/Tests/CudaOnly/OptixIR/CMakeLists.txt @@ -13,6 +13,7 @@ if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.7.0") set_property(TARGET CudaOptix PROPERTY CUDA_OPTIX_COMPILATION ON) endif() +set_property(TARGET CudaOptix PROPERTY CUDA_SEPARABLE_COMPILATION ON) set_property(TARGET CudaOptix PROPERTY CUDA_ARCHITECTURES native) add_executable(CudaOnlyOptixIR main.cu) diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt new file mode 100644 index 0000000..7dbc0d5 --- /dev/null +++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.25.5) + +project(SeparateCompilationObjects LANGUAGES CUDA) + +add_library(foo OBJECT foo.cu) +set_target_properties(foo PROPERTIES CUDA_SEPARABLE_COMPILATION ON) + +add_library(bar OBJECT bar.cu) +set_target_properties(bar PROPERTIES CUDA_SEPARABLE_COMPILATION ON) + +add_executable(CudaOnlySeparateCompilationTargetObjects main.cu) +set_target_properties(CudaOnlySeparateCompilationTargetObjects PROPERTIES CUDA_SEPARABLE_COMPILATION ON) +target_link_libraries(CudaOnlySeparateCompilationTargetObjects PRIVATE $<TARGET_OBJECTS:foo> bar) + +if(APPLE) + # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime. + set_property(TARGET CudaOnlySeparateCompilationTargetObjects PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) +endif() diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu new file mode 100644 index 0000000..234586f --- /dev/null +++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu @@ -0,0 +1,18 @@ + +#include <iostream> + +#ifdef _WIN32 +# define EXPORT __declspec(dllexport) +#else +# define EXPORT __attribute__((__visibility__("default"))) +#endif + +__global__ void b1() +{ +} + +EXPORT int bar() +{ + b1<<<1, 1>>>(); + return 0; +} diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu new file mode 100644 index 0000000..75c04af --- /dev/null +++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu @@ -0,0 +1,18 @@ + +#include <iostream> + +#ifdef _WIN32 +# define EXPORT __declspec(dllexport) +#else +# define EXPORT __attribute__((__visibility__("default"))) +#endif + +__global__ void k1() +{ +} + +EXPORT int foo() +{ + k1<<<1, 1>>>(); + return 0; +} diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu new file mode 100644 index 0000000..78b10b1 --- /dev/null +++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu @@ -0,0 +1,16 @@ +// main.cu +#include <iostream> + +#ifdef _WIN32 +# define IMPORT __declspec(dllimport) +#else +# define IMPORT +#endif + +IMPORT int foo(); +IMPORT int bar(); + +int main(int argc, char**) +{ + return foo() && bar(); +} diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 5bafdf8..2c5662d 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -462,7 +462,7 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA unset(EXP_ERROR_VARIABLE CACHE) try_compile(EXP_ERROR_VARIABLE - "${CMAKE_CURRENT_SOURCE_DIR}/test_system" + "${CMAKE_CURRENT_BINARY_DIR}/test_system" "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable" LINK_LIBRARIES exp_systemlib @@ -476,7 +476,7 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA unset(EXP_RUN_VAR CACHE) unset(EXP_COMPILE_VAR CACHE) try_run(EXP_RUN_VAR EXP_COMPILE_VAR - "${CMAKE_CURRENT_SOURCE_DIR}/test_system" + "${CMAKE_CURRENT_BINARY_DIR}/test_system" "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable" LINK_LIBRARIES exp_systemlib @@ -493,7 +493,7 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA unset(BLD_ERROR_VARIABLE CACHE) try_compile(BLD_ERROR_VARIABLE - "${CMAKE_CURRENT_SOURCE_DIR}/test_system" + "${CMAKE_CURRENT_BINARY_DIR}/test_system" "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable" LINK_LIBRARIES bld_systemlib @@ -507,7 +507,7 @@ if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREA unset(BLD_RUN_VAR CACHE) unset(BLD_COMPILE_VAR CACHE) try_run(BLD_RUN_VAR BLD_COMPILE_VAR - "${CMAKE_CURRENT_SOURCE_DIR}/test_system" + "${CMAKE_CURRENT_BINARY_DIR}/test_system" "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp" COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable" LINK_LIBRARIES bld_systemlib diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake index 6a34697..447a5b6 100644 --- a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake @@ -3,7 +3,7 @@ include("${CMAKE_CURRENT_LIST_DIR}/../check_installed.cmake") if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") check_installed([[^lib;lib/libdep8\.so;lib/liblib\.so;subdir;subdir/bin;subdir/bin/exe1;subdir/bin/exe2;subdir/lib;subdir/lib/libdep10\.so;subdir/lib/libdep11\.so;subdir/lib/libdep2\.so\.1;subdir/lib/libdep2\.so\.1\.2\.3;subdir/lib/libdep3\.so;subdir/lib/libdep5\.so;subdir/lib/libdep6\.so;subdir/lib/libdep8\.so;subdir/lib/libdep9\.so;subdir/lib/liblib\.so;subdir/lib/libmod\.so;subdir/lib/libsublib1\.so$]]) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - set(_msvc_check [[bin;bin/dep8\.dll;bin/lib\.dll;lib;lib/lib\.lib;subdir;subdir/bin;subdir/bin/dep10\.dll;subdir/bin/dep11\.dll;subdir/bin/dep2\.dll;subdir/bin/dep3\.dll;subdir/bin/dep5\.dll;subdir/bin/dep6\.dll;subdir/bin/dep8\.dll;subdir/bin/dep9\.dll;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/lib\.dll;subdir/bin/sublib1\.dll;subdir/lib;subdir/lib/mod\.dll]]) + set(_msvc_check [[bin;bin/dep8\.dll;bin/lib\.dll;lib;lib/lib\.(lib|l);subdir;subdir/bin;subdir/bin/dep10\.dll;subdir/bin/dep11\.dll;subdir/bin/dep2\.dll;subdir/bin/dep3\.dll;subdir/bin/dep5\.dll;subdir/bin/dep6\.dll;subdir/bin/dep8\.dll;subdir/bin/dep9\.dll;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/lib\.dll;subdir/bin/sublib1\.dll;subdir/lib;subdir/lib/mod\.dll]]) set(_mingw_check [[bin;bin/libdep8\.dll;bin/liblib\.dll;lib;lib/liblib\.dll\.a;lib/liblib\.lib;subdir;subdir/bin;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/libdep10\.dll;subdir/bin/libdep11\.dll;subdir/bin/libdep2\.dll;subdir/bin/libdep3\.dll;subdir/bin/libdep5\.dll;subdir/bin/libdep6\.dll;subdir/bin/libdep8\.dll;subdir/bin/libdep9\.dll;subdir/bin/liblib\.dll;subdir/bin/libsublib1\.dll;subdir/lib;subdir/lib/libmod\.dll]]) check_installed("^(${_msvc_check}|${_mingw_check})$") elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") diff --git a/Tests/FindGLUT/CMakeLists.txt b/Tests/FindGLUT/CMakeLists.txt index e75ec40..fc9ed08 100644 --- a/Tests/FindGLUT/CMakeLists.txt +++ b/Tests/FindGLUT/CMakeLists.txt @@ -5,5 +5,5 @@ add_test(NAME FindGLUT.Test COMMAND ${CMAKE_CTEST_COMMAND} ${build_generator_args} --build-project TestFindGLUT --build-options ${build_options} - --test-command ${CMAKE_CTEST_COMMAND} -V + --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options} ) diff --git a/Tests/FindGSL/CMakeLists.txt b/Tests/FindGSL/CMakeLists.txt index 45a3471..594a34e 100644 --- a/Tests/FindGSL/CMakeLists.txt +++ b/Tests/FindGSL/CMakeLists.txt @@ -5,5 +5,5 @@ add_test(NAME FindGSL.rng COMMAND ${CMAKE_CTEST_COMMAND} ${build_generator_args} --build-project FindGSL_rng --build-options ${build_options} - --test-command ${CMAKE_CTEST_COMMAND} -V + --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options} ) diff --git a/Tests/FindOpenSSL/CMakeLists.txt b/Tests/FindOpenSSL/CMakeLists.txt index 66b3fb2..9a7a61c 100644 --- a/Tests/FindOpenSSL/CMakeLists.txt +++ b/Tests/FindOpenSSL/CMakeLists.txt @@ -5,5 +5,5 @@ add_test(NAME FindOpenSSL.rand COMMAND ${CMAKE_CTEST_COMMAND} ${build_generator_args} --build-project FindOpenSSL_rand --build-options ${build_options} - --test-command ${CMAKE_CTEST_COMMAND} -V + --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options} ) diff --git a/Tests/FortranModules/CMakeLists.txt b/Tests/FortranModules/CMakeLists.txt index 5c76132..00f3e57 100644 --- a/Tests/FortranModules/CMakeLists.txt +++ b/Tests/FortranModules/CMakeLists.txt @@ -125,3 +125,12 @@ add_subdirectory(Executable) if(CMake_TEST_Fortran_SUBMODULES) add_subdirectory(Submodules) endif() + +add_subdirectory(Issue25112) +add_subdirectory(Issue25223) +if( # Intel Fortran VS Integration breaks on custom targets with Fortran sources + NOT CMAKE_GENERATOR MATCHES "Visual Studio") + add_subdirectory(Issue25252) + add_subdirectory(Issue25252-iface-target) +endif() +add_subdirectory(Issue25252-iface-sources) diff --git a/Tests/FortranModules/Issue25112/CMakeLists.txt b/Tests/FortranModules/Issue25112/CMakeLists.txt new file mode 100644 index 0000000..cf0c69e --- /dev/null +++ b/Tests/FortranModules/Issue25112/CMakeLists.txt @@ -0,0 +1,4 @@ +set(CMAKE_Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/include") +add_library(objmod OBJECT objmod.f90) +add_executable(objmain objmain.f90) +target_link_libraries(objmain PRIVATE objmod) diff --git a/Tests/FortranOnly/objmain.f90 b/Tests/FortranModules/Issue25112/objmain.f90 index d41d454..d41d454 100644 --- a/Tests/FortranOnly/objmain.f90 +++ b/Tests/FortranModules/Issue25112/objmain.f90 diff --git a/Tests/FortranOnly/objmod.f90 b/Tests/FortranModules/Issue25112/objmod.f90 index 6b79cc7..6b79cc7 100644 --- a/Tests/FortranOnly/objmod.f90 +++ b/Tests/FortranModules/Issue25112/objmod.f90 diff --git a/Tests/FortranModules/Issue25223/CMakeLists.txt b/Tests/FortranModules/Issue25223/CMakeLists.txt new file mode 100644 index 0000000..f2afcb9 --- /dev/null +++ b/Tests/FortranModules/Issue25223/CMakeLists.txt @@ -0,0 +1,15 @@ +# See https://gist.github.com/scivision/8e3070319f0577f7d3efcba863638cae +set(CMAKE_Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/include") +add_library(m1 OBJECT m1.f90) + +add_library(m2 OBJECT m2.f90) +target_link_libraries(m2 PRIVATE m1) + +add_library(m3 OBJECT m3.f90) +target_link_libraries(m3 PRIVATE m2) + +add_library(m4 OBJECT m4.f90) +target_link_libraries(m4 PRIVATE m3) + +add_executable(main25223 main.f90) +target_link_libraries(main25223 PRIVATE m4 m3 m2 m1) diff --git a/Tests/FortranModules/Issue25223/m1.f90 b/Tests/FortranModules/Issue25223/m1.f90 new file mode 100644 index 0000000..6b5ddd5 --- /dev/null +++ b/Tests/FortranModules/Issue25223/m1.f90 @@ -0,0 +1,11 @@ +module m1 + +implicit none + +contains + +pure real function pi() +pi = 4*atan(1.) +end function + +end module m1 diff --git a/Tests/FortranModules/Issue25223/m2.f90 b/Tests/FortranModules/Issue25223/m2.f90 new file mode 100644 index 0000000..c614d0e --- /dev/null +++ b/Tests/FortranModules/Issue25223/m2.f90 @@ -0,0 +1,13 @@ +module m2 + +use m1, only : pi + +implicit none + +contains + +pure real function twopi() +twopi = 2*pi() +end function + +end module diff --git a/Tests/FortranModules/Issue25223/m3.f90 b/Tests/FortranModules/Issue25223/m3.f90 new file mode 100644 index 0000000..a29ca84 --- /dev/null +++ b/Tests/FortranModules/Issue25223/m3.f90 @@ -0,0 +1,13 @@ +module m3 + +use m2, only : twopi + +implicit none + +contains + +pure real function fourpi() +fourpi = 2*twopi() +end function + +end module diff --git a/Tests/FortranModules/Issue25223/m4.f90 b/Tests/FortranModules/Issue25223/m4.f90 new file mode 100644 index 0000000..b1ec1a8 --- /dev/null +++ b/Tests/FortranModules/Issue25223/m4.f90 @@ -0,0 +1,13 @@ +module m4 + +use m3, only : fourpi + +implicit none + +contains + +pure real function halfpi() +halfpi = fourpi() / 8.0 +end function + +end module diff --git a/Tests/FortranModules/Issue25223/main.f90 b/Tests/FortranModules/Issue25223/main.f90 new file mode 100644 index 0000000..3ec3920 --- /dev/null +++ b/Tests/FortranModules/Issue25223/main.f90 @@ -0,0 +1,15 @@ +program main + +use m1, only : pi +use m4, only : halfpi + +implicit none + +real :: rpi, rhalfpi + +rpi = pi() / 2 +rhalfpi = halfpi() + +print '(a,ES15.8)', 'floating point precision loss: ', rpi - rhalfpi + +end program diff --git a/Tests/FortranModules/Issue25252-iface-sources/CMakeLists.txt b/Tests/FortranModules/Issue25252-iface-sources/CMakeLists.txt new file mode 100644 index 0000000..574435f --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-sources/CMakeLists.txt @@ -0,0 +1,9 @@ +enable_language(C) + +add_library(fortran_source_iface_sources STATIC lib.c) +target_sources(fortran_source_iface_sources + INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/iface.f90") + +add_library(lib25252-iface-sources lib.f90) +target_link_libraries(lib25252-iface-sources PRIVATE fortran_source_iface_sources) diff --git a/Tests/FortranModules/Issue25252-iface-sources/iface.f90 b/Tests/FortranModules/Issue25252-iface-sources/iface.f90 new file mode 100644 index 0000000..6b5ddd5 --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-sources/iface.f90 @@ -0,0 +1,11 @@ +module m1 + +implicit none + +contains + +pure real function pi() +pi = 4*atan(1.) +end function + +end module m1 diff --git a/Tests/FortranModules/Issue25252-iface-sources/lib.c b/Tests/FortranModules/Issue25252-iface-sources/lib.c new file mode 100644 index 0000000..6ccdb8d --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-sources/lib.c @@ -0,0 +1,4 @@ +int f() +{ + return 0; +} diff --git a/Tests/FortranModules/Issue25252-iface-sources/lib.f90 b/Tests/FortranModules/Issue25252-iface-sources/lib.f90 new file mode 100644 index 0000000..f971909 --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-sources/lib.f90 @@ -0,0 +1,13 @@ +module lib + +use m1, only : pi + +implicit none + +contains + +pure real function func() +func = pi() +end function + +end module diff --git a/Tests/FortranModules/Issue25252-iface-target/CMakeLists.txt b/Tests/FortranModules/Issue25252-iface-target/CMakeLists.txt new file mode 100644 index 0000000..b312fcd --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-target/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(fortran_source_iface INTERFACE + iface.f90) + +add_library(lib25252-iface-target lib.f90) +add_dependencies(lib25252-iface-target fortran_source_iface) diff --git a/Tests/FortranModules/Issue25252-iface-target/iface.f90 b/Tests/FortranModules/Issue25252-iface-target/iface.f90 new file mode 100644 index 0000000..0528d41 --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-target/iface.f90 @@ -0,0 +1,5 @@ +program main + +implicit none + +end program diff --git a/Tests/FortranModules/Issue25252-iface-target/lib.f90 b/Tests/FortranModules/Issue25252-iface-target/lib.f90 new file mode 100644 index 0000000..a6d7fa6 --- /dev/null +++ b/Tests/FortranModules/Issue25252-iface-target/lib.f90 @@ -0,0 +1,11 @@ +module lib + +implicit none + +contains + +pure real function func() +func = 1.0 +end function + +end module diff --git a/Tests/FortranModules/Issue25252/CMakeLists.txt b/Tests/FortranModules/Issue25252/CMakeLists.txt new file mode 100644 index 0000000..8111c42 --- /dev/null +++ b/Tests/FortranModules/Issue25252/CMakeLists.txt @@ -0,0 +1,6 @@ +add_custom_target(custom_target_with_fortran + COMMAND "${CMAKE_COMMAND}" -E echo "custom target with fortran sources" + SOURCES custom_target.f90) + +add_library(lib25252 lib.f90) +add_dependencies(lib25252 custom_target_with_fortran) diff --git a/Tests/FortranModules/Issue25252/custom_target.f90 b/Tests/FortranModules/Issue25252/custom_target.f90 new file mode 100644 index 0000000..0528d41 --- /dev/null +++ b/Tests/FortranModules/Issue25252/custom_target.f90 @@ -0,0 +1,5 @@ +program main + +implicit none + +end program diff --git a/Tests/FortranModules/Issue25252/lib.f90 b/Tests/FortranModules/Issue25252/lib.f90 new file mode 100644 index 0000000..a6d7fa6 --- /dev/null +++ b/Tests/FortranModules/Issue25252/lib.f90 @@ -0,0 +1,11 @@ +module lib + +implicit none + +contains + +pure real function func() +func = 1.0 +end function + +end module diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt index dfc28dd..02bf2b8 100644 --- a/Tests/FortranOnly/CMakeLists.txt +++ b/Tests/FortranOnly/CMakeLists.txt @@ -186,9 +186,3 @@ if(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF AND set_property(SOURCE no_preprocess_source_upper.F no_preprocess_source_fpp.fpp PROPERTY Fortran_PREPROCESS OFF) endif() - -# Issue 25112 -set(CMAKE_Fortran_MODULE_DIRECTORY "${PROJECT_BINARY_DIR}/include") -add_library(objmod OBJECT objmod.f90) -add_executable(objmain objmain.f90) -target_link_libraries(objmain PRIVATE objmod) diff --git a/Tests/HIP/ArchSpecial/CMakeLists.txt b/Tests/HIP/ArchSpecial/CMakeLists.txt new file mode 100644 index 0000000..2bc6bd2 --- /dev/null +++ b/Tests/HIP/ArchSpecial/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.27) +project(ArchSpecial HIP) + +if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA" AND + CMAKE_HIP_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) + set(compile_options -Wno-deprecated-gpu-targets) +endif() + +function(verify_output flag) + string(REPLACE "-" "_" architectures "${flag}") + string(TOUPPER "${architectures}" architectures) + set(architectures "${CMAKE_HIP_ARCHITECTURES_${architectures}}") + list(TRANSFORM architectures REPLACE "-real" "") + + if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + set(match_regex "-arch compute_([0-9]+)") + endif() + + string(REGEX MATCHALL "${match_regex}" target_cpus "${output}") + + foreach(cpu ${target_cpus}) + string(REGEX MATCH "${match_regex}" dont_care "${cpu}") + list(APPEND command_archs "${CMAKE_MATCH_1}") + endforeach() + + list(SORT command_archs) + list(REMOVE_DUPLICATES command_archs) + if(NOT "${command_archs}" STREQUAL "${architectures}") + message(FATAL_ERROR "Architectures used for \"${flag}\" don't match the reference (\"${command_archs}\" != \"${architectures}\").") + endif() +endfunction() + +set(try_compile_flags -v ${compile_options}) + +set(CMAKE_HIP_ARCHITECTURES all) +try_compile(all_archs_compiles + ${CMAKE_CURRENT_BINARY_DIR}/try_compile/all_archs_compiles + ${CMAKE_CURRENT_SOURCE_DIR}/main.hip + COMPILE_DEFINITIONS ${try_compile_flags} + OUTPUT_VARIABLE output + ) +verify_output(all) + +set(CMAKE_HIP_ARCHITECTURES all-major) +try_compile(all_major_archs_compiles + ${CMAKE_CURRENT_BINARY_DIR}/try_compile/all_major_archs_compiles + ${CMAKE_CURRENT_SOURCE_DIR}/main.hip + COMPILE_DEFINITIONS ${try_compile_flags} + OUTPUT_VARIABLE output + ) +verify_output(all-major) + +set(CMAKE_HIP_ARCHITECTURES native) +try_compile(native_archs_compiles + ${CMAKE_CURRENT_BINARY_DIR}/try_compile/native_archs_compiles + ${CMAKE_CURRENT_SOURCE_DIR}/main.hip + COMPILE_DEFINITIONS ${try_compile_flags} + OUTPUT_VARIABLE output + ) +verify_output(native) + +if(all_archs_compiles AND all_major_archs_compiles AND native_archs_compiles) + set(CMAKE_HIP_ARCHITECTURES all) + add_executable(HIPArchSpecial main.hip) + target_compile_options(HIPArchSpecial PRIVATE ${compile_options}) +endif() diff --git a/Tests/HIP/ArchSpecial/main.hip b/Tests/HIP/ArchSpecial/main.hip new file mode 100644 index 0000000..5047a34 --- /dev/null +++ b/Tests/HIP/ArchSpecial/main.hip @@ -0,0 +1,3 @@ +int main() +{ +} diff --git a/Tests/HIP/ArchitectureOff/CMakeLists.txt b/Tests/HIP/ArchitectureOff/CMakeLists.txt index bccb3b4..18f3a1e 100644 --- a/Tests/HIP/ArchitectureOff/CMakeLists.txt +++ b/Tests/HIP/ArchitectureOff/CMakeLists.txt @@ -2,7 +2,17 @@ cmake_minimum_required(VERSION 3.18) project(HIPArchitecture HIP) # Make sure CMake doesn't pass architectures if HIP_ARCHITECTURES is OFF. -string(APPEND CMAKE_HIP_FLAGS " --offload-arch=gfx908") +set(CMAKE_HIP_ARCHITECTURES OFF) + +# Pass our own architecture flags instead. +if(CMAKE_HIP_PLATFORM STREQUAL "amd") + string(APPEND CMAKE_HIP_FLAGS " --offload-arch=gfx908") +elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia") + string(APPEND CMAKE_HIP_FLAGS " -arch=sm_52") +endif() add_executable(HIPOnlyArchitectureOff main.hip) -set_property(TARGET HIPOnlyArchitectureOff PROPERTY HIP_ARCHITECTURES OFF) +get_property(hip_archs TARGET HIPOnlyArchitectureOff PROPERTY HIP_ARCHITECTURES) +if(NOT hip_archs STREQUAL "OFF") + message(FATAL_ERROR "CMAKE_HIP_ARCHITECTURES did not initialize HIP_ARCHITECTURES") +endif() diff --git a/Tests/HIP/CMakeLists.txt b/Tests/HIP/CMakeLists.txt index 9499be8..f1e2e51 100644 --- a/Tests/HIP/CMakeLists.txt +++ b/Tests/HIP/CMakeLists.txt @@ -5,11 +5,17 @@ macro (add_hip_test_macro name) endmacro () add_hip_test_macro(HIP.ArchitectureOff HIPOnlyArchitectureOff) +if(CMake_TEST_HIP STREQUAL "nvidia") + add_hip_test_macro(HIP.ArchSpecial HIPArchSpecial) +endif() add_hip_test_macro(HIP.CompileFlags HIPOnlyCompileFlags) add_hip_test_macro(HIP.EnableStandard HIPEnableStandard) add_hip_test_macro(HIP.InferHipLang1 HIPInferHipLang1) add_hip_test_macro(HIP.InferHipLang2 HIPInferHipLang2) -add_hip_test_macro(HIP.MathFunctions HIPOnlyMathFunctions) +if(CMake_TEST_HIP STREQUAL "amd") + # The NVIDIA CUDA compiler cannot handle device lambda markup. + add_hip_test_macro(HIP.MathFunctions HIPOnlyMathFunctions) +endif() add_hip_test_macro(HIP.MixedLanguage HIPMixedLanguage) add_hip_test_macro(HIP.TryCompile HIPOnlyTryCompile) add_hip_test_macro(HIP.WithDefs HIPOnlyWithDefs) diff --git a/Tests/HIP/CompileFlags/CMakeLists.txt b/Tests/HIP/CompileFlags/CMakeLists.txt index c808313..a3adb7b 100644 --- a/Tests/HIP/CompileFlags/CMakeLists.txt +++ b/Tests/HIP/CompileFlags/CMakeLists.txt @@ -3,6 +3,11 @@ project(CompileFlags HIP) add_executable(HIPOnlyCompileFlags main.hip) -set_property(TARGET HIPOnlyCompileFlags PROPERTY HIP_ARCHITECTURES gfx803) +if(CMAKE_HIP_PLATFORM STREQUAL "amd") + set(hip_archs gfx803) +elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia") + set(hip_archs 52) +endif() +set_property(TARGET HIPOnlyCompileFlags PROPERTY HIP_ARCHITECTURES ${hip_archs}) target_compile_options(HIPOnlyCompileFlags PRIVATE -DALWAYS_DEFINE) diff --git a/Tests/HIP/MathFunctions/CMakeLists.txt b/Tests/HIP/MathFunctions/CMakeLists.txt index 81e3ddb..7e768e8 100644 --- a/Tests/HIP/MathFunctions/CMakeLists.txt +++ b/Tests/HIP/MathFunctions/CMakeLists.txt @@ -14,5 +14,9 @@ project(MathFunctions HIP) # that hip needs that inject support for __half support # add_executable(HIPOnlyMathFunctions main.hip) -target_compile_options(HIPOnlyMathFunctions PRIVATE -Werror) +if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA") + target_compile_options(HIPOnlyMathFunctions PRIVATE "SHELL:-Werror all-warnings") +elseif(CMAKE_HIP_COMPILER_ID STREQUAL "Clang") + target_compile_options(HIPOnlyMathFunctions PRIVATE "-Werror") +endif() target_compile_features(HIPOnlyMathFunctions PRIVATE hip_std_14) diff --git a/Tests/HIP/MixedLanguage/CMakeLists.txt b/Tests/HIP/MixedLanguage/CMakeLists.txt index 4f6dd3b..af58ade 100644 --- a/Tests/HIP/MixedLanguage/CMakeLists.txt +++ b/Tests/HIP/MixedLanguage/CMakeLists.txt @@ -17,3 +17,7 @@ set_target_properties(MixedStaticLib PROPERTIES POSITION_INDEPENDENT_CODE ON) add_executable(HIPMixedLanguage main.cxx) target_link_libraries(HIPMixedLanguage PRIVATE MixedStaticLib MixedSharedLib) + +add_executable(HIPMixedLanguageCXX main.cxx) +target_link_libraries(HIPMixedLanguageCXX PRIVATE MixedStaticLib MixedSharedLib) +set_property(TARGET HIPMixedLanguageCXX PROPERTY LINKER_LANGUAGE CXX) diff --git a/Tests/HIP/TryCompile/CMakeLists.txt b/Tests/HIP/TryCompile/CMakeLists.txt index 92a834c..1022a58 100644 --- a/Tests/HIP/TryCompile/CMakeLists.txt +++ b/Tests/HIP/TryCompile/CMakeLists.txt @@ -4,7 +4,12 @@ project (TryCompile HIP) #Goal for this example: # Verify try_compile with HIP language works set(CMAKE_HIP_STANDARD 14) -set(CMAKE_HIP_ARCHITECTURES gfx803 gfx900) + +if(CMAKE_HIP_PLATFORM STREQUAL "amd") + set(CMAKE_HIP_ARCHITECTURES gfx803 gfx900) +elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia") + set(CMAKE_HIP_ARCHITECTURES 52) +endif() set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) try_compile(result "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/Tests/HIP/WithDefs/CMakeLists.txt b/Tests/HIP/WithDefs/CMakeLists.txt index 270f957..5602111 100644 --- a/Tests/HIP/WithDefs/CMakeLists.txt +++ b/Tests/HIP/WithDefs/CMakeLists.txt @@ -2,12 +2,11 @@ cmake_minimum_required(VERSION 3.18) project (WithDefs HIP) -set(CMAKE_HIP_ARCHITECTURES OFF) set(release_compile_defs DEFREL) #Goal for this example: -#build a executable that needs to be passed a complex define through add_definitions -#this verifies we can pass C++ style attributes to hipcc +#Build an executable that needs to be passed a complex define through add_definitions. +#Verify we can pass C++ style attributes to the HIP compiler. add_definitions("-DPACKED_DEFINE=[[gnu::packed]]") add_executable(HIPOnlyWithDefs main.hip.cpp) @@ -17,9 +16,8 @@ target_compile_features(HIPOnlyWithDefs PRIVATE hip_std_17) target_compile_options(HIPOnlyWithDefs PRIVATE - --offload-arch=gfx900 -DFLAG_COMPILE_LANG_$<COMPILE_LANGUAGE> - $<$<HIP_COMPILER_ID:Clang>:-DFLAG_LANG_IS_HIP=$<COMPILE_LANGUAGE:HIP>> # Host-only defines are possible only on NVCC. + -DFLAG_LANG_IS_HIP=$<COMPILE_LANGUAGE:HIP> ) target_compile_definitions(HIPOnlyWithDefs diff --git a/Tests/HIP/WithDefs/main.hip.cpp b/Tests/HIP/WithDefs/main.hip.cpp index a8f2d18..b69fa02 100644 --- a/Tests/HIP/WithDefs/main.hip.cpp +++ b/Tests/HIP/WithDefs/main.hip.cpp @@ -51,6 +51,10 @@ static __global__ void DetermineIfValidHIPDevice() # undef PACKED_DEFINE # define PACKED_DEFINE #endif +#ifdef __NVCC__ +# undef PACKED_DEFINE +# define PACKED_DEFINE +#endif struct PACKED_DEFINE result_type { bool valid; diff --git a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt index 0df05b9..0f2067a 100644 --- a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt +++ b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt @@ -55,7 +55,7 @@ endmacro() # detailed features tables, not just meta-features if (CMAKE_C_COMPILE_FEATURES) - if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$") + if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$") set(C_expected_features ${CMAKE_C_COMPILE_FEATURES}) list(FILTER C_expected_features EXCLUDE REGEX "^c_std_[0-9][0-9]") endif() @@ -98,7 +98,7 @@ if (C_expected_features) endif() if (CMAKE_CXX_COMPILE_FEATURES) - if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$") + if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$") set(CXX_expected_features ${CMAKE_CXX_COMPILE_FEATURES}) list(FILTER CXX_expected_features EXCLUDE REGEX "^cxx_std_[0-9][0-9]") endif() diff --git a/Tests/OutDir/OutDir.cmake b/Tests/OutDir/OutDir.cmake index 2a003b8..c42dc49 100644 --- a/Tests/OutDir/OutDir.cmake +++ b/Tests/OutDir/OutDir.cmake @@ -1,5 +1,5 @@ set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "") -set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ".so" ".sl" ".dylib" ".dll.a") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ".so" ".sl" ".dylib" ".dll.a" ".l") find_library(TESTC1_LIB NAMES testc1 testc1_test_debug_postfix diff --git a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake index 75130f2..9f65219 100644 --- a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake +++ b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake @@ -10,6 +10,9 @@ unset(RunCMake_TEST_OPTIONS) if(RunCMake_GENERATOR MATCHES "Watcom WMake|Borland Makefiles") return() endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "OrangeC") + return() +endif() if(RunCMake_GENERATOR MATCHES "Ninja|Visual Studio" AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(EXPORTS TRUE) diff --git a/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake new file mode 100644 index 0000000..3ee9be9 --- /dev/null +++ b/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake @@ -0,0 +1,15 @@ +include(MocExample.cmake) + +if(NOT TARGET Qt${with_qt_version}::moc) + message(FATAL_ERROR "Qt${with_qt_version}::moc not found") +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 $<$<CONFIG:Debug>:exe_debug.cpp> + $<$<CONFIG:Release>:exe_release.cpp> + $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp> +) + +set_target_properties(dummy PROPERTIES AUTOMOC_EXECUTABLE $<TARGET_FILE:mymoc>) diff --git a/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake new file mode 100644 index 0000000..0e46420 --- /dev/null +++ b/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake @@ -0,0 +1,15 @@ +include(RccExample.cmake) + +if(NOT TARGET Qt${with_qt_version}::rcc) + message(FATAL_ERROR "Qt${with_qt_version}::rcc not found") +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 $<$<CONFIG:Debug>:exe_debug.cpp> + $<$<CONFIG:Release>:exe_release.cpp> + $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp> +) + +set_target_properties(dummy PROPERTIES AUTORCC_EXECUTABLE $<TARGET_FILE:myrcc>) diff --git a/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake new file mode 100644 index 0000000..55b88b8 --- /dev/null +++ b/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake @@ -0,0 +1,15 @@ +include(UicExample.cmake) + +if(NOT TARGET Qt${with_qt_version}::uic) + message(FATAL_ERROR "Qt${with_qt_version}::uic not found") +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 $<$<CONFIG:Debug>:exe_debug.cpp> + $<$<CONFIG:Release>:exe_release.cpp> + $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp> +) + +set_target_properties(dummy PROPERTIES AUTOUIC_EXECUTABLE $<TARGET_FILE:myuic>) diff --git a/Tests/RunCMake/Autogen/MocExample.cmake b/Tests/RunCMake/Autogen/MocExample.cmake new file mode 100644 index 0000000..f06f8f6 --- /dev/null +++ b/Tests/RunCMake/Autogen/MocExample.cmake @@ -0,0 +1,11 @@ +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) +target_link_libraries(dummy Qt${with_qt_version}::Core + Qt${with_qt_version}::Widgets + Qt${with_qt_version}::Gui) + +set_target_properties(dummy PROPERTIES AUTOMOC ON) diff --git a/Tests/RunCMake/Autogen/MyWindow.cpp b/Tests/RunCMake/Autogen/MyWindow.cpp new file mode 100644 index 0000000..d87c2e9 --- /dev/null +++ b/Tests/RunCMake/Autogen/MyWindow.cpp @@ -0,0 +1,7 @@ +#include "MyWindow.h" + +MyWindow::MyWindow(QWidget* parent) + : QWidget(parent) +{ + this->m_ui.setupUi(this); +} diff --git a/Tests/RunCMake/Autogen/MyWindow.h b/Tests/RunCMake/Autogen/MyWindow.h new file mode 100644 index 0000000..c267610 --- /dev/null +++ b/Tests/RunCMake/Autogen/MyWindow.h @@ -0,0 +1,16 @@ +#pragma once + +#include <QWidget> + +#include "ui_MyWindow.h" + +class MyWindow : public QWidget +{ + Q_OBJECT + +public: + explicit MyWindow(QWidget* parent = nullptr); + +private: + Ui::MyWindow m_ui; +}; diff --git a/Tests/RunCMake/Autogen/MyWindow.ui b/Tests/RunCMake/Autogen/MyWindow.ui new file mode 100644 index 0000000..fbf294c --- /dev/null +++ b/Tests/RunCMake/Autogen/MyWindow.ui @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MyWindow</class> + <widget class="QWidget" name="MyWindow"/> +</ui> diff --git a/Tests/RunCMake/Ninja/QtAutoMocDeps-stderr.txt b/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt index 6024984..6024984 100644 --- a/Tests/RunCMake/Ninja/QtAutoMocDeps-stderr.txt +++ b/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt diff --git a/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake b/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake index c441169..fc3b2f2 100644 --- a/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake +++ b/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake @@ -1,27 +1,23 @@ enable_language(CXX) -set(QtX Qt${with_qt_version}) - -find_package(${QtX} REQUIRED COMPONENTS Core) +find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) set(CMAKE_AUTOMOC ON) add_library(simple_lib SHARED simple_lib.cpp) add_executable(app_with_qt app.cpp app_qt.cpp) -target_link_libraries(app_with_qt PRIVATE simple_lib ${QtX}::Core) +target_link_libraries(app_with_qt PRIVATE simple_lib Qt${with_qt_version}::Core) -if(${QtX}Widgets_DIR) - find_package(${QtX} REQUIRED COMPONENTS Widgets) +if(${with_qt_version}Widgets_DIR) if(with_qt_version STREQUAL 5) qt5_wrap_ui(_headers MyWindow.ui) else() qt_wrap_ui(_headers MyWindow.ui) endif() add_executable(app_with_widget app.cpp MyWindow.cpp ${_headers}) - target_link_libraries(app_with_widget PRIVATE ${QtX}::Widgets) + target_link_libraries(app_with_widget PRIVATE Qt${with_qt_version}::Widgets) target_include_directories(app_with_widget PRIVATE "${CMAKE_BINARY_DIR}") endif() - add_subdirectory(QtSubDir1) add_subdirectory(QtSubDir2) add_subdirectory(QtSubDir3) diff --git a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt index 3a12dcd..f215b9e 100644 --- a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt +++ b/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt @@ -1,4 +1,4 @@ cmake_policy(SET CMP0116 OLD) add_executable(sub_exe_1 ../app.cpp) -target_link_libraries(sub_exe_1 PRIVATE ${QtX}::Core) +target_link_libraries(sub_exe_1 PRIVATE Qt${with_qt_version}::Core) diff --git a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt index a2f77e4..cccb1e0 100644 --- a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt +++ b/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt @@ -1,4 +1,4 @@ cmake_policy(SET CMP0116 NEW) add_executable(sub_exe_2 ../app.cpp) -target_link_libraries(sub_exe_2 PRIVATE ${QtX}::Core) +target_link_libraries(sub_exe_2 PRIVATE Qt${with_qt_version}::Core) diff --git a/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt new file mode 100644 index 0000000..d268bfe --- /dev/null +++ b/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(sub_exe_3 ../app.cpp) +target_link_libraries(sub_exe_3 PRIVATE Qt${with_qt_version}::Core) diff --git a/Tests/RunCMake/Autogen/RccExample.cmake b/Tests/RunCMake/Autogen/RccExample.cmake new file mode 100644 index 0000000..4554eb0 --- /dev/null +++ b/Tests/RunCMake/Autogen/RccExample.cmake @@ -0,0 +1,11 @@ +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) +target_link_libraries(dummy Qt${with_qt_version}::Core + Qt${with_qt_version}::Widgets + Qt${with_qt_version}::Gui) + +set_target_properties(dummy PROPERTIES AUTORCC ON) diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake index 4fe9406..38987b9 100644 --- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake +++ b/Tests/RunCMake/Autogen/RunCMakeTest.cmake @@ -122,4 +122,336 @@ if (DEFINED with_qt_version) 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) + else() + set(config_list single-config) + endif() + foreach(config IN ITEMS ${config_list}) + block() + if (config STREQUAL "single-config") + set(config_suffix "") + else() + set(config_suffix "_${config}") + endif() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_suffix}-build) + run_cmake(QtAutoMocDeps) + 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() + 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) + 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) + 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}) + 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) + 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 "qrc_data.cpp|Auto${exe}") + set(not_expect_descripton "qrc_data.cpp_and_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() endif () diff --git a/Tests/RunCMake/Autogen/UicExample.cmake b/Tests/RunCMake/Autogen/UicExample.cmake new file mode 100644 index 0000000..4b1f8c1 --- /dev/null +++ b/Tests/RunCMake/Autogen/UicExample.cmake @@ -0,0 +1,11 @@ +enable_language(CXX) + +set(CMAKE_CXX_STANDARD 11) +find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui) + +add_library(dummy STATIC example_ui.cpp uiA.ui) +target_link_libraries(dummy Qt${with_qt_version}::Core + Qt${with_qt_version}::Widgets + Qt${with_qt_version}::Gui) + +set_target_properties(dummy PROPERTIES AUTOUIC ON) diff --git a/Tests/RunCMake/Autogen/app.cpp b/Tests/RunCMake/Autogen/app.cpp new file mode 100644 index 0000000..57380e4 --- /dev/null +++ b/Tests/RunCMake/Autogen/app.cpp @@ -0,0 +1,6 @@ +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + return 0; +} diff --git a/Tests/RunCMake/Autogen/app_qt.cpp b/Tests/RunCMake/Autogen/app_qt.cpp new file mode 100644 index 0000000..302c672 --- /dev/null +++ b/Tests/RunCMake/Autogen/app_qt.cpp @@ -0,0 +1,11 @@ +#include <QObject> + +class Mango : public QObject +{ + Q_OBJECT +public: +Q_SIGNALS: + void eatFruit(); +}; + +#include "app_qt.moc" diff --git a/Tests/RunCMake/Autogen/data.qrc b/Tests/RunCMake/Autogen/data.qrc new file mode 100644 index 0000000..9bd068c --- /dev/null +++ b/Tests/RunCMake/Autogen/data.qrc @@ -0,0 +1,4 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/res/affine"> +</qresource> +</RCC> diff --git a/Tests/RunCMake/Autogen/example.cpp b/Tests/RunCMake/Autogen/example.cpp new file mode 100644 index 0000000..7f1a781 --- /dev/null +++ b/Tests/RunCMake/Autogen/example.cpp @@ -0,0 +1,5 @@ +#include "example.h" + +Example::Example() +{ +} diff --git a/Tests/RunCMake/Autogen/example.h b/Tests/RunCMake/Autogen/example.h new file mode 100644 index 0000000..e8bfa42 --- /dev/null +++ b/Tests/RunCMake/Autogen/example.h @@ -0,0 +1,12 @@ +#ifndef EXAMPLE_H +#define EXAMPLE_H + +#include <QObject> + +class Example : public QObject +{ + Q_OBJECT + Example(); +}; + +#endif diff --git a/Tests/RunCMake/Autogen/example_ui.cpp b/Tests/RunCMake/Autogen/example_ui.cpp new file mode 100644 index 0000000..fb97c32 --- /dev/null +++ b/Tests/RunCMake/Autogen/example_ui.cpp @@ -0,0 +1,5 @@ +#include "example_ui.h" + +Example::Example() +{ +} diff --git a/Tests/RunCMake/Autogen/example_ui.h b/Tests/RunCMake/Autogen/example_ui.h new file mode 100644 index 0000000..d691133 --- /dev/null +++ b/Tests/RunCMake/Autogen/example_ui.h @@ -0,0 +1,14 @@ +#ifndef EXAMPLE_UI_H +#define EXAMPLE_UI_H + +#include <QObject> + +#include "ui_uiA.h" + +class Example : public QObject +{ + Q_OBJECT + Example(); +}; + +#endif diff --git a/Tests/RunCMake/Autogen/exe.cpp b/Tests/RunCMake/Autogen/exe.cpp new file mode 100644 index 0000000..f8b643a --- /dev/null +++ b/Tests/RunCMake/Autogen/exe.cpp @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/Tests/RunCMake/Autogen/exe_common.h b/Tests/RunCMake/Autogen/exe_common.h new file mode 100644 index 0000000..15311c6 --- /dev/null +++ b/Tests/RunCMake/Autogen/exe_common.h @@ -0,0 +1,48 @@ +#ifndef EXE_COMMON_H +#define EXE_COMMON_H + +#include <cstdlib> +#include <fstream> +#include <string> +#include <vector> + +inline int runRealExe(const int argc, char** argv) +{ + std::vector<std::string> args; + std::string realMocPath; + std::string const pathArg = "EXE_PATH="; + std::string cmd; + if (argc > 1) { + for (int i = 1; i < argc; ++i) { + std::string const arg = argv[i]; + if (arg.find(pathArg) != std::string::npos) { + realMocPath = arg.substr(pathArg.length()); + // if EXE_PATH contains spaces, wrap it in quotes + if (realMocPath.find(" ") != std::string::npos) { + realMocPath = "\"" + realMocPath + "\""; + } + } else { + args.push_back(arg); + } + } + } +#ifdef _WIN32 + cmd += "cmd /C \""; +#endif + cmd += realMocPath + " "; + for (auto arg : args) { + // if arg contains spaces, wrap it in quotes + if (arg.find(' ') != std::string::npos) { + cmd += " \"" + arg + "\""; + } else { + cmd += " " + arg; + } + } +#ifdef _WIN32 + cmd += "\""; +#endif + std::cout << "Running real exe:" << cmd << std::endl; + return std::system(cmd.c_str()); +} + +#endif diff --git a/Tests/RunCMake/Autogen/exe_debug.cpp b/Tests/RunCMake/Autogen/exe_debug.cpp new file mode 100644 index 0000000..ae5185b --- /dev/null +++ b/Tests/RunCMake/Autogen/exe_debug.cpp @@ -0,0 +1,10 @@ +#include <fstream> +#include <iostream> + +#include "exe_common.h" + +int main(int argc, char* argv[]) +{ + std::cout << "running_exe_Debug\n"; + return runRealExe(argc, argv); +} diff --git a/Tests/RunCMake/Autogen/exe_release.cpp b/Tests/RunCMake/Autogen/exe_release.cpp new file mode 100644 index 0000000..384c992 --- /dev/null +++ b/Tests/RunCMake/Autogen/exe_release.cpp @@ -0,0 +1,10 @@ +#include <fstream> +#include <iostream> + +#include "exe_common.h" + +int main(int argc, char* argv[]) +{ + std::cout << "running_exe_Release\n"; + return runRealExe(argc, argv); +} diff --git a/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp new file mode 100644 index 0000000..aa6c558 --- /dev/null +++ b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp @@ -0,0 +1,10 @@ +#include <fstream> +#include <iostream> + +#include "exe_common.h" + +int main(int argc, char* argv[]) +{ + std::cout << "running_exe_RelWithDebInfo\n"; + return runRealExe(argc, argv); +} diff --git a/Tests/RunCMake/Autogen/simple_lib.cpp b/Tests/RunCMake/Autogen/simple_lib.cpp new file mode 100644 index 0000000..cf8d689 --- /dev/null +++ b/Tests/RunCMake/Autogen/simple_lib.cpp @@ -0,0 +1,6 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + void dummy_symbol() +{ +} diff --git a/Tests/RunCMake/Autogen/uiA.ui b/Tests/RunCMake/Autogen/uiA.ui new file mode 100644 index 0000000..4c5762e --- /dev/null +++ b/Tests/RunCMake/Autogen/uiA.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UiA</class> + <widget class="QWidget" name="UiA"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt index 7230a07..07c17e2 100644 --- a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt +++ b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt @@ -6,7 +6,7 @@ Some library files are in directories implicitly searched by the linker when invoked for C: - .*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib) + .*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib|l) For compatibility with older versions of CMake, the generated link line will ask the linker to search for these by library name. diff --git a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake index 7395827..2576a28 100644 --- a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake @@ -8,10 +8,10 @@ function(run_CMP0119 status) endfunction() if(NOT RunCMake_GENERATOR MATCHES "Visual Studio|Xcode" AND - NOT CMAKE_C_COMPILER_ID MATCHES "(Borland|Embarcadero|Watcom)") + NOT CMAKE_C_COMPILER_ID MATCHES "(Borland|Embarcadero|Watcom|OrangeC)") run_CMP0119(WARN) run_CMP0119(OLD) endif() -if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI)")) +if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI|OrangeC)")) run_CMP0119(NEW) endif() diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index a4f6141..4387c5b 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -201,6 +201,7 @@ elseif(CMake_TEST_Qt5) endif() if(CMAKE_GENERATOR MATCHES "Ninja") set(Ninja_ARGS + -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX} -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}) @@ -242,6 +243,9 @@ if(CMAKE_GENERATOR MATCHES "Ninja") add_RunCMake_test(NinjaMultiConfig) set_property(TEST RunCMake.NinjaMultiConfig APPEND PROPERTY LABELS "CUDA") + add_RunCMake_test(NinjaPrivateDeps + -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION} + -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig}) endif() add_RunCMake_test(CTest) @@ -274,6 +278,7 @@ if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND) cmake_path(GET base_dir PARENT_PATH base_dir) # <base> 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=$<TARGET_FILE:pseudo_tidy> @@ -286,6 +291,7 @@ 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}" ) set(want_NoQt_test FALSE) @@ -393,6 +399,7 @@ add_RunCMake_test(GenEx-PATH) add_RunCMake_test(GenEx-PATH_EQUAL) add_RunCMake_test(GenEx-LIST) add_RunCMake_test(GeneratorExpression) +add_RunCMake_test(GeneratorExpressionShortCircuit) add_RunCMake_test(GeneratorInstance) add_RunCMake_test(GeneratorPlatform) if(XCODE_VERSION) @@ -466,6 +473,7 @@ add_RunCMake_test(build_command) add_executable(exit_code exit_code.c) set(execute_process_ARGS -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code> + -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin> -DPython_EXECUTABLE=${Python_EXECUTABLE} ) if(NOT CMake_TEST_EXTERNAL_CMAKE) @@ -473,6 +481,9 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE) endif() add_RunCMake_test(execute_process) add_RunCMake_test(export) +if(CMake_TEST_MSYSTEM_PREFIX) + list(APPEND cmake_host_system_information_ARGS -DCMake_TEST_MSYSTEM_PREFIX=${CMake_TEST_MSYSTEM_PREFIX}) +endif() add_RunCMake_test(cmake_host_system_information) add_RunCMake_test(cmake_language) add_RunCMake_test(cmake_minimum_required) @@ -514,10 +525,10 @@ foreach(var endforeach() add_RunCMake_test(file-DOWNLOAD) add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) -add_RunCMake_test(find_file) -add_RunCMake_test(find_library -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}) -add_RunCMake_test(find_package -DMSYS=${MSYS}) -add_RunCMake_test(find_path) +add_RunCMake_test(find_file -DMINGW=${MINGW}) +add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}) +add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS}) +add_RunCMake_test(find_path -DMINGW=${MINGW}) add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(foreach) add_RunCMake_test(function) @@ -634,8 +645,10 @@ else() message(STATUS "Could not find ctresalloc") endif() +get_filename_component(real_binary_dir "${CMake_BINARY_DIR}" REALPATH) if(NOT WIN32 AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS + AND real_binary_dir STREQUAL CMake_BINARY_DIR ) add_RunCMake_test(SymlinkTrees) endif () @@ -701,14 +714,15 @@ if(XCODE_VERSION) set_property(TEST RunCMake.XcodeProject PROPERTY TIMEOUT ${CMake_TEST_XcodeProject_TIMEOUT}) endif() -if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" +if((CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0) - add_RunCMake_test(Framework) - if(NOT DEFINED CMake_TEST_XcFramework) + OR (APPLE AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0)) + add_RunCMake_test(Framework -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}) + if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT DEFINED CMake_TEST_XcFramework) set(CMake_TEST_XcFramework ON) endif() if(CMake_TEST_XcFramework AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 11.0) - set(XcFramework_ARGS -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) + set(XcFramework_ARGS -DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION}) add_RunCMake_test(XcFramework) # This test can take a very long time due to lots of combinations. diff --git a/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake b/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake index d9e399f..df58e72 100644 --- a/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake +++ b/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake @@ -1,5 +1,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake) get_filename_component(_parent "${CMAKE_SOURCE_DIR}" DIRECTORY) -file(REAL_PATH "${_parent}" _parent) test_variable(CMAKE_BINARY_DIR "" "${_parent}/GoodNoSCachePrep-build") diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index c4a8b3f..88027fb 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake @@ -153,6 +153,13 @@ run_cmake_presets(ConditionFuture) run_cmake_presets(SubConditionNull) run_cmake_presets(TraceNotSupported) +set(CMakePresets_NO_PRESET 1) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +run_cmake_presets(SchemaSupported --list-presets) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) +run_cmake_presets(SchemaNotSupported --list-presets) +unset(CMakePresets_NO_PRESET) + # Test cmakeMinimumRequired field run_cmake_presets(MinimumRequiredInvalid) set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) diff --git a/Tests/RunCMake/CMakePresets/SchemaNotSupported-result.txt b/Tests/RunCMake/CMakePresets/SchemaNotSupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMakePresets/SchemaNotSupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt new file mode 100644 index 0000000..2df4b3d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt @@ -0,0 +1,3 @@ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/SchemaNotSupported: +File version must be 8 or higher for [$]schema support$ diff --git a/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in b/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in new file mode 100644 index 0000000..736f307 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in @@ -0,0 +1,4 @@ +{ + "version": 7, + "$schema": "https://example.com/schema.json" +} diff --git a/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt b/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMakePresets/SchemaSupported.json.in b/Tests/RunCMake/CMakePresets/SchemaSupported.json.in new file mode 100644 index 0000000..5426131 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/SchemaSupported.json.in @@ -0,0 +1,4 @@ +{ + "version": 8, + "$schema": "https://example.com/schema.json" +} diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake index c90d543..223a61c 100644 --- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake @@ -472,7 +472,7 @@ add_test(test1 \"${CMAKE_COMMAND}\" -E false) add_test(test2 \"${CMAKE_COMMAND}\" -E echo \"hello world\") add_test(test3 \"${CMAKE_COMMAND}\" -E true) set_tests_properties(test3 PROPERTIES DISABLED \"ON\") -add_test(test4 \"${CMAKE_COMMAND}/doesnt_exist\") +add_test(test4 \"${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist\") add_test(test5 \"${CMAKE_COMMAND}\" -E echo \"please skip\") set_tests_properties(test5 PROPERTIES SKIP_REGULAR_EXPRESSION \"please skip\") ") diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt index ce30dc8..c57c378 100644 --- a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt @@ -1 +1,2 @@ -Unable to find executable: .*doesnt_exist +Unable to find executable:[^ +]*does_not_exist diff --git a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt index 6c42612..dbadd048 100644 --- a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt +++ b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt @@ -1,5 +1,7 @@ -^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\): +^CMake Error at .*/Internal/CMakeCUDAArchitecturesValidate\.cmake:[0-9]+ \(message\): CMAKE_CUDA_ARCHITECTURES must be non-empty if set\. Call Stack \(most recent call first\): + [^ +]*/Modules/CMakeDetermineCUDACompiler.cmake:[0-9]+ \(cmake_cuda_architectures_validate\) architectures-empty\.cmake:2 \(enable_language\) CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt index 14c76d2..891aa40 100644 --- a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt +++ b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt @@ -1,4 +1,4 @@ -^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\): +^CMake Error at .*/Internal/CMakeCUDAArchitecturesValidate\.cmake:[0-9]+ \(message\): CMAKE_CUDA_ARCHITECTURES: invalid @@ -10,5 +10,7 @@ \* a special value: all, all-major, native Call Stack \(most recent call first\): + [^ +]*/Modules/CMakeDetermineCUDACompiler.cmake:[0-9]+ \(cmake_cuda_architectures_validate\) architectures-invalid\.cmake:2 \(enable_language\) CMakeLists\.txt:3 \(include\)$ diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt index a82791b..49e869a 100644 --- a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt +++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at ExportBuildCxxModules.cmake:5 \(target_sources\): +CMake Warning \(dev\) at ExportBuildCxxModules.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt index db02227..4a6969d 100644 --- a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt +++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at ExportInstallCxxModules.cmake:5 \(target_sources\): +CMake Warning \(dev\) at ExportInstallCxxModules.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt index dfcdbec..cb21e2c 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt @@ -1,12 +1,12 @@ -CMake Warning \(dev\) at FileSetModulesInterface.cmake:2 \(target_sources\): +CMake Warning \(dev\) at FileSetModulesInterface.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. -CMake Error at FileSetModulesInterface.cmake:2 \(target_sources\): +CMake Error at FileSetModulesInterface.cmake:[0-9]+ \(target_sources\): target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE" visibility Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt index 4420bbc..aae763b 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at FileSetModulesInterfaceImported.cmake:2 \(target_sources\): +CMake Warning \(dev\) at FileSetModulesInterfaceImported.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt index 0c82ccc..a6e778a 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at FileSetModulesPrivate.cmake:5 \(target_sources\): +CMake Warning \(dev\) at FileSetModulesPrivate.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt index a27a28e..7fa8aa4 100644 --- a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt +++ b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at FileSetModulesPublic.cmake:5 \(target_sources\): +CMake Warning \(dev\) at FileSetModulesPublic.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt index fc3c7db..0f50963 100644 --- a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt +++ b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at InstallBMI.cmake:8 \(install\): +CMake Warning \(dev\) at InstallBMI.cmake:[0-9]+ \(install\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt index 44c961f..52b9c35 100644 --- a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt +++ b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at InstallBMIGenericArgs.cmake:8 \(install\): +CMake Warning \(dev\) at InstallBMIGenericArgs.cmake:[0-9]+ \(install\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt index d9d2c2d..bfff34c 100644 --- a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt +++ b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at InstallBMIIgnore.cmake:5 \(install\): +CMake Warning \(dev\) at InstallBMIIgnore.cmake:[0-9]+ \(install\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake index 0d08c44..6de2e1e 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake @@ -2,19 +2,19 @@ include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) set(have_file 0) - foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) - if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json") + foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${CXXModules_config}/CXXDependInfo.json") continue () endif () set(have_file 1) - set(CMAKE_BUILD_TYPE "${config}") + set(CMAKE_BUILD_TYPE "${CXXModules_config}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") endforeach () @@ -24,6 +24,9 @@ if (RunCMake_GENERATOR_IS_MULTI_CONFIG) "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-bmi-install-public.dir/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") @@ -32,3 +35,5 @@ else () file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-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/NinjaDependInfoBMIInstall-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt index 9a7c1f9..e5bd1ec 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at NinjaDependInfoBMIInstall.cmake:13 \(target_sources\): +CMake Warning \(dev\) at NinjaDependInfoBMIInstall.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake index 7720257..0c933c9 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake @@ -2,19 +2,19 @@ include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) set(have_file 0) - foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) - if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json") + foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${CXXModules_config}/CXXDependInfo.json") continue () endif () set(have_file 1) - set(CMAKE_BUILD_TYPE "${config}") + set(CMAKE_BUILD_TYPE "${CXXModules_config}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") endforeach () @@ -24,6 +24,9 @@ if (RunCMake_GENERATOR_IS_MULTI_CONFIG) "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-exports-public.dir/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") @@ -32,3 +35,5 @@ else () file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-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/NinjaDependInfoExport-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt index b66005b..ce0e55a 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at NinjaDependInfoExport.cmake:13 \(target_sources\): +CMake Warning \(dev\) at NinjaDependInfoExport.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake index b9a1315..4eaa891 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake @@ -2,19 +2,19 @@ include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) set(have_file 0) - foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) - if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json") + foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel) + if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${CXXModules_config}/CXXDependInfo.json") continue () endif () set(have_file 1) - set(CMAKE_BUILD_TYPE "${config}") + set(CMAKE_BUILD_TYPE "${CXXModules_config}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") - file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${config}/CXXDependInfo.json" actual_contents) + file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") endforeach () @@ -24,6 +24,9 @@ if (RunCMake_GENERATOR_IS_MULTI_CONFIG) "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-file-sets-public.dir/CXXDependInfo.json" actual_contents) file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents) check_json("${actual_contents}" "${expect_contents}") @@ -32,3 +35,5 @@ else () file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-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/NinjaDependInfoFileSet-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt index 949b7af..055e741 100644 --- a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt +++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt @@ -1,6 +1,6 @@ -CMake Warning \(dev\) at NinjaDependInfoFileSet.cmake:13 \(target_sources\): +CMake Warning \(dev\) at NinjaDependInfoFileSet.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt index aa7f406..c261e8c 100644 --- a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt +++ b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt @@ -1,20 +1,25 @@ -CMake Warning \(dev\) at NoCXX.cmake:4 \(target_sources\): +CMake Warning \(dev\) at NoCXX.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. CMake Error in CMakeLists.txt: - The "nocxx" target has C\+\+ module sources but the "CXX" language has not - been enabled + The target named "nocxx" has C\+\+ sources that export modules but the "CXX" + language has not been enabled ( CMake Error in CMakeLists.txt: -( The "nocxx" target has C\+\+ module sources but the "CXX" language has not - been enabled -| The "nocxx" target contains C\+\+ module sources which are not supported by - the generator +( The target named "nocxx" has C\+\+ sources that export modules but the "CXX" + language has not been enabled +| The target named "nocxx" contains C\+\+ sources that export modules which is + not supported by the generator +| Target "nocxx" has source file + + .*/Tests/RunCMake/CXXModules/sources/module.cxx + + in a "FILE_SET TYPE CXX_MODULES" but it is not scheduled for compilation. ) )* CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt index 95d73b1..aba0c89 100644 --- a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt +++ b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt @@ -1,20 +1,22 @@ -CMake Warning \(dev\) at NoCXX20.cmake:4 \(target_sources\): +CMake Warning \(dev\) at NoCXX20.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. CMake Error in CMakeLists.txt: - The "nocxx20" target has C\+\+ module sources but is not using at least - "cxx_std_20" + The target named "nocxx20" has C\+\+ sources that export modules but does not + include "cxx_std_20" \(or newer\) among its `target_compile_features`; found + "cxx_std_17" ( CMake Error in CMakeLists.txt: -( The "nocxx20" target has C\+\+ module sources but is not using at least - "cxx_std_20" -| The "nocxx20" target contains C\+\+ module sources which are not supported by - the generator +( The target named "nocxx20" has C\+\+ sources that export modules but does not + include "cxx_std_20" \(or newer\) among its `target_compile_features`; found + "cxx_std_17" +| The target named "nocxx20" contains C\+\+ sources that export modules which + is not supported by the generator ) )* CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt index aa99af0..06174b5 100644 --- a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt +++ b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt @@ -1,20 +1,20 @@ -CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:6 \(target_sources\): +CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. CMake Error in CMakeLists.txt: - The "noexperimentalflag" target has C\+\+ module sources but its experimental - support has not been requested + The target named "noexperimentalflag" has C\+\+ sources that export modules + but its experimental support has not been requested ( CMake Error in CMakeLists.txt: -( The "noexperimentalflag" target has C\+\+ module sources but its experimental - support has not been requested -| The "noexperimentalflag" target contains C\+\+ module sources which are not - supported by the generator +( The target named "noexperimentalflag" has C\+\+ sources that export modules + but its experimental support has not been requested +| The target named "noexperimentalflag" contains C\+\+ sources that export + modules which is not supported by the generator ) )* CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt index b63d291..8d15c19 100644 --- a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt +++ b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt @@ -1,8 +1,8 @@ -CMake Warning \(dev\) at NoDyndepSupport.cmake:9 \(target_sources\): +CMake Warning \(dev\) at NoDyndepSupport.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. (CMake Error: @@ -13,13 +13,13 @@ This warning is for project developers. Use -Wno-dev to suppress it. due to lack of required features. Ninja 1.11 or higher is required. |CMake Error in CMakeLists.txt: - The "nodyndep" target contains C\+\+ module sources which are not supported - by the generator + The target named "nodyndep" contains C\+\+ sources that export modules which + is not supported by the generator ( CMake Error in CMakeLists.txt: - The "nodyndep" target contains C\+\+ module sources which are not supported - by the generator + The target named "nodyndep" contains C\+\+ sources that export modules which + is not supported by the generator )*) CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt index 78d3dc6..d73ff98 100644 --- a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt +++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt @@ -1,8 +1,8 @@ -CMake Warning \(dev\) at NotCXXSourceModules.cmake:6 \(target_sources\): +CMake Warning \(dev\) at NotCXXSourceModules.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - CMakeLists.txt:6 \(include\) + CMakeLists.txt:[0-9]+ \(include\) This warning is for project developers. Use -Wno-dev to suppress it. CMake Error in CMakeLists.txt: diff --git a/Tests/RunCMake/CXXModules/NotCompiledSourceModules-result.txt b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt new file mode 100644 index 0000000..c963ce7 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt @@ -0,0 +1,16 @@ +CMake Warning \(dev\) at NotCompiledSourceModules.cmake:[0-9]+ \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) +This warning is for project developers. Use -Wno-dev to suppress it. + +(CMake Error in CMakeLists.txt: + Target "not-cxx-source" has source file + + .*/Tests/RunCMake/CXXModules/sources/not-compiled.txt + + in a "FILE_SET TYPE CXX_MODULES" but it is not scheduled for compilation. + +)+ +CMake Generate step failed. Build files cannot be regenerated correctly. diff --git a/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake new file mode 100644 index 0000000..0bab635 --- /dev/null +++ b/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake @@ -0,0 +1,13 @@ +enable_language(CXX) +set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "") + +add_library(not-cxx-source) +target_sources(not-cxx-source + PRIVATE + sources/cxx-anchor.cxx + PUBLIC + FILE_SET fs TYPE CXX_MODULES FILES + sources/not-compiled.txt) +target_compile_features(not-cxx-source + PRIVATE + cxx_std_20) diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake index 0ca9945..f111b74 100644 --- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake +++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake @@ -71,8 +71,9 @@ foreach (fileset_type IN LISTS fileset_types) endforeach () run_cmake("FileSet${fileset_type}InterfaceImported") - # Test the error message when a non-C++ source file is found in the source + # Test the error messages when a non-C++ source file is found in the source # list. + run_cmake("NotCompiledSource${fileset_type}") run_cmake("NotCXXSource${fileset_type}") endforeach () @@ -187,6 +188,7 @@ endif () if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-interface-no-properties-build) run_cxx_module_test(export-interface-build) + run_cxx_module_test(export-include-directories-build) run_cxx_module_test(export-usage-build) run_cxx_module_test(export-bmi-and-interface-build) @@ -198,6 +200,9 @@ if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) 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) endif () @@ -214,6 +219,7 @@ if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) run_cxx_module_test(export-interface-no-properties-install) run_cxx_module_test(export-interface-install) + run_cxx_module_test(export-include-directories-install) run_cxx_module_test(export-usage-install) run_cxx_module_test(export-bmi-and-interface-install) @@ -226,6 +232,9 @@ if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION) 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(RunCMake_CXXModules_INSTALL 1) diff --git a/Tests/RunCMake/CXXModules/check-json.cmake b/Tests/RunCMake/CXXModules/check-json.cmake index 19d0c8a..bb04b36 100644 --- a/Tests/RunCMake/CXXModules/check-json.cmake +++ b/Tests/RunCMake/CXXModules/check-json.cmake @@ -2,19 +2,20 @@ cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) function (json_placeholders in out) - string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" in "${in}") + string(REPLACE "<CONFIG>" "${CXXModules_config}" in "${in}") if (RunCMake_GENERATOR_IS_MULTI_CONFIG) - string(REPLACE "<CONFIG_DIR>" "${CMAKE_BUILD_TYPE}/" in "${in}") + string(REPLACE "<CONFIG_DIR>" "/${CXXModules_config}" in "${in}") else () string(REPLACE "<CONFIG_DIR>" "" in "${in}") endif () if (CMAKE_BUILD_TYPE) - string(REPLACE "<CONFIG_FORCE>" "${CMAKE_BUILD_TYPE}" in "${in}") + string(REPLACE "<CONFIG_FORCE>" "${CXXModules_config}" in "${in}") else () string(REPLACE "<CONFIG_FORCE>" "noconfig" in "${in}") endif () string(REPLACE "<SOURCE_DIR>" "${RunCMake_SOURCE_DIR}" in "${in}") string(REPLACE "<BINARY_DIR>" "${RunCMake_TEST_BINARY_DIR}" in "${in}") + string(REPLACE "<OBJEXT>" "${CMAKE_CXX_OUTPUT_EXTENSION}" in "${in}") set("${out}" "${in}" PARENT_SCOPE) endfunction () @@ -22,6 +23,7 @@ function (check_json_value path actual_type expect_type actual_value expect_valu if (NOT actual_type STREQUAL expect_type) list(APPEND RunCMake_TEST_FAILED "Type mismatch at ${path}: ${actual_type} vs. ${expect_type}") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) return () endif () @@ -53,6 +55,8 @@ function (check_json_value path actual_type expect_type actual_value expect_valu elseif (actual_type STREQUAL OBJECT) check_json_object("${path}" "${actual_value}" "${expect_value}") endif () + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) endfunction () # Check that two arrays are the same. @@ -82,6 +86,8 @@ function (check_json_array path actual expect) string(JSON expect_value GET "${expect}" "${idx}") check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}") endforeach () + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) endfunction () # Check that two inner objects are the same. @@ -131,12 +137,12 @@ function (check_json_object path actual expect) if (actual_keys_missed) string(REPLACE ";" ", " actual_keys_missed_text "${actual_keys_missed}") list(APPEND RunCMake_TEST_FAILED - "Missing expected members at ${path}: ${actual_keys_missed_text}") + "Extra unexpected members at ${path}: ${actual_keys_missed_text}") endif () if (expect_keys_missed) string(REPLACE ";" ", " expect_keys_missed_text "${expect_keys_missed}") list(APPEND RunCMake_TEST_FAILED - "Extra unexpected members at ${path}: ${expect_keys_missed_text}") + "Missing expected members at ${path}: ${expect_keys_missed_text}") endif () foreach (key IN LISTS common_keys) @@ -148,13 +154,15 @@ function (check_json_object path actual expect) string(JSON expect_value GET "${expect}" "${key}") check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}") endforeach () + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) endfunction () # Check that two JSON objects are the same. function (check_json actual expect) check_json_object("" "${actual}" "${expect}") -endfunction () -string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction () cmake_policy(POP) diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/compiler_introspection.cmake index 0e61383..8c74940 100644 --- a/Tests/RunCMake/CXXModules/compiler_introspection.cmake +++ b/Tests/RunCMake/CXXModules/compiler_introspection.cmake @@ -21,6 +21,8 @@ set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\") set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\") set(forced_cxx_standard \"${forced_cxx_standard}\") set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\") +set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\") +set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\") ") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}") diff --git a/Tests/RunCMake/CXXModules/examples/circular-stderr.txt b/Tests/RunCMake/CXXModules/examples/circular-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/circular-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/circular-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt index 659414d..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:15 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt b/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt index e868787..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:9 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-build-stderr.txt new file mode 100644 index 0000000..28a7b1f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) at CMakeLists.txt:[0-9] \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt new file mode 100644 index 0000000..bc2ae7f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_include_directories CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_include_directories STATIC + include/include.h) +target_sources(export_include_directories + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx + subdir/importable.cxx + ) +target_compile_features(export_include_directories PUBLIC cxx_std_20) +target_include_directories(export_include_directories + PRIVATE + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>") + +add_library(no_modules STATIC no_modules.cxx) + +install(TARGETS export_include_directories no_modules + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +export(EXPORT CXXModules + NAMESPACE CXXModules:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-targets.cmake" + CXX_MODULES_DIRECTORY "export_include_directories-cxx-modules") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_include_directories-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_include_directories_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_CURRENT_SOURCE_DIR}" + "-Dexpected_binary_dir=${CMAKE_CURRENT_BINARY_DIR}" + "-Dexport_include_directories_DIR=${CMAKE_CURRENT_BINARY_DIR}" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx new file mode 100644 index 0000000..6a1d83e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx @@ -0,0 +1,18 @@ +module; + +#include "include/include.h" + +#ifndef include_h_included +# error "include define not found" +#endif + +export module importable; + +extern "C++" { +int forwarding(); +} + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h new file mode 100644 index 0000000..e3eee34 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h @@ -0,0 +1,3 @@ +#pragma once + +#define include_h_included diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx new file mode 100644 index 0000000..eea854f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx @@ -0,0 +1,3 @@ +void no_modules() +{ +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx new file mode 100644 index 0000000..07d6af6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx @@ -0,0 +1,6 @@ +export module subdir_importable; + +export int from_subdir() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt new file mode 100644 index 0000000..f5bdfd4 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") + +find_package(export_include_directories REQUIRED) + +if (NOT TARGET CXXModules::export_include_directories) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(include_directories TARGET CXXModules::export_include_directories + PROPERTY IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES) +foreach (include_directory IN LISTS include_directories) + if (NOT EXISTS "${include_directory}") + message(FATAL_ERROR + "Missing include directory in C++ module interface CXXModules::export_include_directories:\n ${include_directory}") + endif () +endforeach () diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-install-stderr.txt new file mode 100644 index 0000000..be89b8c --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt new file mode 100644 index 0000000..444882d --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_export_include_directories CXX) + +include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake") + +add_library(export_include_directories STATIC + include/include.h) +target_sources(export_include_directories + PRIVATE + forward.cxx + PRIVATE + FILE_SET modules_private TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + private.cxx + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}" + FILES + importable.cxx + subdir/importable.cxx + ) +target_compile_features(export_include_directories PUBLIC cxx_std_20) +target_include_directories(export_include_directories + PRIVATE + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" + "$<INSTALL_INTERFACE:include>") + +add_library(no_modules STATIC no_modules.cxx) + +install(TARGETS export_include_directories no_modules + EXPORT CXXModules + FILE_SET modules DESTINATION "lib/cxx/miu") +install(DIRECTORY include + DESTINATION "include") +install(EXPORT CXXModules + NAMESPACE CXXModules:: + DESTINATION "lib/cmake/export_include_directories" + FILE "export_include_directories-targets.cmake" + CXX_MODULES_DIRECTORY "export_include_directories-cxx-modules") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_include_directories-targets.cmake\") +set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1) +") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake" + DESTINATION "lib/cmake/export_include_directories") + +set(generator + -G "${CMAKE_GENERATOR}") +if (CMAKE_GENERATOR_TOOLSET) + list(APPEND generator + -T "${CMAKE_GENERATOR_TOOLSET}") +endif () +if (CMAKE_GENERATOR_PLATFORM) + list(APPEND generator + -A "${CMAKE_GENERATOR_PLATFORM}") +endif () + +add_test(NAME export_include_directories_build + COMMAND + "${CMAKE_COMMAND}" + "-Dexpected_source_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu" + "-Dexpected_binary_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/bmi" + "-Dexport_include_directories_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_include_directories" + ${generator} + -S "${CMAKE_CURRENT_SOURCE_DIR}/test" + -B "${CMAKE_CURRENT_BINARY_DIR}/test") diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx new file mode 100644 index 0000000..7f53271 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx @@ -0,0 +1,6 @@ +import priv; + +int forwarding() +{ + return from_private(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx new file mode 100644 index 0000000..6a1d83e --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx @@ -0,0 +1,18 @@ +module; + +#include "include/include.h" + +#ifndef include_h_included +# error "include define not found" +#endif + +export module importable; + +extern "C++" { +int forwarding(); +} + +export int from_import() +{ + return forwarding(); +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h new file mode 100644 index 0000000..e3eee34 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h @@ -0,0 +1,3 @@ +#pragma once + +#define include_h_included diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx new file mode 100644 index 0000000..eea854f --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx @@ -0,0 +1,3 @@ +void no_modules() +{ +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx new file mode 100644 index 0000000..c5b719a --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx @@ -0,0 +1,6 @@ +export module priv; + +export int from_private() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx new file mode 100644 index 0000000..07d6af6 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx @@ -0,0 +1,6 @@ +export module subdir_importable; + +export int from_subdir() +{ + return 0; +} diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt new file mode 100644 index 0000000..9cdb7ff --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.24) +project(cxx_modules_library NONE) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "ac01f462-0f5f-432a-86aa-acef252918a6") + +find_package(export_include_directories REQUIRED) + +if (NOT TARGET CXXModules::export_include_directories) + message(FATAL_ERROR + "Missing imported target") +endif () + +get_property(file_sets TARGET CXXModules::export_include_directories + PROPERTY INTERFACE_CXX_MODULE_SETS) +if (NOT file_sets STREQUAL "modules") + message(FATAL_ERROR + "Incorrect exported file sets in CXXModules::export_include_directories:\n ${file_sets}") +endif () diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/export-usage-build-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/export-usage-install-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt index 06160ce..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:16 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt index 71ee795..e2970ec 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-build-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-build/export_bmi_and_interfaces-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt index d22b2a1..ec5dd1f 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-bmi-and-interface-install-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-install/lib/cmake/export_bmi_and_interfaces/export_bmi_and_interfaces-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-build-stderr.txt new file mode 100644 index 0000000..4ea3679 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-build-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-include-directories-build-build/export_include_directories-targets.cmake:[0-9]+ \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-include-directories-build-build/export_include_directories-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-install-stderr.txt new file mode 100644 index 0000000..50ad6b9 --- /dev/null +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-include-directories-install-stderr.txt @@ -0,0 +1,7 @@ +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-include-directories-install-install/lib/cmake/export_include_directories/export_include_directories-targets.cmake:[0-9]+ \(target_sources\): + CMake's C\+\+ module support is experimental. It is meant only for + experimentation and feedback to CMake developers. +Call Stack \(most recent call first\): + .*/Tests/RunCMake/CXXModules/examples/export-include-directories-install-install/lib/cmake/export_include_directories/export_include_directories-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt index f79abbc..10404c6 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-build-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-interface-build-build/export_interfaces-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt index 32f9452..04124cd 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-install-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-interface-install-install/lib/cmake/export_interfaces/export_interfaces-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt index 9254936..dcf6498 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-build-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-build/export_interfaces_no_properties-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt index 71269f4..7073b4a 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules-export-interface-no-properties-install-stderr.txt @@ -1,7 +1,7 @@ -CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-targets.cmake:[0-9]* \(target_sources\): +CMake Warning \(dev\) at .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-targets.cmake:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. Call Stack \(most recent call first\): - .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-config.cmake:1 \(include\) - CMakeLists.txt:15 \(find_package\) + .*/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-install/lib/cmake/export_interfaces_no_properties/export_interfaces_no_properties-config.cmake:[0-9]+ \(include\) + CMakeLists.txt:[0-9]+ \(find_package\) This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt index 3e6f379..946792c 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt +++ b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt @@ -7,6 +7,8 @@ if (NO_PROPERTIES) set(package_name "export_interfaces_no_properties") elseif (WITH_BMIS) set(package_name "export_bmi_and_interfaces") +elseif (INCLUDE_PROPERTIES) + set(package_name "export_include_directories") else () set(package_name "export_interfaces") endif () diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx index feb38d2..2da1913 100644 --- a/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx +++ b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx @@ -1,3 +1,9 @@ +#if defined(__has_include) +# if __has_include(<include/include.h>) +# error "include directories leaked from private module requirements" +# endif +#endif + import importable; int main(int argc, char* argv[]) diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt index 79c5637..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt index 79c5637..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt index 79c5637..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt b/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt index 4709399..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:[0-9]* \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt index 79c5637..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt b/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt b/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt b/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt index 34f3f85..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:25 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt index 78bdf2b..be89b8c 100644 --- a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/try-compile-stderr.txt b/Tests/RunCMake/CXXModules/examples/try-compile-stderr.txt index 571bb9c..a121e86 100644 --- a/Tests/RunCMake/CXXModules/examples/try-compile-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/try-compile-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:[0-9]* \(try_compile\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(try_compile\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/examples/try-run-stderr.txt b/Tests/RunCMake/CXXModules/examples/try-run-stderr.txt index 508db55..8eea7a6 100644 --- a/Tests/RunCMake/CXXModules/examples/try-run-stderr.txt +++ b/Tests/RunCMake/CXXModules/examples/try-run-stderr.txt @@ -1,4 +1,4 @@ -CMake Warning \(dev\) at CMakeLists.txt:[0-9]* \(try_run\): +CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(try_run\): CMake's C\+\+ module support is experimental. It is meant only for experimentation and feedback to CMake developers. This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json index 65f0759..45b0396 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json @@ -6,9 +6,12 @@ "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake" }, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-bmi-install-private.dir/sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "internal_partitions", "relative-directory": "sources", @@ -16,7 +19,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-bmi-install-private.dir/sources/module-part.cxx.o": { + "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -24,7 +28,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-bmi-install-private.dir/sources/module.cxx.o": { + "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -41,5 +46,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json index 9c8a895..30b55e3 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json @@ -3,12 +3,15 @@ "destination": "lib/cxx/modules/<CONFIG>", "message-level": "", "permissions": "", - "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-noconfig.cmake" + "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake" }, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-bmi-install-public.dir/sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx/internals", "name": "internal_partitions", "relative-directory": "sources", @@ -16,7 +19,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-bmi-install-public.dir/sources/module-part.cxx.o": { + "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -24,7 +28,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-bmi-install-public.dir/sources/module.cxx.o": { + "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -41,5 +46,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json index 0545981..f06a846 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json @@ -1,9 +1,12 @@ { "bmi-installation": null, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-exports-private.dir/sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "internal_partitions", "relative-directory": "sources", @@ -11,7 +14,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-exports-private.dir/sources/module-part.cxx.o": { + "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -19,7 +23,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-exports-private.dir/sources/module.cxx.o": { + "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -69,5 +74,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json index adc3ae3..938481c 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json @@ -1,9 +1,12 @@ { "bmi-installation": null, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-exports-public.dir/sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx/internals", "name": "internal_partitions", "relative-directory": "sources", @@ -11,7 +14,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-exports-public.dir/sources/module-part.cxx.o": { + "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -19,7 +23,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-exports-public.dir/sources/module.cxx.o": { + "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -69,5 +74,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json index 9ba6568..3a66a94 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json @@ -1,9 +1,12 @@ { "bmi-installation": null, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-file-sets-private.dir/sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "internal_partitions", "relative-directory": "sources", @@ -11,7 +14,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-file-sets-private.dir/sources/module-part.cxx.o": { + "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -19,7 +23,8 @@ "type": "CXX_MODULES", "visibility": "PRIVATE" }, - "CMakeFiles/ninja-file-sets-private.dir/sources/module.cxx.o": { + "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": null, "name": "modules", "relative-directory": "", @@ -36,5 +41,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json index 46e2cbf..ac06c0f 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json @@ -1,9 +1,12 @@ { "bmi-installation": null, "compiler-id": "<IGNORE>", + "compiler-frontend-variant": "<IGNORE>", + "compiler-simulate-id": "<IGNORE>", "config": "<CONFIG>", "cxx-modules": { - "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-internal-part.cxx.o": { + "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx/internals", "name": "internal_partitions", "relative-directory": "sources", @@ -11,7 +14,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-part.cxx.o": { + "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -19,7 +23,8 @@ "type": "CXX_MODULES", "visibility": "PUBLIC" }, - "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module.cxx.o": { + "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": { + "bmi-only": false, "destination": "lib/cxx", "name": "modules", "relative-directory": "", @@ -36,5 +41,5 @@ "include-dirs": [], "language": "CXX", "linked-target-dirs": [], - "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir" + "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>" } diff --git a/Tests/RunCMake/CXXModules/sources/not-compiled.txt b/Tests/RunCMake/CXXModules/sources/not-compiled.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/CXXModules/sources/not-compiled.txt diff --git a/Tests/RunCMake/CommandLine/C-no-file-stderr.txt b/Tests/RunCMake/CommandLine/C-no-file-stderr.txt index b65a349..5d3d4b3 100644 --- a/Tests/RunCMake/CommandLine/C-no-file-stderr.txt +++ b/Tests/RunCMake/CommandLine/C-no-file-stderr.txt @@ -1,3 +1,3 @@ -^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine/C-no-file-build/nosuchcachefile.txt +^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine/C-no-file-build/nosuchcachefile.txt CMake Error: The source directory ".*/Tests/RunCMake/CommandLine/C-no-file-build" does not appear to contain CMakeLists.txt. Specify --help for usage, or press the help button on the CMake GUI.$ diff --git a/Tests/RunCMake/CommandLine/Cno-file-stderr.txt b/Tests/RunCMake/CommandLine/Cno-file-stderr.txt index 416686c..4024499 100644 --- a/Tests/RunCMake/CommandLine/Cno-file-stderr.txt +++ b/Tests/RunCMake/CommandLine/Cno-file-stderr.txt @@ -1,3 +1,3 @@ -^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine/Cno-file-build/nosuchcachefile.txt +^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine/Cno-file-build/nosuchcachefile.txt CMake Error: The source directory ".*/Tests/RunCMake/CommandLine/Cno-file-build" does not appear to contain CMakeLists.txt. Specify --help for usage, or press the help button on the CMake GUI.$ diff --git a/Tests/RunCMake/CommandLine/P_directory-stderr.txt b/Tests/RunCMake/CommandLine/P_directory-stderr.txt index b8319a1..ec33aed 100644 --- a/Tests/RunCMake/CommandLine/P_directory-stderr.txt +++ b/Tests/RunCMake/CommandLine/P_directory-stderr.txt @@ -1 +1 @@ -^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine$ +^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine$ diff --git a/Tests/RunCMake/CommandLine/P_no-file-stderr.txt b/Tests/RunCMake/CommandLine/P_no-file-stderr.txt index 2e12399..a3b4e8e 100644 --- a/Tests/RunCMake/CommandLine/P_no-file-stderr.txt +++ b/Tests/RunCMake/CommandLine/P_no-file-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Error processing file: nosuchscriptfile.cmake$ +^CMake Error: Not a file: nosuchscriptfile.cmake +CMake Error: Error processing file: nosuchscriptfile.cmake$ diff --git a/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt b/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt index 320aecc..87999d5 100644 --- a/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt +++ b/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt @@ -1 +1 @@ -^CMake Error: Error processing file: +^CMake Error: Not a file: diff --git a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake index 6847a23..f7959dc 100644 --- a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake +++ b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake @@ -1,7 +1,11 @@ include(RunCMake) function(check_files dir) - set(expected ${ARGN}) + set(expected) + foreach(i IN LISTS ARGN) + get_filename_component(real_path ${i} REALPATH) + list(APPEND expected ${real_path}) + endforeach() list(FILTER expected EXCLUDE REGEX "^$") list(REMOVE_DUPLICATES expected) list(SORT expected) diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake b/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake new file mode 100644 index 0000000..376f2eb --- /dev/null +++ b/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake @@ -0,0 +1,11 @@ +enable_language(C) + +include(FetchContent) + +FetchContent_Declare( + ExcludeFromAll + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/ExcludeFromAll + EXCLUDE_FROM_ALL +) + +FetchContent_MakeAvailable(ExcludeFromAll) diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt new file mode 100644 index 0000000..08ffb33 --- /dev/null +++ b/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt @@ -0,0 +1 @@ +add_library(broken STATIC error.c) diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c b/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c new file mode 100644 index 0000000..f10e687 --- /dev/null +++ b/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c @@ -0,0 +1 @@ +#error "This should not be compiled" diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake index 3781089..d0790eb 100644 --- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake +++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake @@ -64,3 +64,15 @@ run_cmake_command(ScriptMode -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM} -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake ) + +function(run_FetchContent_ExcludeFromAll) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build) + file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") + file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + + run_cmake(ExcludeFromAll) + + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .) +endfunction() +run_FetchContent_ExcludeFromAll() diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 8911e18..807d92b 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -846,7 +846,7 @@ def gen_check_targets(c, g, inSource): e["compileGroups"] = apple_exe_framework["compileGroups"] e["link"] = apple_exe_framework["link"] - if cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'MSVC', 'Embarcadero', 'IBMClang'] and g["name"] != "Xcode": + if cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'MSVC', 'Embarcadero', 'CrayClang', 'IBMClang'] and g["name"] != "Xcode": for e in expected: if e["name"] == "cxx_exe": if matches(g["name"], "^(Visual Studio |Ninja Multi-Config)"): diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json index c4df2ec..f260037 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json @@ -16,7 +16,7 @@ "type": "target", "destination": "lib", "paths": [ - "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_1\\.(a|lib)?$" + "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_1\\.(a|lib|l)?$" ], "isExcludeFromAll": null, "isForAllComponents": null, @@ -172,7 +172,7 @@ "type": "target", "destination": "lib", "paths": [ - "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_2\\.(a|lib)?$" + "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_2\\.(a|lib|l)?$" ], "isExcludeFromAll": null, "isForAllComponents": null, diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json index a35d5e2..b57ab45 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json @@ -80,7 +80,7 @@ "type": "target", "destination": "lib", "paths": [ - "^((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(lib|dll\\.a)$" + "^((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(dll\\.a|lib|l)$" ], "isExcludeFromAll": null, "isForAllComponents": null, @@ -173,7 +173,7 @@ "type": "target", "destination": "lib", "paths": [ - "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?cxx_shared_lib\\.(lib|dll\\.a)$" + "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib|l)$" ], "isExcludeFromAll": null, "isForAllComponents": null, diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json index 6d320f4..a4c13a8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json @@ -34,7 +34,7 @@ "lto": null, "commandFragments": [ { - "fragment": "-iframework .+/framework(/(Debug|Release|RelWithDebInfo|MinSizeRel))?\"? -iframework /usr/Frameworks$", + "fragment": "-F.+/framework(/(Debug|Release|RelWithDebInfo|MinSizeRel))?\"? -F/usr/Frameworks$", "role": "frameworkPath", "backtrace": null }, diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json index f6cfa9c..faf0eaa 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json @@ -219,10 +219,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?c_headers_1\\.(a|lib)$", + "nameOnDisk": "^(lib)?c_headers_1\\.(a|lib|l)$", "artifacts": [ { - "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_1\\.(a|lib)$", + "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_1\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json index 591ba4f..b4f2f9d 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json @@ -71,10 +71,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?c_headers_2\\.(a|lib)$", + "nameOnDisk": "^(lib)?c_headers_2\\.(a|lib|l)$", "artifacts": [ { - "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_2\\.(a|lib)$", + "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_2\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json index dc74fdf..5b58dd1 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json @@ -88,10 +88,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?c_lib\\.(a|lib)$", + "nameOnDisk": "^(lib)?c_lib\\.(a|lib|l)$", "artifacts": [ { - "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib)$", + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json index 85b5108..8d5faf8 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json @@ -100,7 +100,7 @@ "_dllExtra": false }, { - "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib)$", + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib|l)$", "_dllExtra": true }, { diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json index 6a51295..2220581 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json @@ -88,10 +88,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib)$", + "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib|l)$", "artifacts": [ { - "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib)$", + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json index 362caf9..a5bebcd 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json @@ -113,10 +113,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?c_subdir\\.(a|lib)$", + "nameOnDisk": "^(lib)?c_subdir\\.(a|lib|l)$", "artifacts": [ { - "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_subdir\\.(a|lib)$", + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_subdir\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json index 725cad9..2f8d6ed 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json @@ -64,10 +64,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib)$", + "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib|l)$", "artifacts": [ { - "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib)$", + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json index c92e573..2274e45 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json @@ -76,7 +76,7 @@ "_dllExtra": false }, { - "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib)$", + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib|l)$", "_dllExtra": true }, { diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json index 38790dd..2f322b0 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json @@ -64,10 +64,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib)$", + "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib|l)$", "artifacts": [ { - "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib)$", + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json index 521e464..5b47814 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json @@ -73,6 +73,47 @@ { "define": "interface_exe_EXPORTS", "backtrace": null + }, + { + "define": "COMPILED_WITH_INTERFACE_LIB", + "backtrace": [ + { + "file": "^include_test\\.cmake$", + "line": 4, + "command": "target_link_libraries", + "hasParent": true + }, + { + "file": "^include_test\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": 3, + "command": "include", + "hasParent": true + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": null, + "command": null, + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": true + }, + { + "file": "^CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] } ], "compileCommandFragments": null @@ -129,7 +170,7 @@ "_dllExtra": false }, { - "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib)$", + "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib|l)$", "_dllExtra": true }, { diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json index 41b5605..fab6f66 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json @@ -76,7 +76,7 @@ "_dllExtra": false }, { - "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?shared_framework\\.(dll\\.a|lib)$", + "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?shared_framework\\.(dll\\.a|lib|l)$", "_dllExtra": true }, { diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json index 00dd11e..d6cbded 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json @@ -64,10 +64,10 @@ } ], "folder": null, - "nameOnDisk": "^(lib)?static_framework\\.(a|lib)$", + "nameOnDisk": "^(lib)?static_framework\\.(a|lib|l)$", "artifacts": [ { - "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?static_framework\\.(a|lib)$", + "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?static_framework\\.(a|lib|l)$", "_dllExtra": false } ], diff --git a/Tests/RunCMake/FileAPI/include_test.cmake b/Tests/RunCMake/FileAPI/include_test.cmake index c74d264..c188cb3 100644 --- a/Tests/RunCMake/FileAPI/include_test.cmake +++ b/Tests/RunCMake/FileAPI/include_test.cmake @@ -1,7 +1,7 @@ add_library(interface_lib INTERFACE) target_compile_definitions(interface_lib INTERFACE COMPILED_WITH_INTERFACE_LIB) add_executable(interface_exe empty.c) -target_link_libraries(interface_exe PRIVATE inteface_lib) +target_link_libraries(interface_exe PRIVATE interface_lib) set_property(TARGET interface_exe PROPERTY ENABLE_EXPORTS ON) set_property(TARGET interface_exe PROPERTY RUNTIME_OUTPUT_DIRECTORY bin) set_property(TARGET interface_exe PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib) diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake new file mode 100644 index 0000000..98f3cd6 --- /dev/null +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake @@ -0,0 +1,19 @@ +# Prepare environment and variables +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) +if(WIN32) + set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}\\pc-bletch") +else() + set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/pc-bletch") +endif() + +find_package(PkgConfig REQUIRED) +pkg_check_modules(BLETCH QUIET bletch) + +if (NOT BLETCH_FOUND) + message(FATAL_ERROR "Failed to find embedded package bletch via CMAKE_PREFIX_PATH") +endif () + +pkg_get_variable(bletchvar bletch exec_prefix DEFINE_VARIABLES prefix=customprefix) +if (NOT bletchvar STREQUAL "customprefix") + message(FATAL_ERROR "Failed to fetch variable exec_prefix from embedded package bletch with prefix overridden to customprefix") +endif () diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt deleted file mode 100644 index 539e5ef..0000000 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt +++ /dev/null @@ -1,3 +0,0 @@ --- ZOT_LIBRARIES='zot' --- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/prefix-zot-suffix' --- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib;-lzot' diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt new file mode 100644 index 0000000..012458d --- /dev/null +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt @@ -0,0 +1,4 @@ +-- ZOT_LIBRARIES='zot' +-- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib/prefix-zot-suffix' +-- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib;-lzot' +-- ZOT_CFLAGS='-I[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/include' diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake index 1278c49..e58cefb 100644 --- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake +++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake @@ -3,16 +3,19 @@ find_package(PkgConfig REQUIRED) set(ROOT "${CMAKE_CURRENT_BINARY_DIR}/root") string(REPLACE " " "\\ " ESCAPED_ROOT "${ROOT}") set(LIB_DIR "${ROOT}/lib") +set(INCLUDE_DIR "${ROOT}/include") set(PKGCONFIG_DIR "${LIB_DIR}/pkgconfig") file(WRITE "${PKGCONFIG_DIR}/zot.pc" " prefix=${ESCAPED_ROOT} libdir=\${prefix}/lib +includedir=\${prefix}/include Name: Zot Description: Dummy package to test LIBRARY_DIR support Version: 1.0 Libs: -L\${libdir} -lzot +Cflags: -I\${includedir} ") # Create a "library" file to find in libdir. @@ -22,9 +25,13 @@ file(WRITE "${LIB_DIR}/prefix-zot-suffix") # 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default. set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${LIB_DIR}") +# 'pkg-config --cflags' drops -I flags in PKG_CONFIG_SYSTEM_INCLUDE_PATH by default. +set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "${INCLUDE_DIR}") # 'pkgconf --libs' also drops -L flags in LIBRARY_PATH by default. set(ENV{LIBRARY_PATH} "${LIB_DIR}") +# 'pkgconf --cflags' also drops -I flags in CPATH by default. +set(ENV{CPATH} "${INCLUDE_DIR}") set(ENV{PKG_CONFIG_PATH} "${PKGCONFIG_DIR}") pkg_check_modules(ZOT REQUIRED zot) @@ -32,3 +39,4 @@ pkg_check_modules(ZOT REQUIRED zot) message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'") message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'") message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'") +message(STATUS "ZOT_CFLAGS='${ZOT_CFLAGS}'") diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake index 6b8e884..7af425a 100644 --- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake @@ -51,6 +51,7 @@ Libs: -L\${libdir} endif() endif() run_cmake(FindPkgConfig_GET_VARIABLE) + run_cmake(FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES) run_cmake(FindPkgConfig_GET_VARIABLE_PREFIX_PATH) run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH) run_cmake(FindPkgConfig_cache_variables) @@ -59,6 +60,6 @@ Libs: -L\${libdir} run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME) run_cmake(FindPkgConfig_empty_target) if(NOT PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH) - run_cmake(FindPkgConfig_LIBRARY_PATH) + run_cmake(FindPkgConfig_SYSTEM_PATH) endif() endif () diff --git a/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake b/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake index bcf6c29..b50a8ad 100644 --- a/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake +++ b/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake @@ -8,7 +8,7 @@ set_target_properties(Example::Example PROPERTIES ) add_library(testcase FrameworkSystemIncludeTest.c) -target_compile_options(testcase PRIVATE "-Werror=#pragma-messages") +target_compile_options(testcase PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>") target_link_libraries(testcase PRIVATE Example::Example) @@ -20,5 +20,17 @@ set_target_properties(Example::Example2 PROPERTIES ) add_library(testcase2 FrameworkSystemIncludeTest.c) -target_compile_options(testcase2 PRIVATE "-Werror=#pragma-messages") +target_compile_options(testcase2 PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>") target_link_libraries(testcase2 PRIVATE Example::Example2) + + + +add_library(Example::Example3 SHARED IMPORTED) +set_target_properties(Example::Example3 PROPERTIES + FRAMEWORK 1 + IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/subdir/Example.framework" +) + +add_library(testcase3 FrameworkSystemIncludeTest.c) +target_compile_options(testcase3 PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>") +target_link_libraries(testcase3 PRIVATE Example::Example3) diff --git a/Tests/RunCMake/Framework/RunCMakeTest.cmake b/Tests/RunCMake/Framework/RunCMakeTest.cmake index 7319a59..2c5b46f 100644 --- a/Tests/RunCMake/Framework/RunCMakeTest.cmake +++ b/Tests/RunCMake/Framework/RunCMakeTest.cmake @@ -15,8 +15,10 @@ function(framework_layout_test Name Toolchain Type) run_cmake_command(${Name} ${CMAKE_COMMAND} --build .) endfunction() -framework_layout_test(iOSFrameworkLayout-build ios SHARED) -framework_layout_test(iOSFrameworkLayout-build ios STATIC) +if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU") + framework_layout_test(iOSFrameworkLayout-build ios SHARED) + framework_layout_test(iOSFrameworkLayout-build ios STATIC) +endif() framework_layout_test(OSXFrameworkLayout-build osx SHARED) framework_layout_test(OSXFrameworkLayout-build osx STATIC) @@ -36,13 +38,17 @@ function(framework_type_test Toolchain Type UseProperty) run_cmake_command(FrameworkType${Type}-build ${CMAKE_COMMAND} --build .) endfunction() -framework_type_test(ios SHARED NO) -framework_type_test(ios STATIC NO) +if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU") + framework_type_test(ios SHARED NO) + framework_type_test(ios STATIC NO) +endif() framework_type_test(osx SHARED NO) framework_type_test(osx STATIC NO) -framework_type_test(ios SHARED YES) -framework_type_test(ios STATIC YES) +if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU") + framework_type_test(ios SHARED YES) + framework_type_test(ios STATIC YES) +endif() framework_type_test(osx SHARED YES) framework_type_test(osx STATIC YES) diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index 3e35aa3..a34708e 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake @@ -98,7 +98,7 @@ if (WIN32 OR CYGWIN) if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") set(_platform Win32-Clang) - elseif(MSVC AND COMPILER_HAS_DEPRECATED) + elseif((MSVC OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") AND COMPILER_HAS_DEPRECATED) set(_platform Win32) elseif(CYGWIN AND COMPILER_HAS_DEPRECATED) set(_platform Cygwin) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt new file mode 100644 index 0000000..1267cb3 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at BadAND.cmake:2 \(add_custom_target\): + Error evaluating generator expression: + + \$<0> + + \$<0> expression requires a parameter. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake new file mode 100644 index 0000000..91efaf4 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake @@ -0,0 +1,4 @@ +set(error $<0>) +add_custom_target(check ALL COMMAND check + $<AND:1,${error}> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt new file mode 100644 index 0000000..4e296a5 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at BadIF.cmake:2 \(add_custom_target\): + Error evaluating generator expression: + + \$<0> + + \$<0> expression requires a parameter. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake new file mode 100644 index 0000000..797cc69 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake @@ -0,0 +1,4 @@ +set(error $<0>) +add_custom_target(check ALL COMMAND check + $<IF:0,1,${error}> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt new file mode 100644 index 0000000..7876d7d --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at BadOR.cmake:2 \(add_custom_target\): + Error evaluating generator expression: + + \$<0> + + \$<0> expression requires a parameter. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake new file mode 100644 index 0000000..7477b8f --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake @@ -0,0 +1,4 @@ +set(error $<0>) +add_custom_target(check ALL COMMAND check + $<OR:0,${error}> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt new file mode 100644 index 0000000..54a4d62 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.26) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake new file mode 100644 index 0000000..26bcaba --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake @@ -0,0 +1,4 @@ +set(error $<0>) +add_custom_target(check ALL COMMAND check + $<AND:0,${error}> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake new file mode 100644 index 0000000..1f9fbe6 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake @@ -0,0 +1,5 @@ +set(error $<0>) +add_custom_target(check ALL + COMMAND check $<IF:1,1,${error}> + COMMAND Check $<IF:0,${error},1> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake new file mode 100644 index 0000000..b574937 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake @@ -0,0 +1,4 @@ +set(error $<0>) +add_custom_target(check ALL COMMAND check + $<OR:1,${error}> +) diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake new file mode 100644 index 0000000..b0ad679 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake @@ -0,0 +1,9 @@ +include(RunCMake) + +run_cmake(GoodIF) +run_cmake(GoodAND) +run_cmake(GoodOR) + +run_cmake(BadIF) +run_cmake(BadAND) +run_cmake(BadOR) diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt new file mode 100644 index 0000000..61188b6 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt @@ -0,0 +1,10 @@ +CMake Error at CMakeLists.txt:[0-9]+ \(project\): + Generator + + Xcode + + toolset specification field + + buildsystem=1 + + is not allowed with Xcode [0-9.]+\.$ diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake new file mode 100644 index 0000000..2fc38e5 --- /dev/null +++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake @@ -0,0 +1 @@ +message(FATAL_ERROR "This should not be reached!") diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake index a742391..71cc2d4 100644 --- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake @@ -89,7 +89,11 @@ elseif("${RunCMake_GENERATOR}" STREQUAL "Xcode") set(RunCMake_GENERATOR_TOOLSET "Test Toolset") run_cmake(TestToolsetXcodeBuildSystemDefault12) set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=1") - run_cmake(TestToolsetXcodeBuildSystem1) + if(XCODE_VERSION VERSION_GREATER_EQUAL 14) + run_cmake(BadToolsetXcodeBuildSystem1) + else() + run_cmake(TestToolsetXcodeBuildSystem1) + endif() set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=12") run_cmake(TestToolsetXcodeBuildSystem12) else() diff --git a/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake b/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake index d2dc530..571e4aa 100644 --- a/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake +++ b/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake @@ -1,8 +1,14 @@ function(xcode_sign_adhoc target) if(CMAKE_GENERATOR STREQUAL "Xcode" AND - "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64") - # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing. - # See CMake Issue 21845. - target_link_options(${target} PRIVATE LINKER:-adhoc_codesign) + "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64") + if(XCODE_VERSION VERSION_GREATER_EQUAL 15) + # Xcode 15+ enforces '-Xlinker -no_adhoc_codesign' after user flags, + # so we cannot convince the linker to add an adhoc signature. + add_custom_command(TARGET ${target} POST_BUILD COMMAND codesign --sign - --force "$<TARGET_FILE:${target}>") + else() + # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing. + # See CMake Issue 21845. + target_link_options(${target} PRIVATE LINKER:-adhoc_codesign) + endif() endif() endfunction() diff --git a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake index 9b7a6a9..f2df290 100644 --- a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake +++ b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake @@ -24,6 +24,7 @@ run_multilint(CXX) if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake") run_multilint(C-launch) run_multilint(CXX-launch) + run_multilint(genex) endif() function(run_skip_linting test_name) diff --git a/Tests/RunCMake/MultiLint/genex.cmake b/Tests/RunCMake/MultiLint/genex.cmake new file mode 100644 index 0000000..17f9248 --- /dev/null +++ b/Tests/RunCMake/MultiLint/genex.cmake @@ -0,0 +1,6 @@ +enable_language(CXX) +set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<IF:$<BOOL:FALSE>,${PSEUDO_IWYU},>") +set(CMAKE_CXX_CLANG_TIDY "$<IF:$<BOOL:FALSE>,${PSEUDO_TIDY} --error,>") +set(CMAKE_CXX_CPPLINT "$<IF:$<BOOL:FALSE>,${PSEUDO_CPPLINT} --error,>") +set(CMAKE_CXX_CPPCHECK "$<IF:$<BOOL:FALSE>,${PSEUDO_CPPCHECK} -bad,>") +add_executable(main main.cxx) diff --git a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt deleted file mode 100644 index 70644fa..0000000 --- a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_executable(sub_exe_3 ../app.cpp) -target_link_libraries(sub_exe_3 PRIVATE ${QtX}::Core) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 6eb0b1d..69f2587 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake @@ -348,7 +348,11 @@ if("${ninja_version}" VERSION_LESS 1.6) return() endif() -foreach(ninja_output_path_prefix "sub space" "sub") +set(ninja_output_path_prefixes "sub") +if(NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC") + list(APPEND ninja_output_path_prefixes "sub space") +endif() +foreach(ninja_output_path_prefix IN LISTS ninja_output_path_prefixes) run_sub_cmake(Executable "${ninja_output_path_prefix}") run_sub_cmake(StaticLib "${ninja_output_path_prefix}") run_sub_cmake(SharedLib "${ninja_output_path_prefix}") @@ -380,45 +384,6 @@ function (run_ChangeBuildType) endfunction() run_ChangeBuildType() -function(run_QtAutoMocDeps) - set(QtX Qt${CMake_TEST_Qt_version}) - if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps-build) - run_cmake_with_options(QtAutoMocDeps - "-Dwith_qt_version=${CMake_TEST_Qt_version}" - "-D${QtX}_DIR=${${QtX}_DIR}" - "-D${QtX}Core_DIR=${${QtX}Core_DIR}" - "-D${QtX}Widgets_DIR=${${QtX}Widgets_DIR}" - "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" - ) - # Build the project. - run_ninja("${RunCMake_TEST_BINARY_DIR}") - # 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") - # Build and assert that AUTOMOC was not run for app_with_qt. - run_ninja("${RunCMake_TEST_BINARY_DIR}") - if(ninja_stdout MATCHES "Automatic MOC for target app_with_qt") - message(FATAL_ERROR - "AUTOMOC should not have executed for 'app_with_qt' target:\nstdout:\n${ninja_stdout}") - endif() - # Assert that the subdir executables were not rebuilt. - if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_1") - message(FATAL_ERROR - "AUTOMOC should not have executed for 'sub_exe_1' target:\nstdout:\n${ninja_stdout}") - endif() - if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_2") - message(FATAL_ERROR - "AUTOMOC should not have executed for 'sub_exe_2' target:\nstdout:\n${ninja_stdout}") - endif() - # Touch a header file to make sure an automoc dependency cycle is not introduced. - file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h") - run_ninja("${RunCMake_TEST_BINARY_DIR}") - # Need to run a second time to hit the dependency cycle. - run_ninja("${RunCMake_TEST_BINARY_DIR}") - endif() -endfunction() - function(run_QtAutoMocSkipPch) set(QtX Qt${CMake_TEST_Qt_version}) if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0) @@ -433,7 +398,7 @@ function(run_QtAutoMocSkipPch) run_ninja("${RunCMake_TEST_BINARY_DIR}") endif() endfunction() + if(CMake_TEST_Qt_version) - run_QtAutoMocDeps() run_QtAutoMocSkipPch() endif() diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake index 30b24bf..e65be3b 100644 --- a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake @@ -8,7 +8,7 @@ set(expected_compile_commands ]*", "file": "[^ ]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)", - "output": "(CMakeFiles/exe\.dir/Debug/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Debug\\\\main\.c\.obj)" + "output": "(CMakeFiles/exe\.dir/Debug/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Debug\\\\main\.c\.(obj|o))" }, { "directory": "[^ @@ -18,7 +18,7 @@ set(expected_compile_commands ]*", "file": "[^ ]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)", - "output": "(CMakeFiles/exe\.dir/Release/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Release\\\\main\.c\.obj)" + "output": "(CMakeFiles/exe\.dir/Release/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Release\\\\main\.c\.(obj|o))" } ]$]==]) diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake deleted file mode 100644 index 2d8df13..0000000 --- a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake +++ /dev/null @@ -1,7 +0,0 @@ -check_files("${RunCMake_TEST_BINARY_DIR}" - INCLUDE - ${AUTOGEN_FILES} - - ${TARGET_FILE_exe_Debug} - ${TARGET_OBJECT_FILES_exe_Debug} - ) diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake index 130f883..dded383 100644 --- a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake +++ b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake @@ -19,16 +19,17 @@ if(${QtX}Core_VERSION VERSION_GREATER_EQUAL "5.15.0") set(moc_writes_depfiles 1) endif() -set(autogen_files) -if(moc_writes_depfiles) - list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps") - list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp") -endif() -foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES) - list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${c}.cpp") - list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp") +foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) + set(autogen_files) if(moc_writes_depfiles) - list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d") + list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps_${CONFIG}") + list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp_${CONFIG}") endif() + list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${CONFIG}.cpp") + list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${CONFIG}/moc_qt5.cpp") + if(moc_writes_depfiles) + list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${CONFIG}/moc_qt5.cpp.d") + endif() + file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES_${CONFIG} [==[${autogen_files}]==])\n") + unset(autogen_files) endforeach() -file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n") diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake index 47f5eee..a39606d 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$|\\.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[^/]*\\.cmake$") foreach(f IN LISTS _check_files_INCLUDE _check_files_EXCLUDE) if(EXISTS ${f}) list(APPEND actual ${f}) @@ -486,11 +486,35 @@ if(CMake_TEST_Qt_version) "-D${QtX}Core_DIR=${${QtX}Core_DIR}" "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}" ) - run_cmake_configure(QtX) - unset(RunCMake_TEST_OPTIONS) - include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) - run_cmake_build(QtX debug-in-release-graph Release exe:Debug) + + foreach(target_config IN ITEMS Debug Release RelWithDebInfo) + foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) + block() + run_cmake_configure(QtX) + include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake) + run_cmake_build(QtX ${target_config}-in-${ninja_config}-graph ${ninja_config} exe:${target_config}) + check_files("${RunCMake_TEST_BINARY_DIR}" + INCLUDE + "${AUTOGEN_FILES_${target_config}}" + "${TARGET_FILE_exe_${target_config}}" + "${TARGET_OBJECT_FILES_exe_${target_config}}" + ) + if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "") + message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}") + endif() + + check_file_contents("${RunCMake_TEST_BINARY_DIR}/exe_autogen/deps_${target_config}" "exe_autogen/timestamp_${target_config}") + if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "") + message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}") + endif() + endblock() + endforeach() + endforeach() if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0) - run_ninja(QtX automoc-check build-Debug.ninja -t query exe_autogen/timestamp) + foreach(target_config IN ITEMS Debug Release RelWithDebInfo) + foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo) + run_ninja(QtX automoc-check-${target_config} build-${ninja_config}.ninja -t query exe_autogen/timestamp_${target_config}) + endforeach() + endforeach() endif() endif() diff --git a/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt b/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt index 6db4bcc..ace6512 100644 --- a/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt +++ b/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt @@ -1 +1 @@ -^ninja: error: unknown target 'simplestatic:all'$ +^ninja: error: unknown target 'simplestatic:all'(, did you mean 'simplestatic.l'\?)?$ diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake new file mode 100644 index 0000000..7658930 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp) + set(RunCMake_TEST_FAILED "Compiled source generated before compilation of consumers.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake new file mode 100644 index 0000000..3287a65 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp) + set(RunCMake_TEST_FAILED "Compiled source did not generate.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake new file mode 100644 index 0000000..9e99ed8 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/none.cpp) + set(RunCMake_TEST_FAILED "Private source dependency used for target without filesets.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake new file mode 100644 index 0000000..dd814c9 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h) + set(RunCMake_TEST_FAILED "Private header generated before compilation.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake new file mode 100644 index 0000000..357b8c0 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h) + set(RunCMake_TEST_FAILED "Private header generated before compilation.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake new file mode 100644 index 0000000..cfceb7a --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/public.h) + set(RunCMake_TEST_FAILED "Public header did not generate before compilation.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake new file mode 100644 index 0000000..3330269 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0154 NEW) +include(CMP0154-common.cmake) diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake new file mode 100644 index 0000000..8ee1ad4 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp) + set(RunCMake_TEST_FAILED "Policy CMP0154 set to OLD but using new behavior compiled sources.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake new file mode 100644 index 0000000..25c23d7 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h) + set(RunCMake_TEST_FAILED "Policy CMP0154 set to OLD but using new behavior private headers.") +endif() diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake new file mode 100644 index 0000000..691530c --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0154 OLD) +include(CMP0154-common.cmake) diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake new file mode 100644 index 0000000..a9e6d20 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake @@ -0,0 +1,45 @@ +enable_language(CXX) + +function(copy_file file dest) + add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/${dest} + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/${file} ${CMAKE_BINARY_DIR}/${dest} + ) +endfunction() + +copy_file(header.h.in private.h) +copy_file(header.h.in public.h) +copy_file(source.cpp.in empty.cpp) +copy_file(source.cpp.in none.cpp) + +add_library(HelloLib_PrivateFileSet STATIC hello_lib.cpp) +target_sources(HelloLib_PrivateFileSet + PRIVATE FILE_SET HEADERS + BASE_DIRS ${CMAKE_BINARY_DIR} + FILES ${CMAKE_BINARY_DIR}/private.h +) + +add_library(HelloLib_PublicFileSet STATIC hello_lib.cpp) +target_sources(HelloLib_PublicFileSet + PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_BINARY_DIR} + FILES ${CMAKE_BINARY_DIR}/public.h +) + +add_library(HelloLib_EmptyFileSet STATIC hello_lib.cpp empty.cpp) +target_sources(HelloLib_EmptyFileSet + PUBLIC FILE_SET HEADERS +) + +add_library(HelloLib_NoFileSet STATIC hello_lib.cpp none.cpp) + +function(hello_executable name) + add_executable(Hello_${name} hello.cpp) + target_link_libraries(Hello_${name} PRIVATE HelloLib_${name}) +endfunction() + +hello_executable(PrivateFileSet) +hello_executable(PublicFileSet) +hello_executable(EmptyFileSet) +hello_executable(NoFileSet) diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt b/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt new file mode 100644 index 0000000..54a4d62 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.26) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake b/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake new file mode 100644 index 0000000..e821185 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake @@ -0,0 +1,30 @@ +include(RunCMake) + +function(compile_source test target) + if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(config "Debug/") + endif() + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${test}-Build-${target}-Source ${CMAKE_COMMAND} --build . + --target CMakeFiles/Hello_${target}.dir/${config}hello.cpp${CMAKE_C_OUTPUT_EXTENSION}) +endfunction() + +function(compile_target test target) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${test}-Build-${target} ${CMAKE_COMMAND} --build . + --target Hello_${target}) +endfunction() + +run_cmake(CMP0154-OLD) +compile_source(CMP0154-OLD PrivateFileSet) +compile_source(CMP0154-OLD EmptyFileSet) + +run_cmake(CMP0154-NEW) +compile_source(CMP0154-NEW PrivateFileSet) +compile_target(CMP0154-NEW PrivateFileSet) +compile_source(CMP0154-NEW PublicFileSet) +compile_source(CMP0154-NEW NoFileSet) +compile_source(CMP0154-NEW EmptyFileSet) +compile_target(CMP0154-NEW EmptyFileSet) diff --git a/Tests/RunCMake/NinjaPrivateDeps/header.h.in b/Tests/RunCMake/NinjaPrivateDeps/header.h.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/header.h.in diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello.cpp b/Tests/RunCMake/NinjaPrivateDeps/hello.cpp new file mode 100644 index 0000000..34fabe9 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/hello.cpp @@ -0,0 +1,6 @@ +#include "hello_lib.h" + +int main() +{ + say_hello(); +} diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp new file mode 100644 index 0000000..d67113f --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp @@ -0,0 +1,8 @@ +#include "hello_lib.h" + +#include <iostream> + +void say_hello() +{ + std::cout << "hello" << std::endl; +} diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h new file mode 100644 index 0000000..3b4775f --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h @@ -0,0 +1 @@ +void say_hello(); diff --git a/Tests/RunCMake/NinjaPrivateDeps/source.cpp.in b/Tests/RunCMake/NinjaPrivateDeps/source.cpp.in new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/NinjaPrivateDeps/source.cpp.in diff --git a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake index 3d60556..a1fa7b3 100644 --- a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake +++ b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake @@ -14,8 +14,14 @@ target_link_libraries(LinkObjLHSShared AnObjLib) # Verify that our dependency on OtherLib generated its versioning symlinks. if(CMAKE_GENERATOR STREQUAL "Xcode" AND "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64") - # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing. - # See CMake Issue 21845. - target_link_options(LinkObjLHSShared PRIVATE LINKER:-adhoc_codesign) + if(XCODE_VERSION VERSION_GREATER_EQUAL 15) + # Xcode 15+ enforces '-Xlinker -no_adhoc_codesign' after user flags, + # so we cannot convince the linker to add an adhoc signature. + add_custom_command(TARGET LinkObjLHSShared POST_BUILD COMMAND codesign --sign - --force "$<TARGET_FILE:LinkObjLHSShared>") + else() + # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing. + # See CMake Issue 21845. + target_link_options(LinkObjLHSShared PRIVATE LINKER:-adhoc_codesign) + endif() endif() add_custom_command(TARGET LinkObjLHSShared POST_BUILD COMMAND LinkObjLHSShared) diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index bc4a330..fcf904e 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -24,6 +24,8 @@ function(run_cmake test) if(EXISTS ${top_src}/${test}-result.txt) file(READ ${top_src}/${test}-result.txt expect_result) string(REGEX REPLACE "\n+$" "" expect_result "${expect_result}") + elseif(DEFINED RunCMake_TEST_EXPECT_RESULT) + set(expect_result "${RunCMake_TEST_EXPECT_RESULT}") else() set(expect_result 0) endif() @@ -53,6 +55,11 @@ function(run_cmake test) unset(expect_${o}) endif() endforeach() + foreach(o IN ITEMS stdout stderr config) + if(DEFINED RunCMake_TEST_NOT_EXPECT_${o}) + string(REGEX REPLACE "\n+$" "" not_expect_${o} "${RunCMake_TEST_NOT_EXPECT_${o}}") + endif() + endforeach() if (NOT expect_stderr) if (NOT RunCMake_DEFAULT_stderr) set(RunCMake_DEFAULT_stderr "^$") @@ -145,7 +152,19 @@ function(run_cmake test) ${maybe_timeout} ${maybe_input_file} )]]) + if(DEFINED ENV{PWD}) + set(old_pwd "$ENV{PWD}") + else() + set(old_pwd) + endif() + # Emulate a shell using this directory. + set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}") cmake_language(EVAL CODE "${_code}") + if(DEFINED old_pwd) + set(ENV{PWD} "${old_pwd}") + else() + set(ENV{PWD}) + endif() set(msg "") if(NOT "${actual_result}" MATCHES "${expect_result}") string(APPEND msg "Result is [${actual_result}], not [${expect_result}].\n") @@ -182,6 +201,8 @@ function(run_cmake test) "|ic(p?c|l): remark #10441: The Intel\\(R\\) C\\+\\+ Compiler Classic \\(ICC\\) is deprecated" "|[^\n]*install_name_tool: warning: changes being made to the file will invalidate the code signature in:" + "|[^\n]*(createItemModels|_NSMainThread|Please file a bug at)" + "|[^\n]*xcodebuild[^\n]*DVTAssertions: Warning" "|[^\n]*xcodebuild[^\n]*DVTCoreDeviceEnabledState: DVTCoreDeviceEnabledState_Disabled set via user default" "|[^\n]*xcodebuild[^\n]*DVTPlugInManager" "|[^\n]*xcodebuild[^\n]*DVTSDK: Warning: SDK path collision for path" @@ -216,6 +237,11 @@ function(run_cmake test) string(APPEND msg "${o} does not match that expected.\n") endif() endif() + if(DEFINED not_expect_${o}) + if("${actual_${o}}" MATCHES "${not_expect_${o}}") + string(APPEND msg "${o} matches that not expected.\n") + endif() + endif() endforeach() unset(RunCMake_TEST_FAILED) if(RunCMake-check-file AND EXISTS ${top_src}/${RunCMake-check-file}) diff --git a/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake b/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake index 58a111a..12f004b 100644 --- a/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake +++ b/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake @@ -29,8 +29,6 @@ function (run_symlink_test case src bin src_from_bin bin_from_src) # Test running in binary directory. set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - # Emulate a shell using this directory. - set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}") # Pass absolute path to the source tree, plain. set(RunCMake_TEST_VARIANT_DESCRIPTION " $abs/${name}/${src}") @@ -50,8 +48,6 @@ function (run_symlink_test case src bin src_from_bin bin_from_src) # Test running in source directory. set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}") - # Emulate a shell using this directory. - set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}") # Pass absolute path to the binary tree with -B. set(RunCMake_TEST_VARIANT_DESCRIPTION " -B $abs/${name}/${bin}") @@ -63,8 +59,6 @@ function (run_symlink_test case src bin src_from_bin bin_from_src) # Test running in another directory. set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_BINARY_DIR}/${name}") - # Emulate a shell using this directory. - set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}") # Pass absolute paths to the source and binary trees. set(RunCMake_TEST_VARIANT_DESCRIPTION " -S $abs/${name}/${src} -B $abs/${name}/${bin}") diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 0d8e4c9..7e91b76 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -37,6 +37,7 @@ \* CMP0119 \* CMP0131 \* CMP0142 + \* CMP0154 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake index 22c28b4..9a13892 100644 --- a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake @@ -14,25 +14,36 @@ function(create_libraries type) create_library(${type} ios iOS "arm64" iphoneos) create_library(${type} tvos tvOS "arm64" appletvos) create_library(${type} watchos watchOS "armv7k\\\\;arm64_32" watchos) - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) - create_library(${type} visionos visionOS "arm64" xros) - endif() + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # create_library(${type} visionos visionOS "arm64" xros) + #endif() create_library(${type} ios-simulator iOS "${macos_archs_2}" iphonesimulator) create_library(${type} tvos-simulator tvOS "${macos_archs_2}" appletvsimulator) create_library(${type} watchos-simulator watchOS "${watch_sim_archs_2}" watchsimulator) - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) - create_library(${type} visionos-simulator visionOS "${macos_archs_2}" xrsimulator) - endif() + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # create_library(${type} visionos-simulator visionOS "${macos_archs_2}" xrsimulator) + #endif() endfunction() function(create_xcframework name type platforms) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-xcframework-${name}-build) + if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # 'xcodebuild -create-xcframework' fails on symlinked paths. + file(REAL_PATH "${RunCMake_SOURCE_DIR}" src_dir) + file(REAL_PATH "${RunCMake_BINARY_DIR}" bld_dir) + else() + set(src_dir "${RunCMake_SOURCE_DIR}") + set(bld_dir "${RunCMake_BINARY_DIR}") + endif() set(args) foreach(platform IN LISTS platforms) + set(lib_dir "${bld_dir}/create-${type}-${platform}-build/install/lib") if(type STREQUAL "framework") - list(APPEND args -framework ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build/install/lib/mylib.framework) + list(APPEND args -framework ${lib_dir}/mylib.framework) else() - list(APPEND args -library ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build/install/lib/libmylib.a -headers ${RunCMake_SOURCE_DIR}/mylib/include) + list(APPEND args -library ${lib_dir}/libmylib.a -headers ${src_dir}/mylib/include) endif() endforeach() run_cmake_command(create-xcframework-${name} xcodebuild -create-xcframework ${args} -output ${RunCMake_TEST_BINARY_DIR}/mylib.xcframework) @@ -51,22 +62,25 @@ function(create_executables name type) create_executable(${name}-ios ${type} iOS "arm64" iphoneos) create_executable(${name}-tvos ${type} tvOS "arm64" appletvos) create_executable(${name}-watchos ${type} watchOS "armv7k\\\\;arm64_32" watchos) - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) - create_executable(${name}-visionos ${type} visionOS "arm64" xros) - endif() + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # create_executable(${name}-visionos ${type} visionOS "arm64" xros) + #endif() create_executable(${name}-ios-simulator ${type} iOS "${macos_archs_2}" iphonesimulator) create_executable(${name}-tvos-simulator ${type} tvOS "${macos_archs_2}" appletvsimulator) create_executable(${name}-watchos-simulator ${type} watchOS "${watch_sim_archs_2}" watchsimulator) - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) - create_executable(${name}-visionos-simulator ${type} visionOS "${macos_archs_2}" xrsimulator) - endif() + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) + # create_executable(${name}-visionos-simulator ${type} visionOS "${macos_archs_2}" xrsimulator) + #endif() endfunction() set(xcframework_platforms macos ios tvos watchos ios-simulator tvos-simulator watchos-simulator) -if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15) - list(APPEND xcframework_platforms visionos visionos-simulator) -endif() -if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12) +#FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. +#if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15) +# list(APPEND xcframework_platforms visionos visionos-simulator) +#endif() +if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12) set(macos_archs_1 "x86_64\\;arm64") set(macos_archs_2 "x86_64\\\\;arm64") set(watch_sim_archs_2 "x86_64") @@ -87,7 +101,7 @@ run_cmake_with_options(create-executable-incomplete -DCMAKE_SYSTEM_NAME=Darwin " create_executables(target-library library) create_executables(target-framework framework) run_cmake_with_options(create-executable-target-incomplete -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework) -if(RunCMake_GENERATOR STREQUAL "Xcode" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12) +if(RunCMake_GENERATOR STREQUAL "Xcode" AND CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12) create_executables(library-link-phase library) create_executables(framework-link-phase framework) run_cmake_with_options(create-executable-incomplete-link-phase -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework) diff --git a/Tests/RunCMake/XcFramework/find-library.cmake b/Tests/RunCMake/XcFramework/find-library.cmake index c5fe8db..1bc7672 100644 --- a/Tests/RunCMake/XcFramework/find-library.cmake +++ b/Tests/RunCMake/XcFramework/find-library.cmake @@ -1,5 +1,6 @@ find_library(MYLIB_XCFRAMEWORK mylib NO_DEFAULT_PATH PATHS "${CMAKE_BINARY_DIR}/../create-xcframework-framework-build") -file(REAL_PATH "${CMAKE_BINARY_DIR}/../create-xcframework-framework-build/mylib.xcframework" expected_path) +get_filename_component(bin_parent "${CMAKE_BINARY_DIR}" PATH) +set(expected_path "${bin_parent}/create-xcframework-framework-build/mylib.xcframework") if(NOT MYLIB_XCFRAMEWORK STREQUAL expected_path) message(FATAL_ERROR "Expected value of MYLIB_XCFRAMEWORK:\n ${expected_path}\nActual value:\n ${MYLIB_XCFRAMEWORK}") endif() diff --git a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake index b3ab624..e4dbb90 100644 --- a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake @@ -93,24 +93,25 @@ if(NOT XCODE_VERSION VERSION_LESS 7.1) unset(RunCMake_TEST_OPTIONS) endif() -if(NOT XCODE_VERSION VERSION_LESS 15) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeBundlesVisionOS-build) - set(RunCMake_TEST_NO_CLEAN 1) - set(RunCMake_TEST_OPTIONS - "-DCMAKE_SYSTEM_NAME=visionOS" - "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/_install") - - file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") - file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") - - run_cmake(XcodeBundles) - run_cmake_command(XcodeBundles-build-visionOS ${CMAKE_COMMAND} --build .) - run_cmake_command(XcodeBundles-install-visionOS ${CMAKE_COMMAND} --build . --target install) - - unset(RunCMake_TEST_BINARY_DIR) - unset(RunCMake_TEST_NO_CLEAN) - unset(RunCMake_TEST_OPTIONS) -endif() +#FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. +#if(NOT XCODE_VERSION VERSION_LESS 15) +# set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeBundlesVisionOS-build) +# set(RunCMake_TEST_NO_CLEAN 1) +# set(RunCMake_TEST_OPTIONS +# "-DCMAKE_SYSTEM_NAME=visionOS" +# "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/_install") +# +# file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") +# file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") +# +# run_cmake(XcodeBundles) +# run_cmake_command(XcodeBundles-build-visionOS ${CMAKE_COMMAND} --build .) +# run_cmake_command(XcodeBundles-install-visionOS ${CMAKE_COMMAND} --build . --target install) +# +# unset(RunCMake_TEST_BINARY_DIR) +# unset(RunCMake_TEST_NO_CLEAN) +# unset(RunCMake_TEST_OPTIONS) +#endif() if(NOT XCODE_VERSION VERSION_LESS 7) set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/osx.cmake") @@ -261,10 +262,11 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 8) deployment_target_test(tvOS appletvsimulator) deployment_target_test(watchOS watchos) deployment_target_test(watchOS watchsimulator) - if(XCODE_VERSION VERSION_GREATER_EQUAL 15) - deployment_target_test(visionOS xros) - deployment_target_test(visionOS xrsimulator) - endif() + #FIXME(#25266): Xcode 15.0 does not have visionOS. Improve this condition. + #if(XCODE_VERSION VERSION_GREATER_EQUAL 15) + # deployment_target_test(visionOS xros) + # deployment_target_test(visionOS xrsimulator) + #endif() endif() if(XCODE_VERSION VERSION_GREATER_EQUAL 8) @@ -311,9 +313,11 @@ if (XCODE_VERSION VERSION_GREATER_EQUAL 7.3) endfunction() if(XCODE_VERSION VERSION_GREATER_EQUAL 12) - xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") xctest_add_bundle_test(Darwin macosx "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") - xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + if(XCODE_VERSION VERSION_LESS 14) + xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns") + endif() if (XCODE_VERSION VERSION_LESS 12.5) xctest_add_bundle_test(iOS iphonesimulator "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>") else() diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt new file mode 100644 index 0000000..d5a0ca8 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt @@ -0,0 +1 @@ +MSYSTEM_PREFIX='' diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake new file mode 100644 index 0000000..ac36c8d --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake @@ -0,0 +1,3 @@ +unset(ENV{MSYSTEM}) +cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX) +message(STATUS "MSYSTEM_PREFIX='${result}'") diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-result.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt new file mode 100644 index 0000000..89c4e9b --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt @@ -0,0 +1,3 @@ +^CMake Error at [^ +]*/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake:[0-9]+ \(cmake_host_system_information\): + cmake_host_system_information does not recognize <key> MSYSTEM_PREFIX$ diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake new file mode 100644 index 0000000..dc1def3 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake @@ -0,0 +1,2 @@ +unset(ENV{MSYSTEM}) +cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX) diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt new file mode 100644 index 0000000..f6e2549 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt @@ -0,0 +1,2 @@ +-- MSYSTEM_PREFIX='[^ +]+' diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake new file mode 100644 index 0000000..d1c996b --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake @@ -0,0 +1,7 @@ +cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX) +message(STATUS "MSYSTEM_PREFIX='${result}'") +if(CMake_TEST_MSYSTEM_PREFIX) + if(NOT "${result}" STREQUAL "${CMake_TEST_MSYSTEM_PREFIX}") + message(FATAL_ERROR "Actual result:\n ${result}\nis not expected result:\n ${CMake_TEST_MSYSTEM_PREFIX}") + endif() +endif() diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake index 9122470..0b3576d 100644 --- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake @@ -22,6 +22,17 @@ else() run_cmake(VsMSBuildMissing) endif() +if(CMAKE_HOST_WIN32) + run_cmake_script(MSYSTEM_PREFIX-Empty) + if("$ENV{MSYSTEM}" MATCHES "(MSYS|MINGW32|MINGW64|UCRT64)") + set(RunCMake_TEST_VARIANT_DESCRIPTION "-$ENV{MSYSTEM}") + run_cmake_script(MSYSTEM_PREFIX -DCMake_TEST_MSYSTEM_PREFIX=${CMake_TEST_MSYSTEM_PREFIX}) + unset(RunCMake_TEST_VARIANT_DESCRIPTION) + endif() +else() + run_cmake_script(MSYSTEM_PREFIX-Missing) +endif() + # WINDOWS_REGISTRY tests run_cmake(Registry_NoArgs) run_cmake(Registry_BadQuery1) diff --git a/Tests/CTestTestFailure/badCode.cxx b/Tests/RunCMake/ctest_build/BuildFailure.cxx index 8102883..8102883 100644 --- a/Tests/CTestTestFailure/badCode.cxx +++ b/Tests/RunCMake/ctest_build/BuildFailure.cxx diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake index 12525f2..af56ead 100644 --- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake @@ -13,18 +13,18 @@ endfunction() run_ctest_build(BuildQuiet QUIET) run_ctest_build(ParallelLevel PARALLEL_LEVEL 1) -function(run_BuildFailure) - set(CASE_CMAKELISTS_SUFFIX_CODE [[ -add_custom_target(BuildFailure ALL COMMAND command-does-not-exist) -]]) +block() + set(LANG CXX) + configure_file("${RunCMake_SOURCE_DIR}/BuildFailure.cxx" "${RunCMake_BINARY_DIR}/BuildFailure/BuildFailure.cxx" COPYONLY) + set(CASE_CMAKELISTS_SUFFIX_CODE [=[ + add_executable(BuildFailure BuildFailure.cxx) + ]=]) set(CASE_CMAKELISTS_PREFIX_CODE [[ if(NOT CTEST_USE_LAUNCHERS) message(FATAL_ERROR "CTEST_USE_LAUNCHERS not set") endif() ]]) - set(CASE_TEST_PREFIX_CODE [[ -cmake_policy(SET CMP0061 NEW) -]]) + set(CASE_TEST_PREFIX_CODE "") set(CASE_TEST_SUFFIX_CODE [[ if (ctest_build_return_value) message("ctest_build returned non-zero") @@ -35,13 +35,16 @@ endif() run_ctest(BuildFailure) if (RunCMake_GENERATOR MATCHES "Makefiles") + set(LANG NONE) set(CASE_TEST_PREFIX_CODE [[ cmake_policy(VERSION 3.2) ]]) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_custom_target(BuildFailure ALL COMMAND command-does-not-exist) +]]) run_ctest(BuildFailure-CMP0061-OLD) endif() -endfunction() -run_BuildFailure() +endblock() function(run_BuildChangeId) set(CASE_TEST_PREFIX_CODE [[ diff --git a/Tests/RunCMake/ctest_test/NotRun-result.txt b/Tests/RunCMake/ctest_test/NotRun-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-result.txt @@ -0,0 +1 @@ +(-1|255) diff --git a/Tests/RunCMake/ctest_test/NotRun-stderr.txt b/Tests/RunCMake/ctest_test/NotRun-stderr.txt new file mode 100644 index 0000000..85907f3 --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-stderr.txt @@ -0,0 +1,2 @@ +.*Unable to find executable[^ +]*does_not_exist diff --git a/Tests/RunCMake/ctest_test/NotRun-stdout.txt b/Tests/RunCMake/ctest_test/NotRun-stdout.txt new file mode 100644 index 0000000..8d60833 --- /dev/null +++ b/Tests/RunCMake/ctest_test/NotRun-stdout.txt @@ -0,0 +1,7 @@ +.*Could not find executable[^ +]*does_not_exist +.* +50% tests passed, 1 tests failed out of 2 +.* +The following tests FAILED: +.*testNotRun \(Not Run\) diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake index 242a059..d2f3da3 100644 --- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake @@ -53,6 +53,13 @@ unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING}) unset(CASE_CTEST_TEST_LOAD) unset(RunCTest_VERBOSE_FLAG) +block() + set(CASE_CMAKELISTS_SUFFIX_CODE [[ + add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist) + ]]) + run_ctest_test(NotRun) +endblock() + function(run_TestChangeId) set(CASE_TEST_PREFIX_CODE [[ set(CTEST_CHANGE_ID "<>1") @@ -131,8 +138,7 @@ run_TestRepeat(AfterTimeout RETURN_VALUE:0 REPEAT AFTER_TIMEOUT:3) # test repeat and not run tests interact correctly set(CASE_CMAKELISTS_SUFFIX_CODE [[ -add_test(NAME testNotRun - COMMAND ${CMAKE_COMMAND}/doesnt_exist) + add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist) set_property(TEST testNotRun PROPERTY TIMEOUT 5) ]]) run_TestRepeat(NotRun RETURN_VALUE:1 REPEAT UNTIL_PASS:3) diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt index a69932d..85907f3 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt @@ -1 +1,2 @@ -.*Unable to find executable.* +.*Unable to find executable[^ +]*does_not_exist diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt index 72c98bc..8d60833 100644 --- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt +++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt @@ -1,5 +1,7 @@ +.*Could not find executable[^ +]*does_not_exist .* 50% tests passed, 1 tests failed out of 2 .* The following tests FAILED: -.*testNotRun.*Not Run.* +.*testNotRun \(Not Run\) diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index c2f9144..1f89829 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake @@ -34,6 +34,7 @@ run_cmake_command(AnyCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyC run_cmake_command(LastCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandError.cmake) run_cmake_command(LastCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandTimeout.cmake) run_cmake_command(LastCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandGood.cmake) +run_cmake_command(Stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/Stdin.cmake) if(UNIX AND Python_EXECUTABLE) run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake) diff --git a/Tests/RunCMake/execute_process/Stdin-stdin.txt b/Tests/RunCMake/execute_process/Stdin-stdin.txt new file mode 100644 index 0000000..cd08755 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin-stdin.txt @@ -0,0 +1 @@ +Hello world! diff --git a/Tests/RunCMake/execute_process/Stdin-stdout.txt b/Tests/RunCMake/execute_process/Stdin-stdout.txt new file mode 100644 index 0000000..04bd136 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin-stdout.txt @@ -0,0 +1 @@ +^Hello world!$ diff --git a/Tests/RunCMake/execute_process/Stdin.cmake b/Tests/RunCMake/execute_process/Stdin.cmake new file mode 100644 index 0000000..e8a2098 --- /dev/null +++ b/Tests/RunCMake/execute_process/Stdin.cmake @@ -0,0 +1 @@ +execute_process(COMMAND ${PRINT_STDIN_EXE}) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake index 10b7b82..61ab542 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake @@ -1,4 +1,4 @@ -if(CMAKE_C_COMPILER_ID STREQUAL "Borland") +if(CMAKE_C_COMPILER_ID MATCHES "^(Borland|OrangeC)$") # Borland upper-cases dll names referenced in import libraries. set(conflict_dll [[CONFLICT\.DLL]]) set(unresolved_dll [[UNRESOLVED\.DLL]]) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt new file mode 100644 index 0000000..607e4b8 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for PATH\.DLL: + + [^ +]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test1/path\.dll + [^ +]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test2/path\.dll$ diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt new file mode 100644 index 0000000..fea1083 --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt @@ -0,0 +1,4 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve runtime dependencies: + + UNRESOLVED\.DLL$ diff --git a/Tests/RunCMake/file/REAL_PATH.cmake b/Tests/RunCMake/file/REAL_PATH.cmake index 08d400d..0485655 100644 --- a/Tests/RunCMake/file/REAL_PATH.cmake +++ b/Tests/RunCMake/file/REAL_PATH.cmake @@ -1,17 +1,19 @@ if (NOT WIN32 OR CYGWIN) + file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}" real_binary_dir) + file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/test.txt") file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/test.sym") file(CREATE_LINK "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/test.sym" SYMBOLIC) file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/test.sym" real_path) - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"") + if (NOT real_path STREQUAL "${real_binary_dir}/test.txt") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/test.txt\"") endif() file(REAL_PATH "test.sym" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"") + if (NOT real_path STREQUAL "${real_binary_dir}/test.txt") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/test.txt\"") endif() file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/") @@ -21,33 +23,33 @@ if (NOT WIN32 OR CYGWIN) cmake_policy(SET CMP0152 NEW) file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../" real_path) - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir/nested") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir/nested\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested\"") endif() file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../bin" real_path) - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"") endif() file(REAL_PATH "dir/bin/../bin" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"") endif() file(REAL_PATH "../bin" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/" ) - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"") endif() cmake_policy(SET CMP0152 OLD) file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../" real_path) - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir/nested\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested\"") endif() file(REAL_PATH "../" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/") - if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/dir") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/dir\"") + if (NOT real_path STREQUAL "${real_binary_dir}/dir") + message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir\"") endif() endif() @@ -61,12 +63,14 @@ If (WIN32) else() set(HOME_DIR "$ENV{HOME}") endif() +file(REAL_PATH "${HOME_DIR}" HOME_DIR) file(REAL_PATH "~" real_path EXPAND_TILDE) if (NOT real_path STREQUAL "${HOME_DIR}") message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}\"") endif() +file(TOUCH "${HOME_DIR}/test.txt") file(REAL_PATH "~/test.txt" real_path EXPAND_TILDE) if (NOT real_path STREQUAL "${HOME_DIR}/test.txt") message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}/test.txt\"") diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt deleted file mode 100644 index 6912bdf..0000000 --- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' --- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt new file mode 100644 index 0000000..ab9b9d5 --- /dev/null +++ b/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt @@ -0,0 +1 @@ +-- MSYSTEM_PREFIX_H='[^']*/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h' diff --git a/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake new file mode 100644 index 0000000..08e4127 --- /dev/null +++ b/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake @@ -0,0 +1,5 @@ +set(ENV{MSYSTEM} "FAKEMSYS") +set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX") +enable_language(C) +find_file(MSYSTEM_PREFIX_H NAMES msystem_prefix.h) +message(STATUS "MSYSTEM_PREFIX_H='${MSYSTEM_PREFIX_H}'") diff --git a/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h b/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt deleted file mode 100644 index d73bc1d..0000000 --- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt deleted file mode 100644 index d73bc1d..0000000 --- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt deleted file mode 100644 index d73bc1d..0000000 --- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/RunCMakeTest.cmake b/Tests/RunCMake/find_file/RunCMakeTest.cmake index 296bb71..2d59e38 100644 --- a/Tests/RunCMake/find_file/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_file/RunCMakeTest.cmake @@ -12,6 +12,10 @@ run_cmake(VALIDATOR-undefined-function) run_cmake(VALIDATOR-specify-macro) run_cmake(VALIDATOR) +if(CMAKE_HOST_WIN32 AND MINGW) + run_cmake(MSYSTEM_PREFIX) +endif() + run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PrefixInPATH_File) if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt deleted file mode 100644 index 01e2720..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt deleted file mode 100644 index 01e2720..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt deleted file mode 100644 index 01e2720..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt deleted file mode 100644 index 48f36cc..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt deleted file mode 100644 index 48f36cc..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt deleted file mode 100644 index 48f36cc..0000000 --- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt +++ /dev/null @@ -1,6 +0,0 @@ --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' --- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt new file mode 100644 index 0000000..b19bcce --- /dev/null +++ b/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt @@ -0,0 +1 @@ +-- MSYSTEM_PREFIX_LIB='[^']*/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a' diff --git a/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake new file mode 100644 index 0000000..ef4ce2f --- /dev/null +++ b/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake @@ -0,0 +1,5 @@ +set(ENV{MSYSTEM} "FAKEMSYS") +set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX") +enable_language(C) +find_library(MSYSTEM_PREFIX_LIB NAMES msystem_prefix) +message(STATUS "MSYSTEM_PREFIX_LIB='${MSYSTEM_PREFIX_LIB}'") diff --git a/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a b/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt deleted file mode 100644 index 1ab884c..0000000 --- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt deleted file mode 100644 index 1ab884c..0000000 --- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt deleted file mode 100644 index 1ab884c..0000000 --- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake index 8b223b4..0bed252 100644 --- a/Tests/RunCMake/find_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake @@ -20,6 +20,10 @@ run_cmake(VALIDATOR-undefined-function) run_cmake(VALIDATOR-specify-macro) run_cmake(VALIDATOR) +if(CMAKE_HOST_WIN32 AND MINGW) + run_cmake(MSYSTEM_PREFIX) +endif() + run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp") run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=CREATED_LIBRARY) diff --git a/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt new file mode 100644 index 0000000..b1c8346 --- /dev/null +++ b/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt @@ -0,0 +1 @@ +-- MsysPfx_DIR='[^']*/Tests/RunCMake/find_package/MSYSTEM_PREFIX' diff --git a/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake new file mode 100644 index 0000000..2826e53 --- /dev/null +++ b/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake @@ -0,0 +1,5 @@ +set(ENV{MSYSTEM} "FAKEMSYS") +set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX") +enable_language(C) +find_package(MsysPfx QUIET) +message(STATUS "MsysPfx_DIR='${MsysPfx_DIR}'") diff --git a/Tests/RunCMake/find_package/MSYSTEM_PREFIX/MsysPfxConfig.cmake b/Tests/RunCMake/find_package/MSYSTEM_PREFIX/MsysPfxConfig.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_package/MSYSTEM_PREFIX/MsysPfxConfig.cmake diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake index 006757a..a93b811 100644 --- a/Tests/RunCMake/find_package/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake @@ -72,6 +72,10 @@ run_cmake(REGISTRY_VIEW-no-view) run_cmake(REGISTRY_VIEW-wrong-view) run_cmake(REGISTRY_VIEW-propagated) +if(CMAKE_HOST_WIN32 AND MINGW) + run_cmake(MSYSTEM_PREFIX) +endif() + if(CMAKE_HOST_WIN32) run_cmake(CMP0144-WARN-CaseInsensitive) run_cmake(CMP0144-OLD-CaseInsensitive) diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt deleted file mode 100644 index 8f3e7ca..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt deleted file mode 100644 index 8f3e7ca..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt deleted file mode 100644 index 8f3e7ca..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt deleted file mode 100644 index a502d78..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt deleted file mode 100644 index a502d78..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt deleted file mode 100644 index a502d78..0000000 --- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt +++ /dev/null @@ -1,9 +0,0 @@ --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' --- PATH_IN_ENV_PATH='' diff --git a/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt new file mode 100644 index 0000000..87ee490 --- /dev/null +++ b/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt @@ -0,0 +1 @@ +-- MSYSTEM_PREFIX_INCLUDE_DIR='[^']*/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include' diff --git a/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake new file mode 100644 index 0000000..d17a52f --- /dev/null +++ b/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake @@ -0,0 +1,5 @@ +set(ENV{MSYSTEM} "FAKEMSYS") +set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX") +enable_language(C) +find_path(MSYSTEM_PREFIX_INCLUDE_DIR NAMES msystem_prefix.h) +message(STATUS "MSYSTEM_PREFIX_INCLUDE_DIR='${MSYSTEM_PREFIX_INCLUDE_DIR}'") diff --git a/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include/msystem_prefix.h b/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include/msystem_prefix.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include/msystem_prefix.h diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt deleted file mode 100644 index bb2ceb7..0000000 --- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt deleted file mode 100644 index bb2ceb7..0000000 --- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt deleted file mode 100644 index bb2ceb7..0000000 --- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt +++ /dev/null @@ -1,4 +0,0 @@ --- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' diff --git a/Tests/RunCMake/find_path/RunCMakeTest.cmake b/Tests/RunCMake/find_path/RunCMakeTest.cmake index 9c76f2e..2ed4d7d 100644 --- a/Tests/RunCMake/find_path/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_path/RunCMakeTest.cmake @@ -12,6 +12,10 @@ run_cmake(VALIDATOR-undefined-function) run_cmake(VALIDATOR-specify-macro) run_cmake(VALIDATOR) +if(CMAKE_HOST_WIN32 AND MINGW) + run_cmake(MSYSTEM_PREFIX) +endif() + if(APPLE) run_cmake(FrameworksWithSubdirs) endif() diff --git a/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake b/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake index d099469..97b9b5d 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake @@ -1,7 +1,7 @@ if(WIN32) set(_check_files [[lib3]] - [[lib3/(lib)?lib3\.(dll\.a|lib)]] + [[lib3/(lib)?lib3\.(dll\.a|lib|l)]] [[lib4]] [[lib4/(lib)?lib4\.dll]] [[mybin]] @@ -10,8 +10,8 @@ if(WIN32) [[myinclude]] [[myinclude/obj3\.h]] [[mylib]] - [[mylib/(lib)?lib1\.(dll\.a|lib)]] - [[mylib/(lib)?lib2\.(a|lib)]] + [[mylib/(lib)?lib1\.(dll\.a|lib|l)]] + [[mylib/(lib)?lib2\.(a|lib|l)]] ) elseif(MSYS) set(_check_files diff --git a/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake b/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake index 6e13b84..f357c1e 100644 --- a/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake @@ -8,10 +8,10 @@ if(WIN32) [[include/obj2\.h]] [[include/obj3\.h]] [[lib]] - [[lib/(lib)?lib1\.(dll\.a|lib)]] - [[lib/(lib)?lib2\.(a|lib)]] + [[lib/(lib)?lib1\.(dll\.a|lib|l)]] + [[lib/(lib)?lib2\.(a|lib|l)]] [[lib3]] - [[lib3/(lib)?lib3\.(dll\.a|lib)]] + [[lib3/(lib)?lib3\.(dll\.a|lib|l)]] [[lib4]] [[lib4/(lib)?lib4\.dll]] ) diff --git a/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake b/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake index 91d5ef0..919ccf1 100644 --- a/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake @@ -1,7 +1,7 @@ if(WIN32) set(test123 [[bin/test1out\.exe;bin/test2deb\.exe;bin/test3exc\.exe]]) set(libtest45 [[bin/libtest4\.dll;bin/libtest4\.dll\.a;bin/libtest5ar\.a]]) - set(test45 [[bin/test4\.dll;bin/test4\.lib;bin/test5ar\.lib]]) + set(test45 [[bin/test4\.dll;bin/test4\.(lib|l);bin/test5ar\.(lib|l)]]) check_installed("^bin;(${libtest45};${test123})|(${test123};${test45})\$") elseif(MSYS) diff --git a/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake b/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake index 6245839..8137e92 100644 --- a/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake @@ -1 +1 @@ -check_installed([[^include;include/obj1\.h;lib;lib/(mylib\.lib|(lib|cyg)mylib\.a)$]]) +check_installed([[^include;include/obj1\.h;lib;lib/(mylib\.(lib|l)|(lib|cyg)mylib\.a)$]]) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake index dafc2a4..38635c3 100644 --- a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake @@ -1 +1 @@ -check_installed([[^static;static/(liblib\.a|lib\.lib)$]]) +check_installed([[^static;static/(liblib\.a|lib\.(lib|l))$]]) diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 8e4745a..0f3a6b7 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake @@ -31,7 +31,7 @@ if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode" set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) endif() - if (MINGW OR MSYS OR CYGWIN) + if (MINGW OR MSYS OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") set(LINK_EXTERN_LIBRARY_SUFFIX "") else() set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}") diff --git a/Tests/TestsWorkingDirectory/main.c b/Tests/TestsWorkingDirectory/main.c index ca72f21..f856339 100644 --- a/Tests/TestsWorkingDirectory/main.c +++ b/Tests/TestsWorkingDirectory/main.c @@ -5,7 +5,7 @@ #if defined(_WIN32) && \ (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) || \ - defined(__MINGW32__)) + defined(__MINGW32__) || defined(__ORANGEC__)) # include <direct.h> # include <io.h> diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index e45970d..6056030 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -75,6 +75,10 @@ { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] }, { include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] }, + { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] }, + { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] }, + { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] }, + { include: [ "<filesystem>", public, "<cm/filesystem>", public ] }, { include: [ "<optional>", public, "<cm/optional>", public ] }, { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] }, diff --git a/Utilities/Release/win/qt-5.12.1-win-x86-msvc-install.patch b/Utilities/Release/win/qt-5.15.10-win-x86-msvc-install.patch index 39a649e..de31d52 100644 --- a/Utilities/Release/win/qt-5.12.1-win-x86-msvc-install.patch +++ b/Utilities/Release/win/qt-5.15.10-win-x86-msvc-install.patch @@ -1,26 +1,26 @@ diff --git a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake -index 04ec302..75d5596 100644 +index 5bad1af..25bf3e3 100644 --- a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake +++ b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake -@@ -118,7 +118,7 @@ if (NOT TARGET Qt5::Core) - list(REMOVE_DUPLICATES Qt5Core_COMPILE_DEFINITIONS) - list(REMOVE_DUPLICATES Qt5Core_EXECUTABLE_COMPILE_FLAGS) +@@ -264,7 +264,7 @@ if (NOT TARGET Qt5::Core) + return() + endif() - set(_Qt5Core_LIB_DEPENDENCIES "") + set(_Qt5Core_LIB_DEPENDENCIES "${_qt5Core_install_prefix}/lib/qtpcre2.lib;netapi32.lib;version.lib") - add_library(Qt5::Core STATIC IMPORTED) + if(NOT Qt5_EXCLUDE_STATIC_DEPENDENCIES) diff --git a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake -index a07b953..2e07371 100644 +index d9966ad..308e4cb 100644 --- a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake +++ b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake -@@ -118,7 +118,7 @@ if (NOT TARGET Qt5::Widgets) - list(REMOVE_DUPLICATES Qt5Widgets_COMPILE_DEFINITIONS) - list(REMOVE_DUPLICATES Qt5Widgets_EXECUTABLE_COMPILE_FLAGS) +@@ -264,7 +264,7 @@ if (NOT TARGET Qt5::Widgets) + return() + endif() - set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core") + set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core;dwmapi.lib;uxtheme.lib") - add_library(Qt5::Widgets STATIC IMPORTED) + if(NOT Qt5_EXCLUDE_STATIC_DEPENDENCIES) diff --git a/Utilities/Release/win/qt-5.12.1-win-x86-msvc.ps1 b/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1 index d9e9617..e1d9ad1 100755 --- a/Utilities/Release/win/qt-5.12.1-win-x86-msvc.ps1 +++ b/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1 @@ -32,8 +32,8 @@ if ($env:VCToolsVersion -match '^(?<version>[0-9][0-9]\.[0-9])') { Write-Host "VCToolsVersion env var not set. Run this from a Visual Studio Command Prompt." } -$srcname = "qt-everywhere-src-5.12.1" -$pkgname = "qt-5.12.1-win-$arch-$toolset-1" +$srcname = "qt-everywhere-src-5.15.10" +$pkgname = "qt-5.15.10-win-$arch-$toolset-1" $topdir = $pwd.Path $srcdir = Join-Path $topdir $srcname $blddir = Join-Path $topdir "$pkgname-build" @@ -41,8 +41,9 @@ $prefix = Join-Path $topdir $pkgname # JOM if ( -not (Test-Path -Path "jom")) { - Invoke-WebRequest -Uri "http://download.qt-project.org/official_releases/jom/unstable-jom.zip" -OutFile jom.zip - if ($(Get-FileHash "jom.zip").Hash -ne '128fdd846fe24f8594eed37d1d8929a0ea78df563537c0c1b1861a635013fff8') { + Invoke-WebRequest -Uri "http://download.qt-project.org/official_releases/jom/jom_1_1_4.zip" -OutFile jom.zip + if ($(Get-FileHash "jom.zip").Hash -ne 'd533c1ef49214229681e90196ed2094691e8c4a0a0bef0b2c901debcb562682b') { + Write-Host "jom hash does not match" exit 1 } Expand-Archive -Path jom.zip -DestinationPath jom @@ -52,8 +53,9 @@ $jom = "$topdir\jom\jom.exe" # Qt Source if ( -not (Test-Path -Path $srcdir)) { - Invoke-WebRequest -Uri "https://download.qt.io/official_releases/qt/5.12/5.12.1/single/qt-everywhere-src-5.12.1.tar.xz" -OutFile qt.tar.xz - if ($(Get-FileHash "qt.tar.xz").Hash -ne 'caffbd625c7bc10ff8c5c7a27dbc7d84fa4de146975c0e1ffe904b514ccd6da4') { + Invoke-WebRequest -Uri "https://download.qt.io/archive/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.tar.xz" -OutFile qt.tar.xz + if ($(Get-FileHash "qt.tar.xz").Hash -ne 'b545cb83c60934adc9a6bbd27e2af79e5013de77d46f5b9f5bb2a3c762bf55ca') { + Write-Host "qt hash does not match" exit 1 } & $cmake -E tar xvf qt.tar.xz @@ -94,6 +96,7 @@ if ( -not (Test-Path -Path $blddir)) { -skip qtlocation ` -skip qtmultimedia ` -skip qtsensors ` + -skip qtserialbus ` -skip qtserialport ` -skip qtsvg ` -skip qtwayland ` @@ -110,7 +113,7 @@ if ( -not (Test-Path -Path $prefix)) { & $jom install # Patch the installation. Set-Location -Path $prefix - & $git apply -v (Join-Path $PSScriptRoot qt-5.12.1-win-x86-msvc-install.patch) + & $git apply -v (Join-Path $PSScriptRoot qt-5.15.10-win-x86-msvc-install.patch) } # Package Qt diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 4dae07a..70541cb 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash @@ -8,7 +8,7 @@ readonly name="curl" readonly ownership="Curl Upstream <curl-library@lists.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-8_2_1" +readonly tag="curl-8_3_0" readonly shortlog=false readonly paths=" CMake/* diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in index d4e4059..b8d8474 100644 --- a/Utilities/Sphinx/conf.py.in +++ b/Utilities/Sphinx/conf.py.in @@ -92,6 +92,7 @@ linkcheck_ignore = [r'about:|https://gitlab.kitware.com/cmake/community/-/wikis/ linkcheck_allowed_redirects = { r'https://cdash\.org': r'https://www\.cdash\.org/', + r'https://cmake.org/get-involved/': r'https://cmake.org/documentation/', r'https://docs\.nvidia\.com/cuda/': r'https://docs\.nvidia\.com/cuda/index\.html', r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments': r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments\?.*', r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313', diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake index 5605667..4e3fd5c 100644 --- a/Utilities/cmThirdPartyChecks.cmake +++ b/Utilities/cmThirdPartyChecks.cmake @@ -265,7 +265,6 @@ if(WIN32) set(HAVE_WINDOWS_H 1) set(HAVE_WINIOCTL_H 1) set(HAVE_WINSOCK2_H 1) - set(HAVE_WINSOCK_H 1) set(HAVE_WS2TCPIP_H 1) set(USE_WINCRYPT 1) # We do not need to build as a Windows App. diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c index 3dbba3c..38be522 100644 --- a/Utilities/cmcurl/CMake/CurlTests.c +++ b/Utilities/cmcurl/CMake/CurlTests.c @@ -510,7 +510,7 @@ main() { int main() { _Atomic int i = 1; - i = 0; // Force an atomic-write operation. + i = 0; /* Force an atomic-write operation. */ return i; } #endif diff --git a/Utilities/cmcurl/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake deleted file mode 100644 index ccddf42..0000000 --- a/Utilities/cmcurl/CMake/FindNSS.cmake +++ /dev/null @@ -1,40 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at https://curl.se/docs/copyright.html. -# -# You may opt to use, copy, modify, merge, publish, distribute and/or sell -# copies of the Software, and permit persons to whom the Software is -# furnished to do so, under the terms of the COPYING file. -# -# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# KIND, either express or implied. -# -# SPDX-License-Identifier: curl -# -########################################################################### -if(UNIX) - find_package(PkgConfig QUIET) - pkg_search_module(PC_NSS nss) -endif() -if(NOT PC_NSS_FOUND) - return() -endif() - -set(NSS_LIBRARIES ${PC_NSS_LINK_LIBRARIES}) -set(NSS_INCLUDE_DIRS ${PC_NSS_INCLUDE_DIRS}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NSS - REQUIRED_VARS NSS_LIBRARIES NSS_INCLUDE_DIRS - VERSION_VAR PC_NSS_VERSION) - -mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARIES) diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index fa1e458..762e6d1 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake @@ -38,7 +38,7 @@ if(HAVE_WINDOWS_H) set(_source_epilogue "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif") set(signature_call_conv "PASCAL") - if(HAVE_LIBWS2_32) + if(WIN32) set(CMAKE_REQUIRED_LIBRARIES ws2_32) endif() else() @@ -133,4 +133,3 @@ if(NOT CMAKE_CROSSCOMPILING) }" HAVE_POLL_FINE) endif() endif() - diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake index 3771237..44a1fc9 100644 --- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake +++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake @@ -23,30 +23,64 @@ ########################################################################### if(NOT UNIX) if(WIN32) + + set(HAVE_WINDOWS_H 1) + set(HAVE_WS2TCPIP_H 1) + set(HAVE_WINSOCK2_H 1) + + if(MINGW) + set(HAVE_SNPRINTF 1) + set(HAVE_UNISTD_H 1) + set(HAVE_INTTYPES_H 1) + set(HAVE_STRTOLL 1) + elseif(MSVC) + if(NOT MSVC_VERSION LESS 1800) + set(HAVE_INTTYPES_H 1) + set(HAVE_STRTOLL 1) + else() + set(HAVE_INTTYPES_H 0) + set(HAVE_STRTOLL 0) + endif() + if(NOT MSVC_VERSION LESS 1900) + set(HAVE_SNPRINTF 1) + else() + set(HAVE_SNPRINTF 0) + endif() + endif() + set(HAVE_LIBSOCKET 0) set(HAVE_GETHOSTNAME 1) set(HAVE_LIBZ 0) set(HAVE_ARPA_INET_H 0) + set(HAVE_ARPA_TFTP_H 0) set(HAVE_FCNTL_H 1) + set(HAVE_IFADDRS_H 0) set(HAVE_IO_H 1) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN_H 0) + set(HAVE_NETINET_TCP_H 0) set(HAVE_NET_IF_H 0) + set(HAVE_IOCTL_SIOCGIFADDR 0) + set(HAVE_POLL_H 0) set(HAVE_PWD_H 0) set(HAVE_SETJMP_H 1) set(HAVE_SIGNAL_H 1) set(HAVE_STDLIB_H 1) set(HAVE_STRINGS_H 0) set(HAVE_STRING_H 1) + set(HAVE_SYS_FILIO_H 0) + set(HAVE_SYS_IOCTL_H 0) set(HAVE_SYS_PARAM_H 0) set(HAVE_SYS_POLL_H 0) + set(HAVE_SYS_RESOURCE_H 0) set(HAVE_SYS_SELECT_H 0) set(HAVE_SYS_SOCKET_H 0) set(HAVE_SYS_SOCKIO_H 0) set(HAVE_SYS_STAT_H 1) set(HAVE_SYS_TIME_H 0) set(HAVE_SYS_TYPES_H 1) + set(HAVE_SYS_UN_H 0) set(HAVE_SYS_UTIME_H 1) set(HAVE_TERMIOS_H 0) set(HAVE_TERMIO_H 0) @@ -66,10 +100,12 @@ if(NOT UNIX) set(HAVE_GETPWUID 0) set(HAVE_GETEUID 0) set(HAVE_UTIME 1) - set(HAVE_RAND_EGD 0) set(HAVE_GMTIME_R 0) set(HAVE_GETHOSTBYNAME_R 0) set(HAVE_SIGNAL 1) + set(HAVE_LINUX_TCP_H 0) + set(HAVE_GLIBC_STRERROR_R 0) + set(HAVE_MACH_ABSOLUTE_TIME 0) set(HAVE_GETHOSTBYNAME_R_3 0) set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in index dbe4ed2..056907c 100644 --- a/Utilities/cmcurl/CMake/curl-config.cmake.in +++ b/Utilities/cmcurl/CMake/curl-config.cmake.in @@ -33,3 +33,6 @@ endif() include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") check_required_components("@PROJECT_NAME@") + +# Alias for either shared or static library +add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@) diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index ced5af4..c30a1d8 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt @@ -2,7 +2,9 @@ set(BUILD_CURL_EXE OFF CACHE INTERNAL "No curl exe") set(BUILD_DASHBOARD_REPORTS OFF CACHE INTERNAL "No curl dashboard reports") set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs") -set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build shared libraries") +set(BUILD_SHARED_LIBS OFF) +set(BUILD_STATIC_LIBS ON) +set(BUILD_STATIC_CURL OFF) set(CURL_USE_BEARSSL OFF) set(CURL_USE_GSSAPI OFF) set(CURL_USE_LIBPSL OFF) @@ -17,23 +19,29 @@ set(CURL_USE_SECTRANSP OFF) set(CURL_USE_WOLFSSL OFF) set(CURL_BROTLI OFF) set(CURL_DISABLE_ALTSVC ON) +set(CURL_DISABLE_AWS OFF) +set(CURL_DISABLE_BASIC_AUTH OFF) +set(CURL_DISABLE_BEARER_AUTH OFF) set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support") -set(CURL_DISABLE_CRYPTO_AUTH OFF CACHE INTERNAL "Do not disable curl crypto auth") set(CURL_DISABLE_DICT ON CACHE INTERNAL "Disable curl dict protocol?") +set(CURL_DISABLE_DIGEST_AUTH OFF) set(CURL_DISABLE_DOH OFF) set(CURL_DISABLE_FILE OFF CACHE INTERNAL "Disable curl file protocol?") +set(CURL_DISABLE_FORM_API OFF) set(CURL_DISABLE_FTP OFF CACHE INTERNAL "Disable curl ftp protocol?") set(CURL_DISABLE_GETOPTIONS OFF) set(CURL_DISABLE_GOPHER ON CACHE INTERNAL "Disable curl gopher protocol?") -set(CURL_DISABLE_HSTS ON) +set(CURL_DISABLE_HSTS OFF) set(CURL_DISABLE_HTTP_AUTH OFF) set(CURL_DISABLE_HTTP OFF CACHE INTERNAL "Disable curl http protocol?") set(CURL_DISABLE_IMAP ON CACHE INTERNAL "Disable curl imap protocol?") +set(CURL_DISABLE_KERBEROS_AUTH OFF) set(CURL_DISABLE_LDAP ON CACHE INTERNAL "Disable curl ldap protocol?") set(CURL_DISABLE_LDAPS ON CACHE INTERNAL "Disable curl ldaps protocol?") set(CURL_DISABLE_LIBCURL_OPTION OFF) set(CURL_DISABLE_MIME OFF) set(CURL_DISABLE_MQTT ON) +set(CURL_DISABLE_NEGOTIATE_AUTH OFF) set(CURL_DISABLE_NETRC OFF) set(CURL_DISABLE_NTLM OFF) set(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG OFF) @@ -77,6 +85,7 @@ 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(SHARE_LIB_OBJECT OFF) set(USE_LIBIDN2 ON) set(USE_NGHTTP2 ON) set(USE_NGTCP2 OFF) @@ -194,10 +203,9 @@ endif() # https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package # # The following variables are available: -# HAVE_RAND_EGD: `RAND_egd` present in OpenSSL +# HAVE_SSL_SET0_WBIO: `SSL_set0_wbio` present in OpenSSL # HAVE_AWSLC: OpenSSL is AWS-LC # HAVE_BORINGSSL: OpenSSL is BoringSSL -# HAVE_PK11_CREATEMANAGEDGENERICOBJECTL: `PK11_CreateManagedGenericObject` present in NSS # HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL # HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE # HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd @@ -248,6 +256,8 @@ 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_SHARED_LIBS "Build shared libraries" ON) +option(BUILD_STATIC_LIBS "Build shared 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) if(WIN32) option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF) @@ -297,6 +307,32 @@ if(NOT DEFINED CMAKE_DEBUG_POSTFIX) endif() endif() +set(LIB_STATIC "libcurl_static") +set(LIB_SHARED "libcurl_shared") + +if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) + set(BUILD_STATIC_LIBS ON) +endif() +if(NOT BUILD_STATIC_CURL AND NOT BUILD_SHARED_LIBS) + set(BUILD_STATIC_CURL ON) +elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS) + set(BUILD_STATIC_CURL OFF) +endif() + +# 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. +if(BUILD_SHARED_LIBS) + set(LIB_SELECTED ${LIB_SHARED}) +else() + set(LIB_SELECTED ${LIB_STATIC}) +endif() + # initialize CURL_LIBS set(CURL_LIBS "") @@ -317,14 +353,29 @@ option(CURL_DISABLE_ALTSVC "disables alt-svc support" OFF) mark_as_advanced(CURL_DISABLE_ALTSVC) option(CURL_DISABLE_COOKIES "disables cookies support" OFF) mark_as_advanced(CURL_DISABLE_COOKIES) -option(CURL_DISABLE_CRYPTO_AUTH "disables cryptographic authentication" OFF) -mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH) +option(CURL_DISABLE_BASIC_AUTH "disables Basic authentication" OFF) +mark_as_advanced(CURL_DISABLE_BASIC_AUTH) +option(CURL_DISABLE_BEARER_AUTH "disables Bearer authentication" OFF) +mark_as_advanced(CURL_DISABLE_BEARER_AUTH) +option(CURL_DISABLE_DIGEST_AUTH "disables Digest authentication" OFF) +mark_as_advanced(CURL_DISABLE_DIGEST_AUTH) +option(CURL_DISABLE_KERBEROS_AUTH "disables Kerberos authentication" OFF) +mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH) +option(CURL_DISABLE_NEGOTIATE_AUTH "disables negotiate authentication" OFF) +mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) +option(CURL_DISABLE_AWS "disables AWS-SIG4" OFF) +mark_as_advanced(CURL_DISABLE_AWS) option(CURL_DISABLE_DICT "disables DICT" OFF) mark_as_advanced(CURL_DISABLE_DICT) option(CURL_DISABLE_DOH "disables DNS-over-HTTPS" OFF) mark_as_advanced(CURL_DISABLE_DOH) option(CURL_DISABLE_FILE "disables 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) +mark_as_advanced(CURL_DISABLE_FORM_API) +endif() option(CURL_DISABLE_FTP "disables 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) @@ -496,8 +547,10 @@ check_library_exists_concat("socket" connect HAVE_LIBSOCKET) check_function_exists(gethostname HAVE_GETHOSTNAME) if(WIN32) - check_library_exists_concat("ws2_32" getch HAVE_LIBWS2_32) - check_library_exists_concat("winmm" getch HAVE_LIBWINMM) + list(APPEND CURL_LIBS "ws2_32") + if(USE_LIBRTMP) + list(APPEND CURL_LIBS "winmm") + endif() # Matching logic used for Curl_win32_random() if(MINGW) @@ -515,24 +568,23 @@ endif() if(0) # This code not needed for building within CMake. # check SSL libraries -# TODO support GnuTLS option(CURL_ENABLE_SSL "Enable SSL support" ON) if(APPLE) - cmake_dependent_option(CURL_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF) + cmake_dependent_option(CURL_USE_SECTRANSP "Enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF) endif() if(WIN32) - cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) + cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON CURL_USE_SCHANNEL OFF) endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) -cmake_dependent_option(CURL_USE_NSS "Enable NSS 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_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) set(openssl_default ON) -if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_NSS OR CURL_USE_WOLFSSL) +if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL) set(openssl_default OFF) endif() cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF) @@ -545,7 +597,6 @@ count_true(enabled_ssl_options_count CURL_USE_OPENSSL CURL_USE_MBEDTLS CURL_USE_BEARSSL - CURL_USE_NSS CURL_USE_WOLFSSL ) if(enabled_ssl_options_count GREATER "1") @@ -603,9 +654,6 @@ if(CURL_USE_OPENSSL) endif() set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) - if(NOT DEFINED HAVE_RAND_EGD) - check_symbol_exists(RAND_egd "${CURL_INCLUDES}" HAVE_RAND_EGD) - endif() if(NOT DEFINED HAVE_BORINGSSL) check_symbol_exists(OPENSSL_IS_BORINGSSL "openssl/base.h" HAVE_BORINGSSL) endif() @@ -647,23 +695,13 @@ if(CURL_USE_WOLFSSL) include_directories(${WolfSSL_INCLUDE_DIRS}) endif() -if(CURL_USE_NSS) - find_package(NSS REQUIRED) - include_directories(${NSS_INCLUDE_DIRS}) - list(APPEND CURL_LIBS ${NSS_LIBRARIES}) - set(SSL_ENABLED ON) - set(USE_NSS ON) - if(NOT DEFINED HAVE_PK11_CREATEMANAGEDGENERICOBJECT) - cmake_push_check_state() - set(CMAKE_REQUIRED_INCLUDES ${NSS_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_LIBRARIES ${NSS_LIBRARIES}) - check_symbol_exists(PK11_CreateManagedGenericObject "pk11pub.h" HAVE_PK11_CREATEMANAGEDGENERICOBJECT) - cmake_pop_check_state() - endif() +if(CURL_USE_GNUTLS) + set(SSL_ENABLED ON) + set(USE_GNUTLS ON) endif() # Keep ZLIB detection after TLS detection, -# and before calling CheckQuicSupportInOpenSSL. +# and before calling openssl_check_symbol_exists(). set(HAVE_LIBZ OFF) set(USE_ZLIB OFF) @@ -694,6 +732,7 @@ 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 <v3.15 CMake. list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) include_directories(${BROTLI_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS}) @@ -718,50 +757,50 @@ if(CURL_ZSTD) endif() endif() -option(USE_NGHTTP2 "Use Nghttp2 library" OFF) +# Check symbol in OpenSSL-like TLS backends. +macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE) + cmake_push_check_state() + if(USE_OPENSSL) + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") + if(HAVE_LIBZ) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") + endif() + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") + if(NOT HAVE_MINGW_ORIGINAL) + list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL + endif() + endif() + elseif(USE_WOLFSSL) + set(CMAKE_REQUIRED_INCLUDES "${WolfSSL_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${WolfSSL_LIBRARIES}") + if(HAVE_LIBZ) + list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") # Public wolfSSL headers require zlib headers + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") + endif() + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32") + endif() + list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T) # to pull in stdint.h (as of wolfSSL v5.5.4) + endif() + check_symbol_exists("${SYMBOL}" "${FILES}" "${VARIABLE}") + cmake_pop_check_state() +endmacro() + +if(USE_OPENSSL OR USE_WOLFSSL) + if(NOT DEFINED HAVE_SSL_SET0_WBIO) + openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO) + endif() +endif() + +option(USE_NGHTTP2 "Use nghttp2 library" OFF) if(USE_NGHTTP2) find_package(NGHTTP2 REQUIRED) include_directories(${NGHTTP2_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES}) endif() -function(CheckQuicSupportInOpenSSL) - # Be sure that the OpenSSL/wolfSSL library actually supports QUIC. - if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) - cmake_push_check_state() - if(USE_WOLFSSL) - set(CMAKE_REQUIRED_INCLUDES "${WolfSSL_INCLUDE_DIRS}") - set(CMAKE_REQUIRED_LIBRARIES "${WolfSSL_LIBRARIES}") - if(HAVE_LIBZ) - list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") # Public wolfSSL headers require zlib headers - list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") - endif() - if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32") - endif() - list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T) # to pull in stdint.h (as of wolfSSL v5.5.4) - check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) - else() - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") - if(HAVE_LIBZ) - list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") - endif() - if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") - if(NOT HAVE_MINGW_ORIGINAL) - list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL - endif() - endif() - check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) - endif() - cmake_pop_check_state() - endif() - if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) - message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR") - endif() -endfunction() - option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF) if(USE_NGTCP2) if(USE_OPENSSL OR USE_WOLFSSL) @@ -772,7 +811,19 @@ if(USE_NGTCP2) else() find_package(NGTCP2 REQUIRED quictls) endif() - CheckQuicSupportInOpenSSL() + + # Be sure that the OpenSSL/wolfSSL library actually supports QUIC. + if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD) + if(USE_OPENSSL) + openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + elseif(USE_WOLFSSL) + openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD) + endif() + endif() + if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD) + message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR") + endif() + elseif(USE_GNUTLS) # TODO add GnuTLS support as vtls library. find_package(NGTCP2 REQUIRED GnuTLS) @@ -822,10 +873,8 @@ if(NOT CURL_DISABLE_LDAP) if(WIN32) option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) if(USE_WIN32_LDAP) - check_library_exists_concat("wldap32" cldap_open HAVE_WLDAP32) - if(NOT HAVE_WLDAP32) - set(USE_WIN32_LDAP OFF) - elseif(NOT CURL_DISABLE_LDAPS) + list(APPEND CURL_LIBS "wldap32") + if(NOT CURL_DISABLE_LDAPS) set(HAVE_LDAP_SSL ON) endif() endif() @@ -1089,7 +1138,7 @@ elseif("${CURL_CA_PATH}" STREQUAL "none") unset(CURL_CA_PATH CACHE) elseif("${CURL_CA_PATH}" STREQUAL "auto") unset(CURL_CA_PATH CACHE) - if(NOT CMAKE_CROSSCOMPILING AND NOT USE_NSS) + if(NOT CMAKE_CROSSCOMPILING) set(CURL_CA_PATH_AUTODETECT TRUE) endif() else() @@ -1143,10 +1192,8 @@ if(NOT UNIX) check_include_file_concat("windows.h" HAVE_WINDOWS_H) check_include_file_concat("ws2tcpip.h" HAVE_WS2TCPIP_H) check_include_file_concat("winsock2.h" HAVE_WINSOCK2_H) - check_include_file_concat("wincrypt.h" HAVE_WINCRYPT_H) else() set(HAVE_WINDOWS_H 0) - set(HAVE_WINSOCK_H 0) set(HAVE_WS2TCPIP_H 0) set(HAVE_WINSOCK2_H 0) endif() @@ -1184,7 +1231,6 @@ check_include_file_concat("poll.h" HAVE_POLL_H) check_include_file_concat("pwd.h" HAVE_PWD_H) check_include_file_concat("setjmp.h" HAVE_SETJMP_H) check_include_file_concat("signal.h" HAVE_SIGNAL_H) -check_include_file_concat("ssl.h" HAVE_SSL_H) check_include_file_concat("stdatomic.h" HAVE_STDATOMIC_H) check_include_file_concat("stdbool.h" HAVE_STDBOOL_H) check_include_file_concat("stdint.h" HAVE_STDINT_H) @@ -1199,7 +1245,6 @@ check_include_file_concat("unistd.h" HAVE_UNISTD_H) check_include_file_concat("utime.h" HAVE_UTIME_H) check_include_file_concat("stddef.h" HAVE_STDDEF_H) -check_include_file_concat("sys/utsname.h" HAVE_SYS_UTSNAME_H) check_type_size(size_t SIZEOF_SIZE_T) check_type_size(ssize_t SIZEOF_SSIZE_T) @@ -1211,7 +1256,7 @@ if(NOT CMAKE_CROSSCOMPILING) endif() # Check for some functions that are used -if(HAVE_LIBWS2_32) +if(WIN32) set(CMAKE_REQUIRED_LIBRARIES ws2_32) elseif(HAVE_LIBSOCKET) set(CMAKE_REQUIRED_LIBRARIES socket) @@ -1470,11 +1515,8 @@ if(WIN32) # Use the manifest embedded in the Windows Resource set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DCURL_EMBED_MANIFEST") - # Check if crypto functions in wincrypt.h are actually available - if(HAVE_WINCRYPT_H) - check_symbol_exists(CryptAcquireContext "windows.h;wincrypt.h" USE_WINCRYPT) - endif() - if(USE_WINCRYPT) + # We use crypto functions that are not available for UWP apps + if(NOT WINDOWS_STORE) set(USE_WIN32_CRYPTO ON) endif() @@ -1551,7 +1593,22 @@ function(transform_makefile_inc INPUT_FILE OUTPUT_FILE) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${INPUT_FILE}") endfunction() -if(0) # This code not needed for building within CMake. +#----------------------------------------------------------------------------- +# CMake-specific curl code. +add_subdirectory(lib) + +add_executable(curltest curltest.c) +target_link_libraries(curltest cmcurl) + +if(BUILD_TESTING AND CMAKE_CURL_TEST_URL) + add_test(curl curltest ${CMAKE_CURL_TEST_URL}) +endif() + +install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl) + +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}) @@ -1559,7 +1616,6 @@ 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") -endif() if(USE_MANUAL) add_subdirectory(docs) @@ -1571,19 +1627,6 @@ if(BUILD_CURL_EXE) add_subdirectory(src) endif() -#----------------------------------------------------------------------------- -# CMake-specific curl code. -add_executable(curltest curltest.c) -target_link_libraries(curltest cmcurl) - -if(BUILD_TESTING AND CMAKE_CURL_TEST_URL) - add_test(curl curltest ${CMAKE_CURL_TEST_URL}) -endif() - -install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl) -#----------------------------------------------------------------------------- - -if(0) # This code not needed for building within CMake. cmake_dependent_option(BUILD_TESTING "Build tests" ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" OFF) @@ -1602,8 +1645,8 @@ endmacro() # NTLM support requires crypto function adaptions from various SSL libs # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS -if(NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND - (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO)) +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() @@ -1625,16 +1668,16 @@ _add_if("GSS-API" HAVE_GSSAPI) _add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) _add_if("HSTS" NOT CURL_DISABLE_HSTS) # TODO SSP1 missing for SPNEGO -_add_if("SPNEGO" NOT CURL_DISABLE_CRYPTO_AUTH AND +_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -_add_if("Kerberos" NOT CURL_DISABLE_CRYPTO_AUTH AND +_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) # NTLM support requires crypto function adaptions from various SSL libs # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS -_add_if("NTLM" NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND +_add_if("NTLM" NOT (CURL_DISABLE_NTLM) AND (use_curl_ntlm_core OR USE_WINDOWS_SSPI)) # TODO missing option (autoconf: --enable-ntlm-wb) -_add_if("NTLM_WB" NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND +_add_if("NTLM_WB" NOT (CURL_DISABLE_NTLM) AND (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED) # TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP @@ -1644,7 +1687,7 @@ _add_if("HTTP2" USE_NGHTTP2) _add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) _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_NSS +_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) @@ -1702,20 +1745,22 @@ _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("NSS" SSL_ENABLED AND USE_NSS) _add_if("wolfSSL" SSL_ENABLED AND USE_WOLFSSL) +_add_if("GnuTLS" SSL_ENABLED AND USE_GNUTLS) + if(_items) 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() # curl-config needs the following options to be set. set(CC "${CMAKE_C_COMPILER}") # TODO probably put a -D... options here? set(CONFIGURE_OPTIONS "") -# TODO when to set "-DCURL_STATICLIB" for CPPFLAG_CURL_STATICLIB? -set(CPPFLAG_CURL_STATICLIB "") set(CURLVERSION "${CURL_VERSION}") set(exec_prefix "\${prefix}") set(includedir "\${prefix}/include") @@ -1745,12 +1790,17 @@ foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS}) endforeach() if(BUILD_SHARED_LIBS) set(ENABLE_SHARED "yes") - set(ENABLE_STATIC "no") set(LIBCURL_NO_SHARED "") + set(CPPFLAG_CURL_STATICLIB "") else() set(ENABLE_SHARED "no") - set(ENABLE_STATIC "yes") set(LIBCURL_NO_SHARED "${LIBCURL_LIBS}") + set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB") +endif() +if(BUILD_STATIC_LIBS) + set(ENABLE_STATIC "yes") +else() + set(ENABLE_STATIC "no") endif() # "a" (Linux) or "lib" (Windows) string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") @@ -1841,4 +1891,3 @@ if(NOT TARGET curl_uninstall) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake) endif() -endif() diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index b668dc0..d3ebb3b 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h @@ -161,7 +161,7 @@ typedef enum { CURLSSLBACKEND_GNUTLS = 2, CURLSSLBACKEND_NSS = 3, CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ - CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_GSKIT CURL_DEPRECATED(8.3.0, "") = 5, CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, CURLSSLBACKEND_WOLFSSL = 7, CURLSSLBACKEND_SCHANNEL = 8, @@ -2731,6 +2731,20 @@ CURL_EXTERN CURLcode curl_global_init_mem(long flags, */ CURL_EXTERN void curl_global_cleanup(void); +/* + * NAME curl_global_trace() + * + * DESCRIPTION + * + * curl_global_trace() can be invoked at application start to + * configure which components in curl should participate in tracing. + + * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the + * curl_version_info_data.features flag (fetch by curl_version_info()). + + */ +CURL_EXTERN CURLcode curl_global_trace(const char *config); + /* linked-list structure for the CURLOPT_QUOTE option (and other) */ struct curl_slist { char *data; @@ -2810,13 +2824,14 @@ CURL_EXTERN void curl_slist_free_all(struct curl_slist *list); */ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); -/* info about the certificate chain, only for OpenSSL, GnuTLS, Schannel, NSS - and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +/* info about the certificate chain, for SSL backends that support it. Asked + 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 - linked list with textual information in the - format "name: value" */ + linked list with textual information for a + certificate in the format "name:content". + eg "Subject:foo", "Issuer:bar", etc. */ }; /* Information about the SSL library used and the respective internal SSL diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 4c39a6d..7004fad 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.2.1" +#define LIBCURL_VERSION "8.3.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 2 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 3 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -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 0x080201 +#define LIBCURL_VERSION_NUM 0x080300 /* * This is the date and time when the full source package was created. The diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h index e652a65..dc5664b 100644 --- a/Utilities/cmcurl/include/curl/mprintf.h +++ b/Utilities/cmcurl/include/curl/mprintf.h @@ -32,18 +32,36 @@ extern "C" { #endif -CURL_EXTERN int curl_mprintf(const char *format, ...); -CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); -CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +#if (defined(__GNUC__) || defined(__clang__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) && !defined(CURL_NO_FMT_CHECKS) +#define CURL_TEMP_PRINTF(a,b) __attribute__ ((format(printf, a, b))) +#else +#define CURL_TEMP_PRINTF(a,b) +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...) + CURL_TEMP_PRINTF(2, 3); CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, - const char *format, ...); -CURL_EXTERN int curl_mvprintf(const char *format, va_list args); -CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); -CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); + const char *format, ...) CURL_TEMP_PRINTF(3, 4); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args) + CURL_TEMP_PRINTF(2, 0); CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, - const char *format, va_list args); -CURL_EXTERN char *curl_maprintf(const char *format, ...); -CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + const char *format, va_list args) + CURL_TEMP_PRINTF(3, 0); +CURL_EXTERN char *curl_maprintf(const char *format, ...) + CURL_TEMP_PRINTF(1, 2); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args) + CURL_TEMP_PRINTF(1, 0); + +#undef CURL_TEMP_PRINTF #ifdef __cplusplus } /* end of extern "C" */ diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index 30a3d93..13b55b7 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h @@ -118,7 +118,7 @@ typedef struct CURLMsg CURLMsg; struct curl_waitfd { curl_socket_t fd; short events; - short revents; /* not supported yet */ + short revents; }; /* diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index b2640c8..97e0d03 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h @@ -347,6 +347,24 @@ # define CURL_PULL_SYS_TYPES_H 1 # define CURL_PULL_SYS_SOCKET_H 1 +#elif defined(__hpux) /* HP aCC compiler */ +# if !defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + /* ===================================== */ /* KEEP MSVC THE PENULTIMATE ENTRY */ /* ===================================== */ diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index b3504b6..88cdeb3 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h @@ -96,7 +96,8 @@ typedef enum { #define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the scheme is unknown. */ #define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */ +#define CURLU_PUNYCODE (1<<12) /* get the host name in punycode */ +#define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ typedef struct Curl_URL CURLU; diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index d5d3aa6..c4c6956 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt @@ -21,18 +21,10 @@ # SPDX-License-Identifier: curl # ########################################################################### -set(LIB_NAME cmcurl) -set(LIBCURL_OUTPUT_NAME cmcurl) +set(LIB_NAME libcurl) +set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library") add_definitions(-DBUILDING_LIBCURL) -if(BUILD_SHARED_LIBS) - set(CURL_STATICLIB NO) -else() - set(CURL_STATICLIB YES) -endif() - -# Use: -# * CURL_STATICLIB configure_file(curl_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) @@ -43,10 +35,6 @@ list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h ) -if(WIN32 AND NOT CURL_STATICLIB) - list(APPEND CSOURCES libcurl.rc) -endif() - # The rest of the build include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) @@ -59,6 +47,17 @@ if(USE_ARES) include_directories(${CARES_INCLUDE_DIR}) endif() +#----------------------------------------------------------------------------- +# CMake-specific curl code. +unset(LIBCURL_OUTPUT_NAME CACHE) + +add_library(cmcurl ${HHEADERS} ${CSOURCES}) +target_compile_definitions(cmcurl INTERFACE CURL_STATICLIB) +target_link_libraries(cmcurl PRIVATE ${CURL_LIBS}) +if(WIN32 AND CMake_BUILD_PCH) + target_precompile_headers(cmcurl PRIVATE "curl_setup.h" "curl_sspi.h" "${CURL_SOURCE_DIR}/include/curl/curl.h") +endif() + # For windows we want to install OPENSSL_LIBRARIES dlls # and also copy them into the build tree so that testing # can find them. @@ -83,13 +82,9 @@ if(CURL_USE_OPENSSL AND OPENSSL_FOUND AND WIN32) endif() endif() -add_library( - ${LIB_NAME} - ${HHEADERS} ${CSOURCES} - ${CMAKE_CURL_SSL_DLLS} - ) +return() # The rest of this file is not needed for building within CMake. +#----------------------------------------------------------------------------- -if(0) # This code not needed for building within CMake. add_library( curlu # special libcurlu library just for unittests STATIC @@ -97,43 +92,17 @@ add_library( ${HHEADERS} ${CSOURCES} ) target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) -endif() - -add_library( - ${PROJECT_NAME}::${LIB_NAME} - ALIAS ${LIB_NAME} - ) if(ENABLE_CURLDEBUG) # We must compile memdebug.c separately to avoid memdebug.h redefinitions # being applied to memdebug.c itself. set_source_files_properties(memdebug.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() - -if(NOT BUILD_SHARED_LIBS) - set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) -endif() - -target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS}) -if(0) # This code not needed for building within CMake. target_link_libraries(curlu PRIVATE ${CURL_LIBS}) -endif() -if(0) # This code not needed for building within CMake. transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) -endif() - -set_target_properties(${LIB_NAME} PROPERTIES - COMPILE_DEFINITIONS BUILDING_LIBCURL - OUTPUT_NAME ${LIBCURL_OUTPUT_NAME} - ) - -if(WIN32 AND CMake_BUILD_PCH) - target_precompile_headers(${LIB_NAME} PRIVATE "curl_setup.h" "curl_sspi.h" "${CURL_SOURCE_DIR}/include/curl/curl.h") -endif() -if(0) # This code not needed for building within CMake. if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR @@ -150,39 +119,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}") set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}") - - set_target_properties(${LIB_NAME} PROPERTIES - VERSION ${CMAKEVERSION} - SOVERSION ${CMAKESONAME} - ) - -endif() - - -if(HIDES_CURL_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") - set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_FLAGS ${CURL_CFLAG_SYMBOLS_HIDE}) -endif() - -# Remove the "lib" prefix since the library is already named "libcurl". -set_target_properties(${LIB_NAME} PROPERTIES PREFIX "") -set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "") - -if(CURL_HAS_LTO) - set_target_properties(${LIB_NAME} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE - INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) +else() + unset(CMAKESONAME) endif() -if(WIN32) - if(BUILD_SHARED_LIBS) - if(MSVC) - # Add "_imp" as a suffix before the extension to avoid conflicting with - # the statically linked "libcurl.lib" - set_target_properties(${LIB_NAME} PROPERTIES IMPORT_SUFFIX "_imp.lib") - endif() - endif() -elseif(NOT CMAKE_CROSSCOMPILING) +if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) # on not-Windows and not-crosscompiling, check for writable argv[] include(CheckCSourceRuns) check_c_source_runs(" @@ -195,22 +136,147 @@ int main(int argc, char **argv) HAVE_WRITABLE_ARGV) endif() -target_include_directories(${LIB_NAME} INTERFACE - $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> - $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) +## Library definition + +# Add "_imp" as a suffix before the extension to avoid conflicting with +# the statically linked "libcurl.lib" (typically with MSVC) +if(WIN32 AND + NOT IMPORT_LIB_SUFFIX AND + CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL CMAKE_IMPORT_LIBRARY_SUFFIX) + set(IMPORT_LIB_SUFFIX "_imp") +endif() + +# Whether to do a single compilation pass for libcurl sources and reuse these +# objects to generate both static and shared target. +if(NOT DEFINED SHARE_LIB_OBJECT) + # Enable it by default on platforms where PIC is the default for both shared + # and static and there is a way to tell the linker which libcurl symbols it + # should export (vs. marking these symbols exportable at compile-time). + if(WIN32) + set(SHARE_LIB_OBJECT ON) + else() + # On other platforms, make it an option disabled by default + set(SHARE_LIB_OBJECT OFF) + endif() +endif() + +if(SHARE_LIB_OBJECT) + set(LIB_OBJECT "libcurl_object") + add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) + target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) + set_target_properties(${LIB_OBJECT} PROPERTIES + COMPILE_DEFINITIONS "BUILDING_LIBCURL" + INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB" + POSITION_INDEPENDENT_CODE ON) + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_target_properties(${LIB_OBJECT} PROPERTIES + COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS" + COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_OBJECT} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + endif() + + target_include_directories(${LIB_OBJECT} INTERFACE + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> + $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) + + set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>) +else() + set(LIB_SOURCE ${HHEADERS} ${CSOURCES}) +endif() + +# 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}) + target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS}) + # Remove the "lib" prefix since the library is already named "libcurl". + set_target_properties(${LIB_STATIC} PROPERTIES + PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" + SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" + COMPILE_DEFINITIONS "BUILDING_LIBCURL" + INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_target_properties(${LIB_STATIC} PROPERTIES + COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS" + COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_STATIC} PROPERTIES + 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 + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> + $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) +endif() + +if(BUILD_SHARED_LIBS) + list(APPEND libcurl_export ${LIB_SHARED}) + add_library(${LIB_SHARED} SHARED ${LIB_SOURCE}) + add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) + if(WIN32) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc ${CURL_SOURCE_DIR}/libcurl.def) + endif() + target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS}) + # Remove the "lib" prefix since the library is already named "libcurl". + set_target_properties(${LIB_SHARED} PROPERTIES + PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" + IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" + COMPILE_DEFINITIONS "BUILDING_LIBCURL" + POSITION_INDEPENDENT_CODE ON) + if(HIDES_CURL_PRIVATE_SYMBOLS) + set_target_properties(${LIB_SHARED} PROPERTIES + COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS" + COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + endif() + if(CURL_HAS_LTO) + set_target_properties(${LIB_SHARED} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) + endif() + if(CMAKEVERSION AND CMAKESONAME) + set_target_properties(${LIB_SHARED} PROPERTIES + VERSION ${CMAKEVERSION} SOVERSION ${CMAKESONAME}) + endif() + + target_include_directories(${LIB_SHARED} INTERFACE + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> + $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) +endif() + +add_library(${LIB_NAME} ALIAS ${LIB_SELECTED}) +add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED}) if(CURL_ENABLE_EXPORT_TARGET) - install(TARGETS ${LIB_NAME} - EXPORT ${TARGETS_EXPORT_NAME} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) + if(BUILD_STATIC_LIBS) + install(TARGETS ${LIB_STATIC} + EXPORT ${TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif() + if(BUILD_SHARED_LIBS) + install(TARGETS ${LIB_SHARED} + EXPORT ${TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif() - export(TARGETS ${LIB_NAME} + export(TARGETS ${libcurl_export} FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake NAMESPACE ${PROJECT_NAME}:: ) endif() - -endif() diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index cc7d287..a08180b 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc @@ -44,13 +44,11 @@ LIB_VAUTH_HFILES = \ LIB_VTLS_CFILES = \ vtls/bearssl.c \ - vtls/gskit.c \ vtls/gtls.c \ vtls/hostcheck.c \ vtls/keylog.c \ vtls/mbedtls.c \ vtls/mbedtls_threadlock.c \ - vtls/nss.c \ vtls/openssl.c \ vtls/rustls.c \ vtls/schannel.c \ @@ -62,13 +60,11 @@ LIB_VTLS_CFILES = \ LIB_VTLS_HFILES = \ vtls/bearssl.h \ - vtls/gskit.h \ vtls/gtls.h \ vtls/hostcheck.h \ vtls/keylog.h \ vtls/mbedtls.h \ vtls/mbedtls_threadlock.h \ - vtls/nssg.h \ vtls/openssl.h \ vtls/rustls.h \ vtls/schannel.h \ @@ -126,7 +122,6 @@ LIB_CFILES = \ curl_get_line.c \ curl_gethostname.c \ curl_gssapi.c \ - curl_log.c \ curl_memrchr.c \ curl_multibyte.c \ curl_ntlm_core.c \ @@ -137,6 +132,7 @@ LIB_CFILES = \ curl_sasl.c \ curl_sspi.c \ curl_threads.c \ + curl_trc.c \ dict.c \ doh.c \ dynbuf.c \ @@ -262,7 +258,6 @@ LIB_HFILES = \ curl_hmac.h \ curl_krb5.h \ curl_ldap.h \ - curl_log.h \ curl_md4.h \ curl_md5.h \ curl_memory.h \ @@ -280,6 +275,7 @@ LIB_HFILES = \ curl_sha256.h \ curl_sspi.h \ curl_threads.h \ + curl_trc.h \ curlx.h \ dict.h \ doh.h \ diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 11009d5..22b0b69 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c @@ -38,6 +38,8 @@ #include "warnless.h" #include "fopen.h" #include "rename.h" +#include "strdup.h" +#include "inet_pton.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -97,19 +99,39 @@ static struct altsvc *altsvc_createid(const char *srchost, { struct altsvc *as = calloc(sizeof(struct altsvc), 1); size_t hlen; + size_t dlen; if(!as) return NULL; hlen = strlen(srchost); + dlen = strlen(dsthost); DEBUGASSERT(hlen); - as->src.host = strdup(srchost); + DEBUGASSERT(dlen); + if(!hlen || !dlen) + /* bad input */ + return NULL; + if((hlen > 2) && srchost[0] == '[') { + /* IPv6 address, strip off brackets */ + srchost++; + hlen -= 2; + } + else if(srchost[hlen - 1] == '.') + /* strip off trailing dot */ + hlen--; + if((dlen > 2) && dsthost[0] == '[') { + /* IPv6 address, strip off brackets */ + dsthost++; + dlen -= 2; + } + + as->src.host = Curl_memdup(srchost, hlen + 1); if(!as->src.host) goto error; - if(hlen && (srchost[hlen - 1] == '.')) - /* strip off trailing any dot */ - as->src.host[--hlen] = 0; - as->dst.host = strdup(dsthost); + as->src.host[hlen] = 0; + + as->dst.host = Curl_memdup(dsthost, dlen + 1); if(!as->dst.host) goto error; + as->dst.host[dlen] = 0; as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -231,18 +253,40 @@ fail: static CURLcode altsvc_out(struct altsvc *as, FILE *fp) { struct tm stamp; + const char *dst6_pre = ""; + const char *dst6_post = ""; + const char *src6_pre = ""; + const char *src6_post = ""; CURLcode result = Curl_gmtime(as->expires, &stamp); if(result) return result; - +#ifdef ENABLE_IPV6 + else { + char ipv6_unused[16]; + if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) { + dst6_pre = "["; + dst6_post = "]"; + } + if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) { + src6_pre = "["; + src6_post = "]"; + } + } +#endif fprintf(fp, - "%s %s %u " - "%s %s %u " + "%s %s%s%s %u " + "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " "%u %d\n", - Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, - Curl_alpnid2str(as->dst.alpnid), as->dst.host, as->dst.port, + Curl_alpnid2str(as->src.alpnid), + src6_pre, as->src.host, src6_post, + as->src.port, + + Curl_alpnid2str(as->dst.alpnid), + dst6_pre, as->dst.host, dst6_post, + as->dst.port, + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, as->persist, as->prio); @@ -500,9 +544,21 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, if(*p != ':') { /* host name starts here */ const char *hostp = p; - while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) - p++; - len = p - hostp; + if(*p == '[') { + /* pass all valid IPv6 letters - does not handle zone id */ + len = strspn(++p, "0123456789abcdefABCDEF:."); + if(p[len] != ']') + /* invalid host syntax, bail out */ + break; + /* we store the IPv6 numerical address *with* brackets */ + len += 2; + p = &p[len-1]; + } + else { + while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) + p++; + len = p - hostp; + } if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { infof(data, "Excessive alt-svc host name, ignoring."); valid = FALSE; diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h index 7997ede..c99d963 100644 --- a/Utilities/cmcurl/lib/amigaos.h +++ b/Utilities/cmcurl/lib/amigaos.h @@ -39,4 +39,3 @@ void Curl_amiga_cleanup(void); #endif #endif /* HEADER_CURL_AMIGAOS_H */ - diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 19fe853..e73e41d 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c @@ -110,7 +110,7 @@ struct thread_data { }; /* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. + 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 @@ -120,6 +120,8 @@ struct thread_data { */ #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 +#define CARES_TIMEOUT_PER_ATTEMPT 2000 + /* * Curl_resolver_global_init() - the generic low-level asynchronous name * resolve API. Called from curl_global_init() to initialize global resolver @@ -173,6 +175,9 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) int optmask = ARES_OPT_SOCK_STATE_CB; options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = easy; + options.timeout = CARES_TIMEOUT_PER_ATTEMPT; + optmask |= ARES_OPT_TIMEOUTMS; + status = ares_init_options((ares_channel*)resolver, &options, optmask); if(status != ARES_SUCCESS) { if(status == ARES_ENOMEM) @@ -770,9 +775,14 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, int pf = PF_INET; memset(&hints, 0, sizeof(hints)); #ifdef CURLRES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && + Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } #endif /* CURLRES_IPV6 */ hints.ai_family = pf; hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c index 6f0a212..a2e294f 100644 --- a/Utilities/cmcurl/lib/asyn-thread.c +++ b/Utilities/cmcurl/lib/asyn-thread.c @@ -696,9 +696,13 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, *waitp = 0; /* default to synchronous response */ #ifdef CURLRES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) + if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; + if(data->conn->ip_version == CURL_IPRESOLVE_V6) + pf = PF_INET6; + else + pf = PF_UNSPEC; + } #endif /* CURLRES_IPV6 */ memset(&hints, 0, sizeof(hints)); diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c index 9d495d4..30db7e5 100644 --- a/Utilities/cmcurl/lib/base64.c +++ b/Utilities/cmcurl/lib/base64.c @@ -32,13 +32,14 @@ !defined(CURL_DISABLE_POP3) || \ !defined(CURL_DISABLE_IMAP) || \ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) - -#include "urldata.h" /* for the Curl_easy definition */ +#include "curl/curl.h" #include "warnless.h" #include "curl_base64.h" /* The last 2 #include files should be in this order */ +#ifdef BUILDING_LIBCURL #include "curl_memory.h" +#endif #include "memdebug.h" /* ---- Base64 Encoding/Decoding Table --- */ diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c index c29983c..61ca29a 100644 --- a/Utilities/cmcurl/lib/c-hyper.c +++ b/Utilities/cmcurl/lib/c-hyper.c @@ -61,6 +61,11 @@ #include "curl_memory.h" #include "memdebug.h" +typedef enum { + USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */ + USERDATA_RESP_BODY +} userdata_t; + size_t Curl_hyper_recv(void *userp, hyper_context *ctx, uint8_t *buf, size_t buflen) { @@ -182,8 +187,11 @@ static int hyper_each_header(void *userdata, } } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; + result = Curl_bump_headersize(data, len, FALSE); + if(result) { + data->state.hresult = result; + return HYPER_ITER_BREAK; + } return HYPER_ITER_CONTINUE; } @@ -313,9 +321,8 @@ static CURLcode status_line(struct Curl_easy *data, if(result) return result; } - data->info.header_size += (curl_off_t)len; - data->req.headerbytecount += (curl_off_t)len; - return CURLE_OK; + result = Curl_bump_headersize(data, len, FALSE); + return result; } /* @@ -348,7 +355,6 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, struct hyptransfer *h = &data->hyp; hyper_task *task; hyper_task *foreach; - hyper_error *hypererr = NULL; const uint8_t *reasonp; size_t reason_len; CURLcode result = CURLE_OK; @@ -391,19 +397,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, break; } t = hyper_task_type(task); - switch(t) { - case HYPER_TASK_ERROR: - hypererr = hyper_task_value(task); - break; - case HYPER_TASK_RESPONSE: - resp = hyper_task_value(task); - break; - default: - break; - } - hyper_task_free(task); - if(t == HYPER_TASK_ERROR) { + hyper_error *hypererr = hyper_task_value(task); + hyper_task_free(task); if(data->state.hresult) { /* override Hyper's view, might not even be an error */ result = data->state.hresult; @@ -414,36 +410,55 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); hyper_code code = hyper_error_code(hypererr); failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf); - if(code == HYPERE_ABORTED_BY_CALLBACK) + switch(code) { + case HYPERE_ABORTED_BY_CALLBACK: result = CURLE_OK; - else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount) - result = CURLE_GOT_NOTHING; - else if(code == HYPERE_INVALID_PEER_MESSAGE) + break; + case HYPERE_UNEXPECTED_EOF: + if(!data->req.bytecount) + result = CURLE_GOT_NOTHING; + else + result = CURLE_RECV_ERROR; + break; + 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 */ - else + break; + default: result = CURLE_RECV_ERROR; + break; + } } *done = TRUE; hyper_error_free(hypererr); break; } - else if(h->endtask == task) { - /* end of transfer, forget the task handled, we might get a - * new one with the same address in the future. */ - *done = TRUE; - h->endtask = NULL; - infof(data, "hyperstream is done"); - if(!k->bodywrites) { - /* hyper doesn't always call the body write callback */ - bool stilldone; - result = Curl_http_firstwrite(data, data->conn, &stilldone); + else if(t == HYPER_TASK_EMPTY) { + void *userdata = hyper_task_userdata(task); + hyper_task_free(task); + if((userdata_t)userdata == USERDATA_RESP_BODY) { + /* end of transfer */ + *done = TRUE; + infof(data, "hyperstream is done"); + if(!k->bodywrites) { + /* hyper doesn't always call the body write callback */ + bool stilldone; + result = Curl_http_firstwrite(data, data->conn, &stilldone); + } + break; + } + else { + /* A background task for hyper; ignore */ + continue; } - break; - } - else if(t != HYPER_TASK_RESPONSE) { - continue; } - /* HYPER_TASK_RESPONSE */ + + DEBUGASSERT(HYPER_TASK_RESPONSE); + + resp = hyper_task_value(task); + hyper_task_free(task); *didwhat = KEEP_RECV; if(!resp) { @@ -523,13 +538,12 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; break; } - DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY); + 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; } - h->endtask = foreach; hyper_response_free(resp); resp = NULL; @@ -757,7 +771,7 @@ static int uploadstreamed(void *userdata, hyper_context *ctx, /* increasing the writebytecount here is a little premature but we don't know exactly when the body is sent */ data->req.writebytecount += fillcount; - Curl_pgrsSetUploadCounter(data, fillcount); + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); } return HYPER_POLL_READY; } @@ -794,15 +808,16 @@ static CURLcode bodysend(struct Curl_easy *data, hyper_body_set_data_func(body, uploadpostfields); else { result = Curl_get_upload_buffer(data); - if(result) + if(result) { + hyper_body_free(body); return result; + } /* init the "upload from here" pointer */ data->req.upload_fromhere = data->state.ulbuf; hyper_body_set_data_func(body, uploadstreamed); } if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { /* fail */ - hyper_body_free(body); result = CURLE_OUT_OF_MEMORY; } } @@ -1194,14 +1209,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = CURLE_OUT_OF_MEMORY; goto error; } + 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; } + sendtask = NULL; /* ownership passed on */ hyper_clientconn_free(client); + client = NULL; if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { /* HTTP GET/HEAD download */ @@ -1214,8 +1232,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) the full request has been sent. */ data->req.start100 = Curl_now(); - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.proxyuserpwd); return CURLE_OK; @@ -1230,6 +1248,12 @@ error: if(handshake) hyper_task_free(handshake); + if(client) + hyper_clientconn_free(client); + + if(req) + hyper_request_free(req); + return result; } diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h index 4218cda..0c7de90 100644 --- a/Utilities/cmcurl/lib/c-hyper.h +++ b/Utilities/cmcurl/lib/c-hyper.h @@ -34,7 +34,6 @@ struct hyptransfer { hyper_waker *write_waker; hyper_waker *read_waker; const hyper_executor *exec; - hyper_task *endtask; hyper_waker *exp100_waker; hyper_waker *send_body_waker; }; diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index c9b157c..e9bc13d 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c @@ -41,7 +41,7 @@ #include "cfilters.h" #include "cf-h1-proxy.h" #include "connect.h" -#include "curl_log.h" +#include "curl_trc.h" #include "curlx.h" #include "vtls/vtls.h" #include "transfer.h" @@ -175,36 +175,36 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, /* entering this one */ switch(new_state) { case H1_TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); + CURL_TRC_CF(data, cf, "new tunnel state 'init'"); tunnel_reinit(ts, cf->conn, data); break; case H1_TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); + CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); ts->tunnel_state = H1_TUNNEL_CONNECT; ts->keepon = KEEPON_CONNECT; Curl_dyn_reset(&ts->rcvbuf); break; case H1_TUNNEL_RECEIVE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'")); + CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); ts->tunnel_state = H1_TUNNEL_RECEIVE; break; case H1_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); + CURL_TRC_CF(data, cf, "new tunnel state 'response'"); ts->tunnel_state = H1_TUNNEL_RESPONSE; break; case H1_TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); + CURL_TRC_CF(data, cf, "new tunnel state 'established'"); infof(data, "CONNECT phase completed"); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; /* FALLTHROUGH */ case H1_TUNNEL_FAILED: if(new_state == H1_TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); + CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); ts->tunnel_state = new_state; Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->req); @@ -416,7 +416,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, if(!auth) return CURLE_OUT_OF_MEMORY; - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header)); + CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); result = Curl_http_input_auth(data, proxy, auth); free(auth); @@ -587,7 +587,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, return result; } - data->info.header_size += (long)perline; + result = Curl_bump_headersize(data, perline, TRUE); + if(result) + return result; /* Newlines are CRLF, so the CR is ignored as the line isn't really terminated until the LF comes. Treat a following CR @@ -636,7 +638,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* without content-length or chunked encoding, we can't keep the connection alive since the close is the end signal so we bail out at once instead */ - DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked")); + CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); ts->keepon = KEEPON_DONE; } } @@ -713,14 +715,13 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, } options = hyper_clientconn_options_new(); - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - if(!options) { failf(data, "Couldn't create hyper client options"); result = CURLE_OUT_OF_MEMORY; goto error; } + hyper_clientconn_options_set_preserve_header_case(options, 1); + hyper_clientconn_options_set_preserve_header_order(options, 1); hyper_clientconn_options_exec(options, h->exec); @@ -751,6 +752,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, client = hyper_task_value(task); hyper_task_free(task); + req = hyper_request_new(); if(!req) { failf(data, "Couldn't hyper_request_new"); @@ -859,12 +861,17 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, result = CURLE_OUT_OF_MEMORY; goto error; } + 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; } + sendtask = NULL; /* ownership passed on */ + + hyper_clientconn_free(client); + client = NULL; error: free(host); @@ -877,6 +884,9 @@ error: hyper_task_free(handshake); if(client) hyper_clientconn_free(client); + if(req) + hyper_request_free(req); + return result; } @@ -975,7 +985,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, switch(ts->tunnel_state) { case H1_TUNNEL_INIT: /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start")); + CURL_TRC_CF(data, cf, "CONNECT start"); result = start_CONNECT(cf, data, ts); if(result) goto out; @@ -984,7 +994,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, case H1_TUNNEL_CONNECT: /* see that the request is completely sent */ - DEBUGF(LOG_CF(data, cf, "CONNECT send")); + CURL_TRC_CF(data, cf, "CONNECT send"); result = send_CONNECT(data, cf->conn, ts, &done); if(result || !done) goto out; @@ -993,7 +1003,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, case H1_TUNNEL_RECEIVE: /* read what is there */ - DEBUGF(LOG_CF(data, cf, "CONNECT receive")); + CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; @@ -1007,7 +1017,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, /* FALLTHROUGH */ case H1_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "CONNECT response")); + CURL_TRC_CF(data, cf, "CONNECT response"); if(data->req.newurl) { /* not the "final" response, we need to do a follow up request. * If the other side indicated a connection close, or if someone @@ -1019,7 +1029,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, * reset our tunnel state. To avoid recursion, we return * and expect to be called again. */ - DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open")); + CURL_TRC_CF(data, cf, "CONNECT need to close+open"); infof(data, "Connect me again please"); Curl_conn_cf_close(cf, data); connkeep(conn, "HTTP proxy CONNECT"); @@ -1043,7 +1053,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, if(data->info.httpproxycode/100 != 2) { /* a non-2xx response and we have no next url to try. */ Curl_safefree(data->req.newurl); - /* failure, close this connection to avoid re-use */ + /* failure, close this connection to avoid reuse */ streamclose(conn, "proxy CONNECT failure"); h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); @@ -1073,7 +1083,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, return CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "connect")); + CURL_TRC_CF(data, cf, "connect"); result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) return result; @@ -1133,14 +1143,14 @@ static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf, static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); tunnel_free(cf, data); } static void cf_h1_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; if(cf->ctx) { h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index f6acfc5..a3feefc 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c @@ -30,11 +30,12 @@ #include "urldata.h" #include "cfilters.h" #include "connect.h" -#include "curl_log.h" +#include "curl_trc.h" #include "bufq.h" #include "dynbuf.h" #include "dynhds.h" #include "http1.h" +#include "http2.h" #include "http_proxy.h" #include "multiif.h" #include "cf-h2-proxy.h" @@ -44,16 +45,16 @@ #include "curl_memory.h" #include "memdebug.h" -#define H2_CHUNK_SIZE (16*1024) +#define PROXY_H2_CHUNK_SIZE (16*1024) #define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) #define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) -#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE) +#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) #define PROXY_H2_NW_SEND_CHUNKS 1 -#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE) -#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / H2_CHUNK_SIZE) +#define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE) typedef enum { @@ -87,9 +88,9 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; - Curl_bufq_init2(&ts->recvbuf, H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, + Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - Curl_bufq_init(&ts->sendbuf, H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); + Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); if(cf->conn->bits.conn_to_host) hostname = cf->conn->conn_to_host.name; @@ -146,29 +147,30 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, /* entering this one */ switch(new_state) { case H2_TUNNEL_INIT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'")); + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); tunnel_stream_clear(ts); break; case H2_TUNNEL_CONNECT: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'")); + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id); ts->state = H2_TUNNEL_CONNECT; break; case H2_TUNNEL_RESPONSE: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'")); + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id); ts->state = H2_TUNNEL_RESPONSE; break; case H2_TUNNEL_ESTABLISHED: - DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'")); + CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", + ts->stream_id); infof(data, "CONNECT phase completed"); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; /* FALLTHROUGH */ case H2_TUNNEL_FAILED: if(new_state == H2_TUNNEL_FAILED) - DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'")); + 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 @@ -231,9 +233,9 @@ static void drain_tunnel(struct Curl_cfilter *cf, bits = CURL_CSELECT_IN; if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len) bits |= CURL_CSELECT_OUT; - if(data->state.dselect_bits != bits) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x", - tunnel->stream_id, bits)); + if(data->state.dselect_bits != bits || 1) { + CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x", + tunnel->stream_id, bits); data->state.dselect_bits = bits; Curl_expire(data, 0, EXPIRE_RUN_NOW); } @@ -244,12 +246,17 @@ static ssize_t proxy_nw_in_reader(void *reader_ctx, CURLcode *err) { struct Curl_cfilter *cf = reader_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; - nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_in_reader(len=%zu) -> %zd, %d", - buflen, nread, *err)); + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); + CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d", + buflen, nread, *err); + } + else { + nread = 0; + } return nread; } @@ -258,12 +265,18 @@ static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, CURLcode *err) { struct Curl_cfilter *cf = writer_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); - DEBUGF(LOG_CF(data, cf, "nw_out_writer(len=%zu) -> %zd, %d", - buflen, nwritten, *err)); + if(cf) { + struct Curl_easy *data = CF_DATA_CURRENT(cf); + nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, + err); + CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", + buflen, nwritten, *err); + } + else { + nwritten = 0; + } return nwritten; } @@ -295,6 +308,10 @@ static ssize_t on_session_send(nghttp2_session *h2, static int proxy_h2_on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +#endif static int proxy_h2_on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *userp); @@ -322,8 +339,8 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->h2); memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); - Curl_bufq_init(&ctx->inbufq, H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); - Curl_bufq_init(&ctx->outbufq, H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); + Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); + Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); if(tunnel_stream_init(cf, &ctx->tunnel)) goto out; @@ -337,6 +354,9 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); nghttp2_session_callbacks_set_on_frame_recv_callback( cbs, proxy_h2_on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif nghttp2_session_callbacks_set_on_data_chunk_recv_callback( cbs, tunnel_recv_callback); nghttp2_session_callbacks_set_on_stream_close_callback( @@ -384,11 +404,11 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, out: if(cbs) nghttp2_session_callbacks_del(cbs); - DEBUGF(LOG_CF(data, cf, "init proxy ctx -> %d", result)); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); return result; } -static int should_close_session(struct cf_h2_proxy_ctx *ctx) +static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) { return !nghttp2_session_want_read(ctx->h2) && !nghttp2_session_want_write(ctx->h2); @@ -409,13 +429,13 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq))); + CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); ctx->nw_out_blocked = 1; } return result; } - DEBUGF(LOG_CF(data, cf, "nw send buffer flushed")); + CURL_TRC_CF(data, cf, "[0] nw send buffer flushed"); return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; } @@ -436,8 +456,7 @@ static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); - DEBUGF(LOG_CF(data, cf, - "fed %zu bytes from nw to nghttp2 -> %zd", blen, rv)); + CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); if(rv < 0) { failf(data, "process_pending_input: nghttp2_session_mem_recv() returned " @@ -447,12 +466,12 @@ static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, } Curl_bufq_skip(&ctx->inbufq, (size_t)rv); if(Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed")); + CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); break; } else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); } } @@ -468,8 +487,8 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, /* Process network input buffer fist */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); if(proxy_h2_process_pending_input(cf, data, &result) < 0) return result; } @@ -482,8 +501,8 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); - DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); + CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d", + Curl_bufq_len(&ctx->inbufq), nread, result); if(nread < 0) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data"); @@ -518,8 +537,8 @@ static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, rv = nghttp2_session_send(ctx->h2); if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); + CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); return CURLE_SEND_ERROR; } return proxy_h2_nw_out_flush(cf, data); @@ -555,6 +574,97 @@ static ssize_t on_session_send(nghttp2_session *h2, return nwritten; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + static int proxy_h2_on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) @@ -566,75 +676,74 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, (void)session; DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ DEBUGASSERT(data); switch(frame->hd.type) { case NGHTTP2_SETTINGS: - /* we do not do anything with this for now */ + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if((data->req.keepon & KEEP_SEND_HOLD) && + (data->req.keepon & KEEP_SEND)) { + data->req.keepon &= ~KEEP_SEND_HOLD; + drain_tunnel(cf, data, &ctx->tunnel); + CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", + stream_id); + } break; case NGHTTP2_GOAWAY: - infof(data, "recveived GOAWAY, error=%d, last_stream=%u", - frame->goaway.error_code, frame->goaway.last_stream_id); ctx->goaway = TRUE; break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE")); - break; default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); + break; } return 0; } if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] rcvd FRAME not for tunnel", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; } switch(frame->hd.type) { - case NGHTTP2_DATA: - /* If body started on this stream, then receiving DATA is illegal. */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame DATA", stream_id)); - break; case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame HEADERS", stream_id)); - /* nghttp2 guarantees that :status is received, and we store it to stream->status_code. Fuzzing has proven this can still be reached without status code having been set. */ if(!ctx->tunnel.resp) return NGHTTP2_ERR_CALLBACK_FAILURE; /* Only final status code signals the end of header */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] got http status: %d", - stream_id, ctx->tunnel.resp->status)); + CURL_TRC_CF(data, cf, "[%d] got http status: %d", + stream_id, ctx->tunnel.resp->status); if(!ctx->tunnel.has_final_response) { if(ctx->tunnel.resp->status / 100 != 1) { ctx->tunnel.has_final_response = TRUE; } } break; - case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv RST", stream_id)); - ctx->tunnel.reset = TRUE; - break; case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id)); if((data->req.keepon & KEEP_SEND_HOLD) && (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; Curl_expire(data, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] unpausing after win update", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] unpausing after win update", + stream_id); } break; default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame %x", - stream_id, frame->hd.type)); break; } return 0; @@ -658,10 +767,9 @@ static int proxy_h2_on_header(nghttp2_session *session, (void)session; DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ if(stream_id != ctx->tunnel.stream_id) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header for non-tunnel stream: " - "%.*s: %.*s", stream_id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: " + "%.*s: %.*s", stream_id, + (int)namelen, name, (int)valuelen, value); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -689,8 +797,8 @@ static int proxy_h2_on_header(nghttp2_session *session, return NGHTTP2_ERR_CALLBACK_FAILURE; resp->prev = ctx->tunnel.resp; ctx->tunnel.resp = resp; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] status: HTTP/2 %03d", - stream_id, ctx->tunnel.resp->status)); + CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", + stream_id, ctx->tunnel.resp->status); return 0; } @@ -703,10 +811,8 @@ static int proxy_h2_on_header(nghttp2_session *session, if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header: %.*s: %.*s", - stream_id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", + stream_id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ } @@ -746,8 +852,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) *data_flags = NGHTTP2_DATA_FLAG_EOF; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] tunnel_send_callback -> %zd", - ts->stream_id, nread)); + CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", + ts->stream_id, nread); return nread; } @@ -791,8 +897,8 @@ static int proxy_h2_on_stream_close(nghttp2_session *session, if(stream_id != ctx->tunnel.stream_id) return 0; - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] proxy_h2_on_stream_close, %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); + CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); ctx->tunnel.closed = TRUE; ctx->tunnel.error = error_code; @@ -910,8 +1016,8 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, NULL, ts, tunnel_send_callback, cf); if(result) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(ts->stream_id), ts->stream_id)); + CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s", + ts->stream_id, nghttp2_strerror(ts->stream_id)); } out: @@ -945,8 +1051,8 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, } if(auth_reply) { - DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", - auth_reply->value)); + CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", + auth_reply->value); result = Curl_http_input_auth(data, ts->resp->status == 407, auth_reply->value); if(result) @@ -976,7 +1082,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, switch(ts->state) { case H2_TUNNEL_INIT: /* Prepare the CONNECT request and make a first attempt to send. */ - DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority)); + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); result = submit_CONNECT(cf, data, ts); if(result) goto out; @@ -1143,8 +1249,8 @@ static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, ssize_t rv = 0; if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new " - "connection", ctx->tunnel.stream_id)); + 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 */ *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ return -1; @@ -1164,7 +1270,8 @@ static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, *err = CURLE_OK; rv = 0; - DEBUGF(LOG_CF(data, cf, "handle_tunnel_close -> %zd, %d", rv, *err)); + CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d", + ctx->tunnel.stream_id, rv, *err); return rv; } @@ -1200,8 +1307,8 @@ static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "tunnel_recv(len=%zu) -> %zd, %d", - len, nread, *err)); + CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d", + ctx->tunnel.stream_id, len, nread, *err); return nread; } @@ -1229,13 +1336,22 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, nread = tunnel_recv(cf, data, buf, len, err); if(nread > 0) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] increase window by %zd", - ctx->tunnel.stream_id, nread)); + CURL_TRC_CF(data, cf, "[%d] increase window by %zd", + ctx->tunnel.stream_id, nread); nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); } result = proxy_h2_progress_egress(cf, data); - if(result && result != CURLE_AGAIN) { + 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) { *err = result; nread = -1; } @@ -1247,8 +1363,8 @@ out: * draining to avoid stalling when no socket events happen. */ drain_tunnel(cf, data, &ctx->tunnel); } - DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d", - ctx->tunnel.stream_id, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d", + ctx->tunnel.stream_id, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1276,12 +1392,12 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, goto out; } else if(ctx->tunnel.upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the + /* 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 - * happend. We are not prepared to handle that. */ + * happen. We are not prepared to handle that. */ failf(data, "HTTP/2 proxy, send again with decreased length"); *err = CURLE_HTTP2; nwritten = -1; @@ -1289,6 +1405,7 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, } 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); @@ -1309,6 +1426,13 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, } } + result = proxy_h2_progress_ingress(cf, data); + if(result) { + *err = result; + nwritten = -1; + goto out; + } + /* 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); @@ -1339,24 +1463,25 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, * proxy connection AND to UNHOLD all of them again when the * window increases. * We *could* iterate over all data on this conn maybe? */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] remote flow " - "window is exhausted", ctx->tunnel.stream_id)); + 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; - DEBUGF(LOG_CF(data, cf, "[h2sid=%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)); + 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(should_close_session(ctx)) { + else 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) { @@ -1364,22 +1489,27 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, nwritten = -1; } else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); + CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); *err = CURLE_HTTP2; nwritten = -1; } } out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, " - "h2 windows %d-%d (stream-conn), " - "buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, len, nwritten, *err, - nghttp2_session_get_stream_remote_window_size( + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + (nwritten >= 0 || *err == CURLE_AGAIN)) { + /* data pending and no fatal error to report. Need to trigger + * draining to avoid stalling when no socket events happen. */ + drain_tunnel(cf, data, &ctx->tunnel); + } + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", + ctx->tunnel.stream_id, len, nwritten, *err, + 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))); + 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 nwritten; } @@ -1409,7 +1539,7 @@ static bool proxy_h2_connisalive(struct Curl_cfilter *cf, /* immediate error, considered dead */ alive = FALSE; else { - alive = !should_close_session(ctx); + alive = !proxy_h2_should_close_session(ctx); } } else if(result != CURLE_AGAIN) { @@ -1431,8 +1561,8 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); - DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending)); + CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", + result, *input_pending); CF_DATA_RESTORE(cf, save); return result; } @@ -1440,7 +1570,7 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, cf_h2_proxy_close, diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index ec0100c..39ac415 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c @@ -30,7 +30,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" -#include "curl_log.h" +#include "curl_trc.h" #include "multiif.h" /* The last 3 #include files should be in this order */ @@ -86,12 +86,12 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, if(data->set.str[STRING_HAPROXY_CLIENT_IP]) client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; else - client_ip = data->info.conn_primary_ip; + client_ip = data->info.conn_local_ip; result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", tcp_version, - data->info.conn_local_ip, client_ip, + data->info.conn_primary_ip, data->info.conn_local_port, data->info.conn_primary_port); @@ -157,14 +157,14 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); cf_haproxy_ctx_free(cf->ctx); } static void cf_haproxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; cf_haproxy_ctx_reset(cf->ctx); if(cf->next) diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index 4e4d4b1..be54aec 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c @@ -28,7 +28,7 @@ #include "urldata.h" #include <curl/curl.h> -#include "curl_log.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" #include "multiif.h" @@ -165,9 +165,9 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, if(winner != &ctx->h21_baller) cf_hc_baller_reset(&ctx->h21_baller, data); - DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, (int)Curl_timediff(Curl_now(), winner->started), - cf_hc_baller_reply_ms(winner, data))); + CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started), + cf_hc_baller_reply_ms(winner, data)); cf->next = winner->cf; winner->cf = NULL; @@ -218,16 +218,16 @@ static bool time_to_start_h21(struct Curl_cfilter *cf, elapsed_ms = Curl_timediff(now, ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21", - ctx->hard_eyeballs_timeout_ms)); + CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21", + ctx->hard_eyeballs_timeout_ms); return TRUE; } if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) { - DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not " - "seen any data, starting h21", - ctx->soft_eyeballs_timeout_ms)); + CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not " + "seen any data, starting h21", + ctx->soft_eyeballs_timeout_ms); return TRUE; } /* set the effective hard timeout again */ @@ -258,7 +258,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->h3_baller.cf); DEBUGASSERT(!ctx->h21_baller.cf); DEBUGASSERT(!cf->next); - DEBUGF(LOG_CF(data, cf, "connect, init")); + CURL_TRC_CF(data, cf, "connect, init"); ctx->started = now; if(ctx->h3_baller.enabled) { cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC); @@ -286,7 +286,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(cf_hc_baller_is_active(&ctx->h21_baller)) { - DEBUGF(LOG_CF(data, cf, "connect, check h21")); + CURL_TRC_CF(data, cf, "connect, check h21"); result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->h21_baller); @@ -297,7 +297,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, if((!ctx->h3_baller.enabled || ctx->h3_baller.result) && (!ctx->h21_baller.enabled || ctx->h21_baller.result)) { /* both failed or disabled. we give up */ - DEBUGF(LOG_CF(data, cf, "connect, all failed")); + CURL_TRC_CF(data, cf, "connect, all failed"); result = ctx->result = ctx->h3_baller.enabled? ctx->h3_baller.result : ctx->h21_baller.result; ctx->state = CF_HC_FAILURE; @@ -321,7 +321,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); return result; } @@ -345,7 +345,7 @@ static int cf_hc_get_select_socks(struct Curl_cfilter *cf, if(!cf_hc_baller_is_active(b)) continue; brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks); - DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc)); + CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc); if(!brc) continue; for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) { @@ -359,7 +359,7 @@ static int cf_hc_get_select_socks(struct Curl_cfilter *cf, } } } - DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc)); + CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc); return rc; } @@ -371,7 +371,7 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf, if(cf->connected) return cf->next->cft->has_data_pending(cf->next, data); - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); return cf_hc_baller_data_pending(&ctx->h3_baller, data) || cf_hc_baller_data_pending(&ctx->h21_baller, data); } @@ -427,7 +427,7 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf, static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf_hc_reset(cf, data); cf->connected = FALSE; @@ -442,7 +442,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_hc_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); cf_hc_reset(cf, data); Curl_safefree(ctx); } @@ -450,7 +450,7 @@ static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, cf_hc_close, diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index 5729fe0..effe6e6 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c @@ -71,6 +71,7 @@ #include "warnless.h" #include "conncache.h" #include "multihandle.h" +#include "rand.h" #include "share.h" #include "version_win32.h" @@ -390,6 +391,7 @@ void Curl_sndbufset(curl_socket_t sockfd) } #endif +#ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sockfd, int af, unsigned int scope) { @@ -444,29 +446,24 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* interface */ if(!is_host) { #ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. + /* + * 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. * - * 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. + * 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 typically "errno 1, error: Operation not permitted" if - * you're not running as root or another suitable privileged - * user. - * If it succeeds it means the parameter was a valid interface and - * not an IP address. Return immediately. + /* 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); return CURLE_OK; } #endif @@ -632,8 +629,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, port++; /* try next port */ if(port == 0) break; - infof(data, "Bind to local port %hu failed, trying next", port - 1); - /* We re-use/clobber the port variable here below */ + infof(data, "Bind to local port %d failed, trying next", port - 1); + /* We reuse/clobber the port variable here below */ if(sock->sa_family == AF_INET) si4->sin_port = ntohs(port); #ifdef ENABLE_IPV6 @@ -653,6 +650,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, return CURLE_INTERFACE_FAILED; } +#endif /* * verifyconnect() returns TRUE if the connect really has happened. @@ -727,8 +725,6 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) static CURLcode socket_connect_result(struct Curl_easy *data, const char *ipaddress, int error) { - char buffer[STRERROR_LEN]; - switch(error) { case EINPROGRESS: case EWOULDBLOCK: @@ -745,8 +741,15 @@ static CURLcode socket_connect_result(struct Curl_easy *data, default: /* unknown error, fallthrough and try another address! */ - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)ipaddress; +#else + { + char buffer[STRERROR_LEN]; + infof(data, "Immediate connect fail for %s: %s", + ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + } +#endif data->state.os_errno = error; /* connect failed */ return CURLE_COULDNT_CONNECT; @@ -775,6 +778,10 @@ struct cf_socket_ctx { struct curltime connected_at; /* when socket connected/got first byte */ struct curltime first_byte_at; /* when first byte was recvd */ int error; /* errno of last failure or 0 */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ + int wpartial_percent; /* percent of bytes written in send */ +#endif BIT(got_first_byte); /* if first byte was received */ BIT(accepted); /* socket was accepted, not connected */ BIT(active); @@ -790,6 +797,22 @@ static void cf_socket_ctx_init(struct cf_socket_ctx *ctx, 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"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wblock_percent = (int)l; + } + p = getenv("CURL_DBG_SOCK_WPARTIAL"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + ctx->wpartial_percent = (int)l; + } + } +#endif } struct reader_ctx { @@ -836,8 +859,8 @@ static ssize_t nw_in_read(void *reader_ctx, nread = -1; } } - DEBUGF(LOG_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", - len, (int)nread, *err)); + CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", + len, (int)nread, *err); return nread; } @@ -852,14 +875,14 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) * closed it) and we just forget about it. */ if(ctx->sock == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", active)", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ", active)", ctx->sock); socket_close(data, cf->conn, !ctx->accepted, ctx->sock); cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; } else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ") no longer at conn->sock[], discarding", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ") no longer at conn->sock[], discarding", ctx->sock); /* TODO: we do not want this to happen. Need to check which * code is messing with conn->sock[cf->sockindex] */ } @@ -869,8 +892,8 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) } else { /* this is our local socket, we did never publish it */ - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T - ", not active)", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T + ", not active)", ctx->sock); socket_close(data, cf->conn, !ctx->accepted, ctx->sock); ctx->sock = CURL_SOCKET_BAD; } @@ -889,7 +912,7 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_socket_ctx *ctx = cf->ctx; cf_socket_close(cf, data); - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); Curl_bufq_free(&ctx->recvbuf); free(ctx); cf->ctx = NULL; @@ -957,7 +980,6 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, bool isconnected = FALSE; CURLcode result = CURLE_COULDNT_CONNECT; bool is_tcp; - const char *ipmsg; (void)data; DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); @@ -970,15 +992,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(result) goto out; +#ifndef CURL_DISABLE_VERBOSE_STRINGS + { + const char *ipmsg; #ifdef ENABLE_IPV6 - if(ctx->addr.family == AF_INET6) { - set_ipv6_v6only(ctx->sock, 0); - ipmsg = " Trying [%s]:%d..."; + if(ctx->addr.family == AF_INET6) { + set_ipv6_v6only(ctx->sock, 0); + ipmsg = " Trying [%s]:%d..."; + } + else +#endif + ipmsg = " Trying %s:%d..."; + infof(data, ipmsg, ctx->r_ip, ctx->r_port); } - else #endif - ipmsg = " Trying %s:%d..."; - infof(data, ipmsg, ctx->r_ip, ctx->r_port); #ifdef ENABLE_IPV6 is_tcp = (ctx->addr.family == AF_INET @@ -1014,6 +1041,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, } } +#ifndef CURL_DISABLE_BINDLOCAL /* possibly bind the local end to an IP, interface or port */ if(ctx->addr.family == AF_INET #ifdef ENABLE_IPV6 @@ -1031,6 +1059,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, goto out; } } +#endif /* set socket non-blocking */ (void)curlx_nonblock(ctx->sock, TRUE); @@ -1047,8 +1076,8 @@ out: ctx->connected_at = Curl_now(); cf->connected = TRUE; } - DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, - result, ctx->sock)); + CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, + result, ctx->sock); return result; } @@ -1155,7 +1184,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, rc = SOCKET_WRITABLE(ctx->sock, 0); if(rc == 0) { /* no connection yet */ - DEBUGF(LOG_CF(data, cf, "not connected yet")); + CURL_TRC_CF(data, cf, "not connected yet"); return CURLE_OK; } else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { @@ -1165,7 +1194,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, set_local_ip(cf, data); *done = TRUE; cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "connected")); + CURL_TRC_CF(data, cf, "connected"); return CURLE_OK; } } @@ -1245,11 +1274,34 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t nwritten; + size_t orig_len = len; *err = CURLE_OK; fdsave = cf->conn->sock[cf->sockindex]; cf->conn->sock[cf->sockindex] = ctx->sock; +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(ctx->wblock_percent > 0) { + unsigned char c; + Curl_rand(data, &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; + nwritten = -1; + cf->conn->sock[cf->sockindex] = fdsave; + return nwritten; + } + } + if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) { + len = len * ctx->wpartial_percent / 100; + if(!len) + len = 1; + CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes", + orig_len, len); + } +#endif + #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(cf->conn->bits.tcp_fastopen) { nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, @@ -1288,8 +1340,8 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, } } - DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d", - len, (int)nwritten, *err)); + CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", + orig_len, (int)nwritten, *err); cf->conn->sock[cf->sockindex] = fdsave; return nwritten; } @@ -1307,7 +1359,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, cf->conn->sock[cf->sockindex] = ctx->sock; if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) { - DEBUGF(LOG_CF(data, cf, "recv from buffer")); + CURL_TRC_CF(data, cf, "recv from buffer"); nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); } else { @@ -1324,7 +1376,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 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. */ - DEBUGF(LOG_CF(data, cf, "partial read: empty buffer first")); + 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) { @@ -1337,7 +1389,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, nread = 0; } else { - DEBUGF(LOG_CF(data, cf, "buffered %zd additional bytes", nwritten)); + CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten); nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); } } @@ -1347,8 +1399,8 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, - *err)); + 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; @@ -1455,19 +1507,19 @@ static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, r = Curl_poll(pfd, 1, 0); if(r < 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead")); + CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead"); return FALSE; } else if(r == 0) { - DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive")); + CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive"); return TRUE; } else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { - DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead")); + CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead"); return FALSE; } - DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive")); + CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive"); *input_pending = TRUE; return TRUE; } @@ -1520,7 +1572,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_tcp = { "TCP", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, cf_socket_close, @@ -1581,10 +1633,10 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, return socket_connect_result(data, ctx->r_ip, SOCKERRNO); } set_local_ip(cf, data); - DEBUGF(LOG_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T - " connected: [%s:%d] -> [%s:%d]", - (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", - ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port)); + CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T + " connected: [%s:%d] -> [%s:%d]", + (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", + ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port); (void)curlx_nonblock(ctx->sock, TRUE); switch(ctx->addr.family) { @@ -1624,7 +1676,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); if(result) { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); goto out; } @@ -1632,13 +1684,13 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, result = cf_udp_setup_quic(cf, data); if(result) goto out; - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (%s:%d)", - ctx->sock, ctx->l_ip, ctx->l_port)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (%s:%d)", + ctx->sock, ctx->l_ip, ctx->l_port); } else { - DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%" - CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock)); + CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" + CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock); } *done = TRUE; cf->connected = TRUE; @@ -1650,7 +1702,7 @@ out: struct Curl_cftype Curl_cft_udp = { "UDP", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_udp_connect, cf_socket_close, @@ -1701,7 +1753,7 @@ out: struct Curl_cftype Curl_cft_unix = { "UNIX", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, cf_socket_close, @@ -1765,7 +1817,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_tcp_accept = { "TCP-ACCEPT", CF_TYPE_IP_CONNECT, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_accept_connect, cf_socket_close, @@ -1810,8 +1862,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, ctx->active = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%" - CURL_FORMAT_SOCKET_T ")", ctx->sock)); + CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" + CURL_FORMAT_SOCKET_T ")", ctx->sock); out: if(result) { @@ -1875,9 +1927,9 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, ctx->accepted = TRUE; ctx->connected_at = Curl_now(); cf->connected = TRUE; - DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T - ", remote=%s port=%d)", - ctx->sock, ctx->r_ip, ctx->r_port)); + CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T + ", remote=%s port=%d)", + ctx->sock, ctx->r_ip, ctx->r_port); return CURLE_OK; } @@ -1922,4 +1974,3 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, } return CURLE_FAILED_INIT; } - diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index 216d0b4..f74eb40 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c @@ -238,7 +238,7 @@ void Curl_conn_cf_add(struct Curl_easy *data, cf->conn = conn; cf->sockindex = index; conn->cfilter[index] = cf; - DEBUGF(LOG_CF(data, cf, "added")); + CURL_TRC_CF(data, cf, "added"); } void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, @@ -646,4 +646,3 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, &n, NULL) : CURLE_UNKNOWN_OPTION; return (result || n <= 0)? 1 : (size_t)n; } - diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index dc93533..033fb7b 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c @@ -381,6 +381,11 @@ struct cf_he_ctx { struct curltime started; }; +/* when there are more than one IP address left to use, this macro returns how + much of the given timeout to spend on *this* attempt */ +#define TIMEOUT_LARGE 600 +#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms) + static CURLcode eyeballer_new(struct eyeballer **pballer, cf_ip_connect_create *cf_create, const struct Curl_addrinfo *addr, @@ -408,7 +413,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer, baller->primary = primary; baller->delay_ms = delay_ms; baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? - timeout_ms / 2 : timeout_ms; + USETIME(timeout_ms) : timeout_ms; baller->timeout_id = timeout_id; baller->result = CURLE_COULDNT_CONNECT; @@ -475,7 +480,7 @@ static void baller_initiate(struct Curl_cfilter *cf, out: if(result) { - DEBUGF(LOG_CF(data, cf, "%s failed", baller->name)); + CURL_TRC_CF(data, cf, "%s failed", baller->name); baller_close(baller, data); } if(cf_prev) @@ -501,7 +506,7 @@ static CURLcode baller_start(struct Curl_cfilter *cf, while(baller->addr) { baller->started = Curl_now(); baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? - timeoutms / 2 : timeoutms; + USETIME(timeoutms) : timeoutms; baller_initiate(cf, data, baller); if(!baller->result) break; @@ -601,8 +606,8 @@ evaluate: continue; } baller->result = baller_connect(cf, data, baller, &now, connected); - DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d", - baller->name, baller->result, *connected)); + CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d", + baller->name, baller->result, *connected); if(!baller->result) { if(*connected) { @@ -623,11 +628,11 @@ evaluate: } baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + CURL_TRC_CF(data, cf, "%s done", baller->name); } else { /* next attempt was started */ - DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name)); + CURL_TRC_CF(data, cf, "%s trying next", baller->name); ++ongoing; } } @@ -661,12 +666,12 @@ evaluate: Curl_timediff(now, ctx->started) >= baller->delay_ms) { baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); if(baller->is_done) { - DEBUGF(LOG_CF(data, cf, "%s done", baller->name)); + CURL_TRC_CF(data, cf, "%s done", baller->name); } else { - DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%" - CURL_FORMAT_TIMEDIFF_T "ms)", - baller->name, baller->timeoutms)); + CURL_TRC_CF(data, cf, "%s starting (timeout=%" + CURL_FORMAT_TIMEDIFF_T "ms)", + baller->name, baller->timeoutms); ++ongoing; ++added; } @@ -683,14 +688,14 @@ evaluate: } /* all ballers have failed to connect. */ - DEBUGF(LOG_CF(data, cf, "all eyeballers failed")); + CURL_TRC_CF(data, cf, "all eyeballers failed"); result = CURLE_COULDNT_CONNECT; for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) { struct eyeballer *baller = ctx->baller[i]; - DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d", - baller?baller->name:NULL, - baller?baller->has_started:0, - baller?baller->result:0)); + CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", + baller?baller->name:NULL, + baller?baller->has_started:0, + baller?baller->result:0); if(baller && baller->has_started && baller->result) { result = baller->result; break; @@ -803,9 +808,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME); if(result) return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[0]->name, ctx->baller[0]->timeoutms)); + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[0]->name, ctx->baller[0]->timeoutms); if(addr1) { /* second one gets a delayed start */ result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, @@ -815,9 +820,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf, timeout_ms, EXPIRE_DNS_PER_NAME2); if(result) return result; - DEBUGF(LOG_CF(data, cf, "created %s (timeout %" - CURL_FORMAT_TIMEDIFF_T "ms)", - ctx->baller[1]->name, ctx->baller[1]->timeoutms)); + CURL_TRC_CF(data, cf, "created %s (timeout %" + CURL_FORMAT_TIMEDIFF_T "ms)", + ctx->baller[1]->name, ctx->baller[1]->timeoutms); } Curl_expire(data, data->set.happy_eyeballs_timeout, @@ -931,7 +936,7 @@ static void cf_he_close(struct Curl_cfilter *cf, { struct cf_he_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf_he_ctx_clear(cf, data); cf->connected = FALSE; ctx->state = SCFST_INIT; @@ -1007,7 +1012,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf, } } *pres1 = reply_ms; - DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1)); + CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1); return CURLE_OK; } case CF_QUERY_TIMER_CONNECT: { @@ -1034,7 +1039,7 @@ static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_he_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_he_ctx_clear(cf, data); } @@ -1045,7 +1050,7 @@ static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_happy_eyeballs = { "HAPPY-EYEBALLS", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_he_destroy, cf_he_connect, cf_he_close, @@ -1148,7 +1153,7 @@ static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, DEBUGASSERT(cf_at); cf_create = get_cf_create(transport); if(!cf_create) { - DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport)); + CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport); return CURLE_UNSUPPORTED_PROTOCOL; } result = cf_happy_eyeballs_create(&cf, data, cf_at->conn, @@ -1286,7 +1291,7 @@ static void cf_setup_close(struct Curl_cfilter *cf, { struct cf_setup_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; ctx->state = CF_SETUP_INIT; @@ -1301,7 +1306,7 @@ static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_setup_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); Curl_safefree(ctx); } @@ -1309,7 +1314,7 @@ static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cftype Curl_cft_setup = { "SETUP", 0, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_setup_destroy, cf_setup_connect, cf_setup_close, @@ -1441,4 +1446,3 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, out: return result; } - diff --git a/Utilities/cmcurl/lib/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h index 806d443..7f7cd1d 100644 --- a/Utilities/cmcurl/lib/curl_base64.h +++ b/Utilities/cmcurl/lib/curl_base64.h @@ -24,11 +24,18 @@ * ***************************************************************************/ +#ifndef BUILDING_LIBCURL +/* this renames functions so that the tool code can use the same code + without getting symbol collisions */ +#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d) +#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d) +#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c) +#endif + CURLcode Curl_base64_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, char **outptr, size_t *outlen); CURLcode Curl_base64_decode(const char *src, unsigned char **outptr, size_t *outlen); - #endif /* HEADER_CURL_BASE64_H */ diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake index 4ab8242..c225eef 100644 --- a/Utilities/cmcurl/lib/curl_config.h.cmake +++ b/Utilities/cmcurl/lib/curl_config.h.cmake @@ -25,14 +25,32 @@ #include <cm3p/kwiml/abi.h> +/* Default SSL backend */ +#cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}" + /* disables alt-svc */ #cmakedefine CURL_DISABLE_ALTSVC 1 /* disables cookies support */ #cmakedefine CURL_DISABLE_COOKIES 1 -/* disables cryptographic authentication */ -#cmakedefine CURL_DISABLE_CRYPTO_AUTH 1 +/* disables Basic authentication */ +#cmakedefine CURL_DISABLE_BASIC_AUTH 1 + +/* disables Bearer authentication */ +#cmakedefine CURL_DISABLE_BEARER_AUTH 1 + +/* disables Digest authentication */ +#cmakedefine CURL_DISABLE_DIGEST_AUTH 1 + +/* disables Kerberos authentication */ +#cmakedefine CURL_DISABLE_KERBEROS_AUTH 1 + +/* disables negotiate authentication */ +#cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1 + +/* disables AWS-SIG4 */ +#cmakedefine CURL_DISABLE_AWS 1 /* disables DICT */ #cmakedefine CURL_DISABLE_DICT 1 @@ -43,6 +61,9 @@ /* disables FILE */ #cmakedefine CURL_DISABLE_FILE 1 +/* disables form api */ +#cmakedefine CURL_DISABLE_FORM_API 1 + /* disables FTP */ #cmakedefine CURL_DISABLE_FTP 1 @@ -125,12 +146,6 @@ /* Use Windows LDAP implementation */ #cmakedefine USE_WIN32_LDAP 1 -/* when not building a shared library */ -#cmakedefine CURL_STATICLIB 1 - -/* your Entropy Gathering Daemon socket pathname */ -#cmakedefine EGD_SOCKET ${EGD_SOCKET} - /* Define if you want to enable IPv6 support */ #cmakedefine ENABLE_IPV6 1 @@ -392,8 +407,8 @@ /* Define to 1 if you have the <pwd.h> header file. */ #cmakedefine HAVE_PWD_H 1 -/* Define to 1 if you have the `RAND_egd' function. */ -#cmakedefine HAVE_RAND_EGD 1 +/* Define to 1 if OpenSSL has the `SSL_set0_wbio` function. */ +#cmakedefine HAVE_SSL_SET0_WBIO 1 /* Define to 1 if you have the recv function. */ #cmakedefine HAVE_RECV 1 @@ -455,9 +470,6 @@ /* Define to 1 if you have the socketpair function. */ #cmakedefine HAVE_SOCKETPAIR 1 -/* Define to 1 if you have the <ssl.h> header file. */ -#cmakedefine HAVE_SSL_H 1 - /* Define to 1 if you have the <stdatomic.h> header file. */ #cmakedefine HAVE_STDATOMIC_H 1 @@ -643,6 +655,9 @@ # define SIZEOF___INT64 KWIML_ABI_SIZEOF___INT64 #endif +/* The size of `long long', as computed by sizeof. */ +${SIZEOF_LONG_LONG_CODE} + /* The size of `off_t', as computed by sizeof. */ ${SIZEOF_OFF_T_CODE} @@ -700,12 +715,6 @@ ${SIZEOF_TIME_T_CODE} /* If you want to build curl with the built-in manual */ #cmakedefine USE_MANUAL 1 -/* if NSS is enabled */ -#cmakedefine USE_NSS 1 - -/* if you have the PK11_CreateManagedGenericObject function */ -#cmakedefine HAVE_PK11_CREATEMANAGEDGENERICOBJECT 1 - /* if you want to use OpenLDAP code instead of legacy ldap implementation */ #cmakedefine USE_OPENLDAP 1 diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c index 5c623b3..b77763f 100644 --- a/Utilities/cmcurl/lib/curl_des.c +++ b/Utilities/cmcurl/lib/curl_des.c @@ -24,12 +24,11 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) #include "curl_des.h" diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h index 6ec450a..66525ab 100644 --- a/Utilities/cmcurl/lib/curl_des.h +++ b/Utilities/cmcurl/lib/curl_des.h @@ -26,12 +26,11 @@ #include "curl_setup.h" -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) +#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ + (defined(USE_GNUTLS) || \ + defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO)) /* Applies odd parity to the given byte array */ void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index 11625c0..9438ca7 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h @@ -24,7 +24,8 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) #include <curl/curl.h> diff --git a/Utilities/cmcurl/lib/curl_log.h b/Utilities/cmcurl/lib/curl_log.h deleted file mode 100644 index ebfa5a0..0000000 --- a/Utilities/cmcurl/lib/curl_log.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef HEADER_CURL_LOG_H -#define HEADER_CURL_LOG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -struct Curl_easy; -struct Curl_cfilter; - -/** - * Init logging, return != 0 on failure. - */ -CURLcode Curl_log_init(void); - - -void Curl_infof(struct Curl_easy *, const char *fmt, ...); -void Curl_failf(struct Curl_easy *, const char *fmt, ...); - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif - -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define infof Curl_infof - -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define failf Curl_failf - - -#define CURL_LOG_DEFAULT 0 -#define CURL_LOG_DEBUG 1 -#define CURL_LOG_TRACE 2 - - -/* the function used to output verbose information */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size); - -#ifdef DEBUGBUILD - -/* explainer: we have some mix configuration and werror settings - * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced - * on gnuc and some other compiler. Need to treat carefully. - */ -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - -#define LOG_CF(data, cf, ...) \ - do { if(Curl_log_cf_is_debug(cf, data)) \ - Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0) -#else -#define LOG_CF Curl_log_cf_debug -#endif - -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, -#if defined(__GNUC__) && !defined(printf) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__MINGW32__) - const char *fmt, ...) - __attribute__((format(printf, 3, 4))); -#else - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(cf, data) \ - ((data) && (data)->set.verbose && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG) - - -#else /* !DEBUGBUILD */ - -#if defined(HAVE_VARIADIC_MACROS_C99) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(...) Curl_nop_stmt -#define Curl_log_cf_debug(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define LOG_CF(x...) Curl_nop_stmt -#define Curl_log_cf_debug(x...) Curl_nop_stmt -#else -#define LOG_CF Curl_log_cf_debug -/* without c99, we seem unable to completely define away this function. */ -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, - const char *fmt, ...); -#endif - -#define Curl_log_cf_is_debug(x,y) ((void)(x), (void)(y), FALSE) - -#endif /* !DEBUGBUILD */ - -#define LOG_CF_IS_DEBUG(cf, data) Curl_log_cf_is_debug(cf, data) - -#endif /* HEADER_CURL_LOG_H */ diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h index 03567b9..4706e49 100644 --- a/Utilities/cmcurl/lib/curl_md4.h +++ b/Utilities/cmcurl/lib/curl_md4.h @@ -25,14 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" +#include <curl/curl.h> -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if defined(USE_CURL_NTLM_CORE) #define MD4_DIGEST_LENGTH 16 -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len); +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len); -#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */ +#endif /* defined(USE_CURL_NTLM_CORE) */ #endif /* HEADER_CURL_MD4_H */ diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index ec2512f..61671c3 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h @@ -24,7 +24,9 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) + #include "curl_hmac.h" #define MD5_DIGEST_LEN 16 diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index ba8457d..cc0ed91 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c @@ -38,7 +38,7 @@ 1. USE_OPENSSL 2. USE_WOLFSSL 3. USE_GNUTLS - 4. USE_NSS + 4. - 5. USE_MBEDTLS 6. USE_SECTRANSP 7. USE_OS400CRYPTO @@ -47,7 +47,7 @@ This ensures that: - the same SSL branch gets activated throughout this source file even if multiple backends are enabled at the same time. - - OpenSSL and NSS have higher priority than Windows Crypt, due + - OpenSSL has higher priority than Windows Crypt, due to issues with the latter supporting NTLM2Session responses in NTLM type-3 messages. */ @@ -96,12 +96,6 @@ # include <nettle/des.h> -#elif defined(USE_NSS) - -# include <nss.h> -# include <pk11pub.h> -# include <hasht.h> - #elif defined(USE_MBEDTLS) # include <mbedtls/des.h> @@ -188,70 +182,6 @@ static void setup_des_key(const unsigned char *key_56, des_set_key(des, (const uint8_t *) key); } -#elif defined(USE_NSS) - -/* - * encrypt_des() expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of - * data, using the expanded key. IN should point to 64 bits of source data, - * OUT to a 64 bit output buffer. - */ -static bool encrypt_des(const unsigned char *in, unsigned char *out, - const unsigned char *key_56) -{ - const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */ - char key[8]; /* expanded 64 bit key */ - SECItem key_item; - PK11SymKey *symkey = NULL; - SECItem *param = NULL; - PK11Context *ctx = NULL; - int out_len; /* not used, required by NSS */ - bool rv = FALSE; - - /* use internal slot for DES encryption (requires NSS to be initialized) */ - PK11SlotInfo *slot = PK11_GetInternalKeySlot(); - if(!slot) - return FALSE; - - /* Expand the 56-bit key to 64-bits */ - extend_key_56_to_64(key_56, key); - - /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); - - /* Import the key */ - key_item.data = (unsigned char *)key; - key_item.len = sizeof(key); - symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT, - &key_item, NULL); - if(!symkey) - goto fail; - - /* Create the DES encryption context */ - param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL); - if(!param) - goto fail; - ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param); - if(!ctx) - goto fail; - - /* Perform the encryption */ - if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8, - (unsigned char *)in, /* inbuflen */ 8) - && SECSuccess == PK11_Finalize(ctx)) - rv = /* all OK */ TRUE; - -fail: - /* cleanup */ - if(ctx) - PK11_DestroyContext(ctx, PR_TRUE); - if(symkey) - PK11_FreeSymKey(symkey); - if(param) - SECITEM_FreeItem(param, PR_TRUE); - PK11_FreeSlot(slot); - return rv; -} - #elif defined(USE_MBEDTLS) static bool encrypt_des(const unsigned char *in, unsigned char *out, @@ -402,7 +332,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, des_encrypt(&des, 8, results + 8, plaintext); setup_des_key(keys + 14, &des); des_encrypt(&des, 8, results + 16, plaintext); -#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); @@ -444,7 +374,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ +#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); @@ -489,6 +419,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, { size_t len = strlen(password); unsigned char *pw; + CURLcode result; if(len > SIZE_T_MAX/2) /* avoid integer overflow */ return CURLE_OUT_OF_MEMORY; pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); @@ -498,12 +429,13 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, ascii_to_unicode_le(pw, password, len); /* Create NT hashed password. */ - Curl_md4it(ntbuffer, pw, 2 * len); - memset(ntbuffer + 16, 0, 21 - 16); + result = Curl_md4it(ntbuffer, pw, 2 * len); + if(!result) + memset(ntbuffer + 16, 0, 21 - 16); free(pw); - return CURLE_OK; + return result; } #if !defined(USE_WINDOWS_SSPI) diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h index 33b651f..0c62ee0 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.h +++ b/Utilities/cmcurl/lib/curl_ntlm_core.h @@ -28,15 +28,6 @@ #if defined(USE_CURL_NTLM_CORE) -/* If NSS is the first available SSL backend (see order in curl_ntlm_core.c) - then it must be initialized to be used by NTLM. */ -#if !defined(USE_OPENSSL) && \ - !defined(USE_WOLFSSL) && \ - !defined(USE_GNUTLS) && \ - defined(USE_NSS) -#define NTLM_NEEDS_NSS_INIT -#endif - #if defined(USE_OPENSSL) # include <openssl/ssl.h> #elif defined(USE_WOLFSSL) diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index 1cb0e54..91ddf10 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c @@ -420,7 +420,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if((enabledmechs & SASL_MECH_DIGEST_MD5) && Curl_auth_is_digest_supported()) { mech = SASL_MECH_STRING_DIGEST_MD5; @@ -530,8 +530,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; const char *hostname, *disp_hostname; int port; -#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_NTLM) +#if defined(USE_KERBEROS5) || defined(USE_NTLM) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : sasl->params->service; @@ -577,7 +577,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_EXTERNAL: result = Curl_auth_create_external_message(conn->user, &resp); break; -#ifndef CURL_DISABLE_CRYPTO_AUTH #ifdef USE_GSASL case SASL_GSASL: result = get_server_message(sasl, data, &serverdata); @@ -587,6 +586,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, newstate = SASL_GSASL; break; #endif +#ifndef CURL_DISABLE_DIGEST_AUTH case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index e395be5..9d5fe99 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h @@ -262,8 +262,9 @@ #if defined(__APPLE__) && !defined(USE_ARES) #include <TargetConditionals.h> #define USE_RESOLVE_ON_IPS 1 -# if !defined(TARGET_OS_OSX) || TARGET_OS_OSX -# define CURL_OSX_CALL_COPYPROXIES 1 +# if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \ + defined(ENABLE_IPV6) +# define CURL_MACOS_CALL_COPYPROXIES 1 # endif #endif @@ -659,32 +660,30 @@ #define LIBIDN_REQUIRED_VERSION "0.4.1" -#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ - defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_GSKIT) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) +#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ + defined(USE_BEARSSL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif /* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ +#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_SPNEGO #endif /* Single point where USE_KERBEROS5 definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ +#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_KERBEROS5 #endif /* Single point where USE_NTLM definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM) -# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +#if !defined(CURL_DISABLE_NTLM) +# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ + defined(USE_GNUTLS) || defined(USE_SECTRANSP) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) # define USE_CURL_NTLM_CORE # endif # if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index c5e157b..d99f958 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h @@ -25,7 +25,9 @@ * ***************************************************************************/ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) + #include <curl/curl.h> #include "curl_hmac.h" diff --git a/Utilities/cmcurl/lib/curl_log.c b/Utilities/cmcurl/lib/curl_trc.c index 782c35a..76c35ba 100644 --- a/Utilities/cmcurl/lib/curl_log.c +++ b/Utilities/cmcurl/lib/curl_trc.c @@ -26,7 +26,7 @@ #include <curl/curl.h> -#include "curl_log.h" +#include "curl_trc.h" #include "urldata.h" #include "easyif.h" #include "cfilters.h" @@ -124,13 +124,13 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...) } } -#ifdef DEBUGBUILD +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { DEBUGASSERT(cf); - if(data && Curl_log_cf_is_debug(cf, data)) { + if(data && Curl_trc_cf_is_verbose(cf, data)) { va_list ap; int len; char buffer[MAXINFO + 2]; @@ -179,44 +179,67 @@ static struct Curl_cftype *cf_types[] = { NULL, }; -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -CURLcode Curl_log_init(void) +CURLcode Curl_trc_opt(const char *config) { - const char *setting = getenv("CURL_DEBUG"); - if(setting) { - char *token, *tok_buf, *tmp; - size_t i; - - tmp = strdup(setting); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - token = strtok_r(tmp, ", ", &tok_buf); - while(token) { - for(i = 0; cf_types[i]; ++i) { - if(strcasecompare(token, cf_types[i]->name)) { - cf_types[i]->log_level = CURL_LOG_DEBUG; - break; - } + char *token, *tok_buf, *tmp; + size_t i; + int lvl; + + tmp = strdup(config); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + + token = strtok_r(tmp, ", ", &tok_buf); + while(token) { + switch(*token) { + case '-': + lvl = CURL_LOG_LVL_NONE; + ++token; + break; + case '+': + lvl = CURL_LOG_LVL_INFO; + ++token; + break; + default: + 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; } - token = strtok_r(NULL, ", ", &tok_buf); } - free(tmp); + token = strtok_r(NULL, ", ", &tok_buf); + } + free(tmp); + return CURLE_OK; +} + +CURLcode Curl_trc_init(void) +{ +#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); } +#endif return CURLE_OK; } -#else /* DEBUGBUILD */ +#else /* !CURL_DISABLE_VERBOSE_STRINGS) */ -CURLcode Curl_log_init(void) +CURLcode Curl_trc_init(void) { return CURLE_OK; } #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf, +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, const char *fmt, ...) { (void)data; diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h new file mode 100644 index 0000000..84b5471 --- /dev/null +++ b/Utilities/cmcurl/lib/curl_trc.h @@ -0,0 +1,150 @@ +#ifndef HEADER_CURL_TRC_H +#define HEADER_CURL_TRC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ + +struct Curl_easy; +struct Curl_cfilter; + +/** + * Init logging, return != 0 on failure. + */ +CURLcode Curl_trc_init(void); + +/** + * Configure tracing. May be called several times during global + * initialization. Later calls may not take effect. + * + * Configuration format supported: + * - comma-separated list of component names to enable logging on. + * E.g. 'http/2,ssl'. Unknown names are ignored. Names are compared + * case-insensitive. + * - component 'all' applies to all known log components + * - prefixing a component with '+' or '-' will en-/disable logging for + * that component + * Example: 'all,-ssl' would enable logging for all components but the + * SSL filters. + * + * @param config configuration string + */ +CURLcode Curl_trc_opt(const char *config); + +/* the function used to output verbose information */ +void Curl_debug(struct Curl_easy *data, curl_infotype type, + char *ptr, size_t size); + +/** + * Output an informational message when transfer's verbose logging is enabled. + */ +void Curl_infof(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +/** + * Output a failure message on registered callbacks for transfer. + */ +void Curl_failf(struct Curl_easy *data, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else + const char *fmt, ...); +#endif + +#define failf Curl_failf + +/** + * Output an informational message when both transfer's verbose logging + * and connection filters verbose logging are enabled. + */ +void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf, +#if defined(__GNUC__) && !defined(printf) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__MINGW32__) + const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +#else + const char *fmt, ...); +#endif + +#define CURL_LOG_LVL_NONE 0 +#define CURL_LOG_LVL_INFO 1 + + +#if !defined(CURL_DISABLE_VERBOSE_STRINGS) +/* informational messages enabled */ + +#define Curl_trc_is_verbose(data) ((data) && (data)->set.verbose) +#define Curl_trc_cf_is_verbose(cf, data) \ + ((data) && (data)->set.verbose && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) + +/* explainer: we have some mix configuration and werror settings + * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced + * on gnuc and some other compiler. Need to treat carefully. + */ +#if defined(HAVE_VARIADIC_MACROS_C99) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + +#define infof(data, ...) \ + do { if(Curl_trc_is_verbose(data)) \ + Curl_infof(data, __VA_ARGS__); } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { if(Curl_trc_cf_is_verbose(cf, data)) \ + Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) + +#else /* no variadic macro args */ +#define infof Curl_infof +#define CURL_TRC_CF Curl_trc_cf_infof +#endif /* variadic macro args */ + +#else /* !CURL_DISABLE_VERBOSE_STRINGS */ +/* All informational messages are not compiled in for size savings */ + +#define Curl_trc_is_verbose(d) ((void)(d), FALSE) +#define Curl_trc_cf_is_verbose(x,y) ((void)(x), (void)(y), FALSE) + +#if defined(HAVE_VARIADIC_MACROS_C99) +#define infof(...) Curl_nop_stmt +#define CURL_TRC_CF(...) Curl_nop_stmt +#define Curl_trc_cf_infof(...) Curl_nop_stmt +#elif defined(HAVE_VARIADIC_MACROS_GCC) +#define infof(x...) Curl_nop_stmt +#define CURL_TRC_CF(x...) Curl_nop_stmt +#define Curl_trc_cf_infof(x...) Curl_nop_stmt +#else +#error "missing VARIADIC macro define, fix and rebuild!" +#endif + +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + +#endif /* HEADER_CURL_TRC_H */ diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index d034629..16bbd35 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c @@ -158,8 +158,8 @@ static CURLcode global_init(long flags, bool memoryfuncs) #endif } - if(Curl_log_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n")); + if(Curl_trc_init()) { + DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n")); goto fail; } @@ -168,19 +168,15 @@ static CURLcode global_init(long flags, bool memoryfuncs) goto fail; } -#ifdef WIN32 if(Curl_win32_init(flags)) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); goto fail; } -#endif -#ifdef __AMIGA__ if(Curl_amiga_init()) { DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; } -#endif if(Curl_macos_init()) { DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); @@ -319,6 +315,26 @@ void curl_global_cleanup(void) global_init_unlock(); } +/** + * curl_global_trace() globally initializes curl logging. + */ +CURLcode curl_global_trace(const char *config) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + CURLcode result; + global_init_lock(); + + result = Curl_trc_opt(config); + + global_init_unlock(); + + return result; +#else + (void)config; + return CURLE_OK; +#endif +} + /* * curl_global_sslset() globally initializes the SSL backend to use. */ @@ -699,7 +715,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi) * * REALITY: it can't 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 re-used so that the same pools and + * function, the same multi handle must be reused so that the same pools and * caches can be used. * * DEBUG: if 'events' is set TRUE, this function will use a replacement engine @@ -1048,7 +1064,7 @@ void curl_easy_reset(struct Curl_easy *data) memset(&data->state.authhost, 0, sizeof(struct auth)); memset(&data->state.authproxy, 0, sizeof(struct auth)); -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif } diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index 2bdb9f2..8984b63 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c @@ -27,7 +27,7 @@ #include <curl/curl.h> #include "formdata.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) #include <libgen.h> @@ -941,7 +941,7 @@ int curl_formget(struct curl_httppost *form, void *arg, void curl_formfree(struct curl_httppost *form) { (void)form; - /* does nothing HTTP is disabled */ + /* Nothing to do. */ } #endif /* if disabled */ diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h index caabb63..af46624 100644 --- a/Utilities/cmcurl/lib/formdata.h +++ b/Utilities/cmcurl/lib/formdata.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_MIME +#ifndef CURL_DISABLE_FORM_API /* used by FormAdd for temporary storage */ struct FormInfo { @@ -53,10 +53,7 @@ CURLcode Curl_getformdata(struct Curl_easy *data, curl_mimepart *, struct curl_httppost *post, curl_read_callback fread_func); -#else -/* disabled */ -#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN -#endif +#endif /* CURL_DISABLE_FORM_API */ #endif /* HEADER_CURL_FORMDATA_H */ diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index a1cfa65..0ba8742 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -32,9 +32,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -865,7 +862,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data, if(conn->bits.reuse && ftpc->entrypath && /* no need to go to entrypath when we have an absolute path */ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { - /* This is a re-used connection. Since we change directory to where the + /* This is a reused connection. Since we change directory to where the transfer is taking place, we must first get back to the original dir where we ended up after login: */ ftpc->cwdcount = 0; /* we count this as the first path, then we add one @@ -975,7 +972,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(ip_end) { /* either ipv6 or (ipv4|domain|interface):port(-range) */ #ifdef ENABLE_IPV6 - if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) { + if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) { /* ipv6 */ port_min = port_max = 0; strcpy(addr, string_ftpport); @@ -1900,7 +1897,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, if(data->set.ftp_skip_ip) { /* told to ignore the remotely given IP but instead use the host we used for the control connection */ - infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead", + infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", ip[0], ip[1], ip[2], ip[3], conn->host.name); ftpc->newhost = strdup(control_address(conn)); diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 4a11d93..61e41b7 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c @@ -185,7 +185,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(strlen(sel) < 1) break; - result = Curl_write(data, sockfd, sel, k, &amount); + result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); if(result) @@ -227,7 +227,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) free(sel_org); if(!result) - result = Curl_write(data, sockfd, "\r\n", 2, &amount); + result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount); if(result) { failf(data, "Failed sending Gopher request"); return result; diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index 4367ce7..3ff4d5e 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c @@ -300,9 +300,16 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, if(data->state.prevhead) /* line folding, append value to the previous header's value */ return unfold_value(data, header, hlen); - else - /* can't unfold without a previous header */ - return CURLE_BAD_FUNCTION_ARGUMENT; + else { + /* Can't unfold without a previous header. Instead of erroring, just + pass the leading blanks. */ + while(hlen && ISBLANK(*header)) { + header++; + hlen--; + } + if(!hlen) + return CURLE_WEIRD_SERVER_REPLY; + } } hs = calloc(1, sizeof(*hs) + hlen); diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c index 8d8de17..87e7be8 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c @@ -26,7 +26,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_AWS) #include <curl/curl.h> @@ -169,4 +170,4 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, return CURLE_OK; } -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* Using NTLM (without SSPI) or AWS */ diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 1a289de..acb4b51 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c @@ -122,19 +122,6 @@ static void freednsentry(void *freethis); /* - * Return # of addresses in a Curl_addrinfo struct - */ -static int num_addresses(const struct Curl_addrinfo *addr) -{ - int i = 0; - while(addr) { - addr = addr->ai_next; - i++; - } - return i; -} - -/* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is * bufsize bytes big. @@ -388,6 +375,19 @@ Curl_fetch_addr(struct Curl_easy *data, } #ifndef CURL_DISABLE_SHUFFLE_DNS +/* + * Return # of addresses in a Curl_addrinfo struct + */ +static int num_addresses(const struct Curl_addrinfo *addr) +{ + int i = 0; + while(addr) { + addr = addr->ai_next; + i++; + } + return i; +} + UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, struct Curl_addrinfo **addr); /* @@ -601,7 +601,7 @@ bool Curl_ipv6works(struct Curl_easy *data) if(data) { /* the nature of most system is that IPv6 status doesn't come and go during a program's lifetime so we only probe the first time and then we - have the info kept for fast re-use */ + have the info kept for fast reuse */ DEBUGASSERT(data); DEBUGASSERT(data->multi); if(data->multi->ipv6_up == IPV6_UNKNOWN) { @@ -1240,7 +1240,7 @@ err: dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); if(dns) { - infof(data, "RESOLVE %.*s:%d is - old addresses discarded", + infof(data, "RESOLVE %.*s:%d - old addresses discarded", (int)hlen, host_begin, port); /* delete old entry, there are two reasons for this 1. old entry may have different addresses. diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index e611d27..4344b9d 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -233,7 +233,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data, if(!http) return CURLE_OUT_OF_MEMORY; - Curl_mime_initpart(&http->form); data->req.p.http = http; connkeep(conn, "HTTP default"); @@ -342,6 +341,8 @@ char *Curl_copy_header_value(const char *header) } #ifndef CURL_DISABLE_HTTP_AUTH + +#ifndef CURL_DISABLE_BASIC_AUTH /* * http_output_basic() sets up an Authorization: header (or the proxy version) * for HTTP Basic authentication. @@ -403,6 +404,9 @@ fail: return result; } +#endif + +#ifndef CURL_DISABLE_BEARER_AUTH /* * http_output_bearer() sets up an Authorization: header * for HTTP Bearer authentication. @@ -430,6 +434,8 @@ fail: #endif +#endif + /* pickoneauth() selects the most favourable authentication method from the * ones available and the ones we want. * @@ -446,18 +452,26 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) of preference in case of the existence of multiple accepted types. */ if(avail & CURLAUTH_NEGOTIATE) pick->picked = CURLAUTH_NEGOTIATE; +#ifndef CURL_DISABLE_BEARER_AUTH else if(avail & CURLAUTH_BEARER) pick->picked = CURLAUTH_BEARER; +#endif +#ifndef CURL_DISABLE_DIGEST_AUTH else if(avail & CURLAUTH_DIGEST) pick->picked = CURLAUTH_DIGEST; +#endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; else if(avail & CURLAUTH_NTLM_WB) pick->picked = CURLAUTH_NTLM_WB; +#ifndef CURL_DISABLE_BASIC_AUTH else if(avail & CURLAUTH_BASIC) pick->picked = CURLAUTH_BASIC; +#endif +#ifndef CURL_DISABLE_AWS else if(avail & CURLAUTH_AWS_SIGV4) pick->picked = CURLAUTH_AWS_SIGV4; +#endif else { pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ picked = FALSE; @@ -723,11 +737,11 @@ output_auth_headers(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)conn; -#ifdef CURL_DISABLE_CRYPTO_AUTH +#ifdef CURL_DISABLE_DIGEST_AUTH (void)request; (void)path; #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_AWS if(authstatus->picked == CURLAUTH_AWS_SIGV4) { auth = "AWS_SIGV4"; result = Curl_output_aws_sigv4(data, proxy); @@ -763,7 +777,7 @@ output_auth_headers(struct Curl_easy *data, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(authstatus->picked == CURLAUTH_DIGEST) { auth = "Digest"; result = Curl_output_digest(data, @@ -775,6 +789,7 @@ output_auth_headers(struct Curl_easy *data, } else #endif +#ifndef CURL_DISABLE_BASIC_AUTH if(authstatus->picked == CURLAUTH_BASIC) { /* Basic */ if( @@ -794,6 +809,8 @@ output_auth_headers(struct Curl_easy *data, functions work that way */ authstatus->done = TRUE; } +#endif +#ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ if((!proxy && data->set.str[STRING_BEARER] && @@ -808,6 +825,7 @@ output_auth_headers(struct Curl_easy *data, functions work that way */ authstatus->done = TRUE; } +#endif if(auth) { #ifndef CURL_DISABLE_PROXY @@ -866,7 +884,12 @@ Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || #endif - data->state.aptr.user || data->set.str[STRING_BEARER]) + data->state.aptr.user || +#ifdef USE_SPNEGO + authhost->want & CURLAUTH_NEGOTIATE || + authproxy->want & CURLAUTH_NEGOTIATE || +#endif + data->set.str[STRING_BEARER]) /* continue please */; else { authhost->done = TRUE; @@ -1064,7 +1087,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } else #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) { if((authp->avail & CURLAUTH_DIGEST) != 0) infof(data, "Ignoring duplicate digest auth header."); @@ -1087,6 +1110,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } else #endif +#ifndef CURL_DISABLE_BASIC_AUTH if(checkprefix("Basic", auth) && is_valid_auth_separator(auth[5])) { *availp |= CURLAUTH_BASIC; @@ -1101,6 +1125,8 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, } } else +#endif +#ifndef CURL_DISABLE_BEARER_AUTH if(checkprefix("Bearer", auth) && is_valid_auth_separator(auth[6])) { *availp |= CURLAUTH_BEARER; @@ -1113,6 +1139,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, data->state.authproblem = TRUE; } } +#endif /* there may be multiple methods on one line, so keep reading */ while(*auth && *auth != ',') /* read up to the next comma */ @@ -1277,7 +1304,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in, curl_off_t *bytes_written, /* how much of the buffer contains body data */ curl_off_t included_body_bytes, - int socketindex) + int sockindex) { ssize_t amount; CURLcode result; @@ -1285,12 +1312,9 @@ CURLcode Curl_buffer_send(struct dynbuf *in, size_t size; struct connectdata *conn = data->conn; size_t sendsize; - curl_socket_t sockfd; size_t headersize; - DEBUGASSERT(socketindex <= SECONDARYSOCKET); - - sockfd = Curl_conn_get_socket(data, socketindex); + DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0); /* The looping below is required since we use non-blocking sockets, but due to the circumstances we will just loop and try again and again etc */ @@ -1372,9 +1396,25 @@ CURLcode Curl_buffer_send(struct dynbuf *in, else sendsize = size; } + + /* We currently cannot send more that this for http here: + * - if sending blocks, it return 0 as amount + * - we then whisk aside the `in` into the `http` struct + * and install our own `data->state.fread_func` that + * on subsequent calls reads `in` empty. + * - when the whisked away `in` is empty, the `fread_func` + * is restored ot its original state. + * The problem is that `fread_func` can only return + * `upload_buffer_size` lengths. If the send we do here + * is larger and blocks, we do re-sending with smaller + * amounts of data and connection filters do not like + * that. + */ + if(http && (sendsize > (size_t)data->set.upload_buffer_size)) + sendsize = (size_t)data->set.upload_buffer_size; } - result = Curl_write(data, sockfd, ptr, sendsize, &amount); + result = Curl_nwrite(data, sockindex, ptr, sendsize, &amount); if(!result) { /* @@ -1527,7 +1567,7 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "HTTP default"); return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done); @@ -1572,7 +1612,6 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK; Curl_dyn_free(&http->send_buffer); - Curl_mime_cleanpart(&http->form); Curl_dyn_reset(&data->state.headerb); Curl_hyper_done(data); Curl_ws_done(data); @@ -2370,45 +2409,53 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, switch(httpreq) { case HTTPREQ_POST_MIME: - http->sendit = &data->set.mimepost; + data->state.mimepost = &data->set.mimepost; break; +#ifndef CURL_DISABLE_FORM_API case HTTPREQ_POST_FORM: - /* Convert the form structure into a mime structure. */ - Curl_mime_cleanpart(&http->form); - result = Curl_getformdata(data, &http->form, data->set.httppost, - data->state.fread_func); - if(result) - return result; - http->sendit = &http->form; + /* Convert the form structure into a mime structure, then keep + the conversion */ + if(!data->state.formp) { + data->state.formp = calloc(sizeof(curl_mimepart), 1); + if(!data->state.formp) + return CURLE_OUT_OF_MEMORY; + Curl_mime_cleanpart(data->state.formp); + result = Curl_getformdata(data, data->state.formp, data->set.httppost, + data->state.fread_func); + if(result) + return result; + data->state.mimepost = data->state.formp; + } break; +#endif default: - http->sendit = NULL; + data->state.mimepost = NULL; } #ifndef CURL_DISABLE_MIME - if(http->sendit) { + if(data->state.mimepost) { const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); /* Read and seek body only. */ - http->sendit->flags |= MIME_BODY_ONLY; + data->state.mimepost->flags |= MIME_BODY_ONLY; /* Prepare the mime structure headers & set content type. */ if(cthdr) for(cthdr += 13; *cthdr == ' '; cthdr++) ; - else if(http->sendit->kind == MIMEKIND_MULTIPART) + else if(data->state.mimepost->kind == MIMEKIND_MULTIPART) cthdr = "multipart/form-data"; - curl_mime_headers(http->sendit, data->set.headers, 0); - result = Curl_mime_prepare_headers(data, http->sendit, cthdr, + curl_mime_headers(data->state.mimepost, data->set.headers, 0); + result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr, NULL, MIMESTRATEGY_FORM); - curl_mime_headers(http->sendit, NULL, 0); + curl_mime_headers(data->state.mimepost, NULL, 0); if(!result) - result = Curl_mime_rewind(http->sendit); + result = Curl_mime_rewind(data->state.mimepost); if(result) return result; - http->postsize = Curl_mime_size(http->sendit); + http->postsize = Curl_mime_size(data->state.mimepost); } #endif @@ -2564,7 +2611,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, { struct curl_slist *hdr; - for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) { + for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) { result = Curl_dyn_addf(r, "%s\r\n", hdr->data); if(result) return result; @@ -2599,7 +2646,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, /* Read from mime structure. */ data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) http->sendit; + data->state.in = (void *) data->state.mimepost; http->sending = HTTPSEND_BODY; /* this sends the buffer and frees all the buffer resources */ @@ -2683,7 +2730,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, if(!data->req.upload_chunky) { /* We're not sending it 'chunked', append it to the request - already now to reduce the number if send() calls */ + already now to reduce the number of send() calls */ result = Curl_dyn_addn(r, data->set.postfields, (size_t)http->postsize); included_body = http->postsize; @@ -3024,7 +3071,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, *done = TRUE; return CURLE_OK; } - /* We have a new url to load, but since we want to be able to re-use 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"); @@ -3064,7 +3111,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, data->info.httpcode = 304; infof(data, "Simulate an HTTP 304 response"); /* we abort the transfer before it is completed == we ruin the - re-use ability. Close the connection */ + reuse ability. Close the connection */ streamclose(conn, "Simulated 304 handling"); return CURLE_OK; } @@ -3308,8 +3355,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) altused ? altused : "" ); - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ + /* clear userpwd and proxyuserpwd to avoid reusing old credentials + * from reused connections */ Curl_safefree(data->state.aptr.userpwd); Curl_safefree(data->state.aptr.proxyuserpwd); free(altused); @@ -3918,6 +3965,29 @@ static CURLcode verify_header(struct Curl_easy *data) return CURLE_OK; } +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only) +{ + size_t bad = 0; + if(delta < MAX_HTTP_RESP_HEADER_SIZE) { + if(!connect_only) + data->req.headerbytecount += (unsigned int)delta; + data->info.header_size += (unsigned int)delta; + if(data->info.header_size > MAX_HTTP_RESP_HEADER_SIZE) + bad = data->info.header_size; + } + else + bad = data->info.header_size + delta; + if(bad) { + failf(data, "Too large response headers: %zu > %u", + bad, MAX_HTTP_RESP_HEADER_SIZE); + return CURLE_RECV_ERROR; + } + return CURLE_OK; +} + + /* * Read any HTTP header lines from the server and pass them to the client app. */ @@ -4056,6 +4126,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* Switching Protocols */ if(k->upgr101 == UPGR101_H2) { /* Switching to HTTP/2 */ + DEBUGASSERT(conn->httpversion < 20); infof(data, "Received 101, Switching to HTTP/2"); k->upgr101 = UPGR101_RECEIVED; @@ -4098,6 +4169,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } else { + if(k->upgr101 == UPGR101_H2) { + /* A requested upgrade was denied, poke the multi handle to possibly + allow a pending pipewait to continue */ + Curl_multi_connchanged(data->multi); + } k->header = FALSE; /* no more header to parse! */ if((k->size == -1) && !k->chunk && !conn->bits.close && @@ -4165,8 +4241,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; - data->info.header_size += (long)headerlen; - data->req.headerbytecount += (long)headerlen; + result = Curl_bump_headersize(data, headerlen, FALSE); + if(result) + return result; /* * When all the headers have been parsed, see if we should give @@ -4229,7 +4306,18 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if((k->httpcode == 417) && data->state.expect100header) { /* 417 Expectation Failed - try again without the Expect header */ - infof(data, "Got 417 while waiting for a 100"); + if(!k->writebytecount && + k->exp100 == EXP100_AWAITING_CONTINUE) { + infof(data, "Got HTTP failure 417 while waiting for a 100"); + } + else { + infof(data, "Got HTTP failure 417 while sending data"); + streamclose(conn, + "Stop sending data before everything sent"); + result = http_perhapsrewind(data, conn); + if(result) + return result; + } data->state.disableexpect = TRUE; DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->state.url); @@ -4488,8 +4576,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; - data->info.header_size += Curl_dyn_len(&data->state.headerb); - data->req.headerbytecount += Curl_dyn_len(&data->state.headerb); + result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb), + FALSE); + if(result) + return result; Curl_dyn_reset(&data->state.headerb); } diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index df3b4e3..9ee3c65 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h @@ -64,6 +64,10 @@ extern const struct Curl_handler Curl_handler_wss; struct dynhds; +CURLcode Curl_bump_headersize(struct Curl_easy *data, + size_t delta, + bool connect_only); + /* Header specific functions */ bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ @@ -183,21 +187,19 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data); #define EXPECT_100_THRESHOLD (1024*1024) #endif +/* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers + combined that libcurl allows for a single HTTP response, any HTTP + version. This count includes CONNECT response headers. */ +#define MAX_HTTP_RESP_HEADER_SIZE (300*1024) + #endif /* CURL_DISABLE_HTTP */ /**************************************************************************** * HTTP unique setup ***************************************************************************/ struct HTTP { - curl_mimepart *sendit; curl_off_t postsize; /* off_t to handle large file sizes */ const char *postdata; - - const char *p_pragma; /* Pragma: string */ - - /* For FORM posting */ - curl_mimepart form; - struct back { curl_read_callback fread_func; /* backup storage for fread pointer */ void *fread_in; /* backup storage for fread_in pointer */ @@ -292,7 +294,7 @@ void Curl_http_req_free(struct httpreq *req); /** * Create the list of HTTP/2 headers which represent the request, - * using HTTP/2 pseudo headers preceeding the `req->headers`. + * using HTTP/2 pseudo headers preceding the `req->headers`. * * Applies the following transformations: * - if `authority` is set, any "Host" header is removed. diff --git a/Utilities/cmcurl/lib/http1.c b/Utilities/cmcurl/lib/http1.c index a442d3e..1ca7d41 100644 --- a/Utilities/cmcurl/lib/http1.c +++ b/Utilities/cmcurl/lib/http1.c @@ -163,7 +163,7 @@ static CURLcode start_req(struct h1_req_parser *parser, break; } } - /* no SPACE found or empty TARGET or empy HTTP_VERSION */ + /* no SPACE found or empty TARGET or empty HTTP_VERSION */ if(!target_len || !hv_len) goto out; diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index 6c09ec1..e0cda76 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -41,6 +41,7 @@ #include "urlapi-int.h" #include "cfilters.h" #include "connect.h" +#include "rand.h" #include "strtoofft.h" #include "strdup.h" #include "transfer.h" @@ -69,7 +70,7 @@ #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) -/* on receving from TLS, we prep for holding a full stream window */ +/* on receiving from TLS, we prep for holding a full stream window */ #define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) /* on send into TLS, we just want to accumulate small frames */ #define H2_NW_SEND_CHUNKS 1 @@ -175,6 +176,7 @@ struct stream_ctx { int32_t id; /* HTTP/2 protocol identifier for stream */ struct bufq recvbuf; /* response buffer */ struct bufq sendbuf; /* request buffer */ + 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; @@ -187,6 +189,7 @@ struct stream_ctx { int status_code; /* HTTP response status code */ uint32_t error; /* stream error code */ uint32_t local_window_size; /* the local recv window size */ + bool resp_hds_complete; /* we have a complete, final response */ bool closed; /* TRUE on stream close */ bool reset; /* TRUE on stream reset */ bool close_handled; /* TRUE if stream closure is handled by libcurl */ @@ -217,8 +220,8 @@ static void drain_stream(struct Curl_cfilter *cf, (stream->upload_left || stream->upload_blocked_len)) bits |= CURL_CSELECT_OUT; if(data->state.dselect_bits != bits) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x", - stream->id, bits)); + CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x", + stream->id, bits); data->state.dselect_bits = bits; Curl_expire(data, 0, EXPIRE_RUN_NOW); } @@ -252,6 +255,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); stream->resp_hds_len = 0; stream->bodystarted = FALSE; @@ -281,8 +285,8 @@ static void http2_data_done(struct Curl_cfilter *cf, if(ctx->h2) { if(!stream->closed && stream->id > 0) { /* RST_STREAM */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] premature DATA_DONE, RST stream", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream", + stream->id); if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, stream->id, NGHTTP2_STREAM_CLOSED)) (void)nghttp2_session_send(ctx->h2); @@ -312,6 +316,7 @@ static void http2_data_done(struct Curl_cfilter *cf, Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); Curl_dynhds_free(&stream->resp_trailers); if(stream->push_headers) { /* if they weren't used and then freed before */ @@ -364,8 +369,12 @@ static ssize_t nw_out_writer(void *writer_ctx, { struct Curl_cfilter *cf = writer_ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); + ssize_t nwritten; - return Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); + nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err); + if(nwritten > 0) + CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); + return nwritten; } static ssize_t send_callback(nghttp2_session *h2, @@ -373,6 +382,10 @@ static ssize_t send_callback(nghttp2_session *h2, void *userp); static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp); +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp); +#endif static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *mem, size_t len, void *userp); @@ -389,18 +402,6 @@ static int error_callback(nghttp2_session *session, const char *msg, size_t len, void *userp); /* - * multi_connchanged() is called to tell that there is a connection in - * this multi handle that has changed state (multiplexing become possible, the - * number of allowed streams changed or similar), and a subsequent use of this - * multi handle should move CONNECT_PEND handles back to CONNECT to have them - * retry. - */ -static void multi_connchanged(struct Curl_multi *multi) -{ - multi->recheckstate = TRUE; -} - -/* * Initialize the cfilter context */ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, @@ -427,6 +428,9 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, nghttp2_session_callbacks_set_send_callback(cbs, send_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); +#endif nghttp2_session_callbacks_set_on_data_chunk_recv_callback( cbs, on_data_chunk_recv); nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); @@ -475,6 +479,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, stream->id); DEBUGASSERT(0); } + CURL_TRC_CF(data, cf, "created session via Upgrade"); } else { nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; @@ -502,6 +507,8 @@ 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)" : ""); out: if(cbs) @@ -547,8 +554,8 @@ static int h2_process_pending_input(struct Curl_cfilter *cf, break; } else { - DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left " - "in connection buffer", Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left " + "in connection buffer", Curl_bufq_len(&ctx->inbufq)); } } @@ -590,8 +597,8 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, *input_pending = FALSE; nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); if(nread != -1) { - DEBUGF(LOG_CF(data, cf, "%zd bytes stray data read before trying " - "h2 connection", nread)); + CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying " + "h2 connection", nread); if(h2_process_pending_input(cf, data, &result) < 0) /* immediate error, considered dead */ alive = FALSE; @@ -653,13 +660,12 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf, nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", - Curl_bufq_len(&ctx->outbufq))); + CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->outbufq)); ctx->nw_out_blocked = 1; } return result; } - DEBUGF(LOG_CF(data, cf, "nw send buffer flushed")); return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; } @@ -851,8 +857,8 @@ static int push_promise(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; int rv; /* one of the CURL_PUSH_* defines */ - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] PUSH_PROMISE received", - frame->promised_stream_id)); + CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", + frame->promised_stream_id); if(data->multi->push_cb) { struct stream_ctx *stream; struct stream_ctx *newstream; @@ -871,7 +877,7 @@ static int push_promise(struct Curl_cfilter *cf, heads.data = data; heads.frame = frame; /* ask the application */ - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application")); + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application"); stream = H2_STREAM_CTX(data); if(!stream) { @@ -943,7 +949,7 @@ static int push_promise(struct Curl_cfilter *cf, } } else { - DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it")); + CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); rv = CURL_PUSH_DENY; } fail: @@ -980,20 +986,19 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, int rv; if(!stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No proto pointer", stream_id)); + CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id); return CURLE_FAILED_INIT; } switch(frame->hd.type) { case NGHTTP2_DATA: rbuflen = Curl_bufq_len(&stream->recvbuf); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[DATA len=%zu pad=%zu], " - "buffered=%zu, window=%d/%d", - stream_id, frame->hd.length, frame->data.padlen, rbuflen, - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id))); + CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d", + stream_id, rbuflen, + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id)); /* If !body started on this stream, then receiving DATA is illegal. */ if(!stream->bodystarted) { rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1011,7 +1016,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, ctx->h2, stream->id); if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) { /* H2 flow control is not absolute, as the server might not have the - * same view, yet. When we recieve more than we want, we enforce + * same view, yet. When we receive more than we want, we enforce * the local window size again to make nghttp2 send WINDOW_UPATEs * accordingly. */ nghttp2_session_set_local_window_size(ctx->h2, @@ -1022,7 +1027,6 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, } break; case NGHTTP2_HEADERS: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[HEADERS]", stream_id)); if(stream->bodystarted) { /* Only valid HEADERS after body started is trailer HEADERS. We buffer them in on_header callback. */ @@ -1045,12 +1049,12 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(result) return result; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] %zu header bytes", - stream_id, Curl_bufq_len(&stream->recvbuf))); + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } drain_stream(cf, data, stream); break; case NGHTTP2_PUSH_PROMISE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[PUSH_PROMISE]", stream_id)); rv = push_promise(cf, data, &frame->push_promise); if(rv) { /* deny! */ DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); @@ -1060,38 +1064,127 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(nghttp2_is_fatal(rv)) return CURLE_SEND_ERROR; else if(rv == CURL_PUSH_ERROROUT) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] fail in PUSH_PROMISE received", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received", + stream_id); return CURLE_RECV_ERROR; } } break; case NGHTTP2_RST_STREAM: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[RST]", stream_id)); stream->closed = TRUE; - stream->reset = TRUE; + if(frame->rst_stream.error_code) { + stream->reset = TRUE; + } stream->send_closed = TRUE; data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); break; case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[WINDOW_UPDATE]", stream_id)); if((data->req.keepon & KEEP_SEND_HOLD) && (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; drain_stream(cf, data, stream); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after win update", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] un-holding after win update", + stream_id); } break; default: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[%x]", - stream_id, frame->hd.type)); break; } return CURLE_OK; } +#ifndef CURL_DISABLE_VERBOSE_STRINGS +static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) +{ + switch(frame->hd.type) { + case NGHTTP2_DATA: { + return msnprintf(buffer, blen, + "FRAME[DATA, len=%d, eos=%d, padlen=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), + (int)frame->data.padlen); + } + case NGHTTP2_HEADERS: { + return msnprintf(buffer, blen, + "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), + !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); + } + case NGHTTP2_PRIORITY: { + return msnprintf(buffer, blen, + "FRAME[PRIORITY, len=%d, flags=%d]", + (int)frame->hd.length, frame->hd.flags); + } + case NGHTTP2_RST_STREAM: { + return msnprintf(buffer, blen, + "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", + (int)frame->hd.length, frame->hd.flags, + frame->rst_stream.error_code); + } + case NGHTTP2_SETTINGS: { + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); + } + return msnprintf(buffer, blen, + "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); + } + case NGHTTP2_PUSH_PROMISE: { + return msnprintf(buffer, blen, + "FRAME[PUSH_PROMISE, len=%d, hend=%d]", + (int)frame->hd.length, + !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); + } + case NGHTTP2_PING: { + return msnprintf(buffer, blen, + "FRAME[PING, len=%d, ack=%d]", + (int)frame->hd.length, + frame->hd.flags&NGHTTP2_FLAG_ACK); + } + case NGHTTP2_GOAWAY: { + char scratch[128]; + size_t s_len = sizeof(scratch)/sizeof(scratch[0]); + size_t len = (frame->goaway.opaque_data_len < s_len)? + frame->goaway.opaque_data_len : s_len-1; + if(len) + memcpy(scratch, frame->goaway.opaque_data, len); + scratch[len] = '\0'; + return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " + "last_stream=%d]", frame->goaway.error_code, + scratch, frame->goaway.last_stream_id); + } + case NGHTTP2_WINDOW_UPDATE: { + return msnprintf(buffer, blen, + "FRAME[WINDOW_UPDATE, incr=%d]", + frame->window_update.window_size_increment); + } + default: + return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", + frame->hd.type, (int)frame->hd.length, + frame->hd.flags); + } +} + +static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, + void *userp) +{ + struct Curl_cfilter *cf = userp; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + + (void)session; + DEBUGASSERT(data); + if(data && Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); + } + return 0; +} +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp) { @@ -1101,40 +1194,51 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, int32_t stream_id = frame->hd.stream_id; DEBUGASSERT(data); +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_cf_is_verbose(cf, data)) { + char buffer[256]; + int len; + len = fr_print(frame, buffer, sizeof(buffer)-1); + buffer[len] = 0; + CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); + } +#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ + if(!stream_id) { /* stream ID zero is for connection-oriented stuff */ DEBUGASSERT(data); switch(frame->hd.type) { case NGHTTP2_SETTINGS: { - uint32_t max_conn = ctx->max_concurrent_streams; - DEBUGF(LOG_CF(data, cf, "FRAME[SETTINGS]")); - ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - ctx->enable_push = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d", - ctx->max_concurrent_streams)); - DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s", - ctx->enable_push ? "TRUE" : "false")); - if(data && max_conn != ctx->max_concurrent_streams) { - /* only signal change if the value actually changed */ - DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u", - ctx->max_concurrent_streams)); - multi_connchanged(data->multi); - } - /* Since the initial stream window is 64K, a request might be on HOLD, - * due to exhaustion. The (initial) SETTINGS may announce a much larger - * window and *assume* that we treat this like a WINDOW_UPDATE. Some - * servers send an explicit WINDOW_UPDATE, but not all seem to do that. - * To be safe, we UNHOLD a stream in order not to stall. */ - if((data->req.keepon & KEEP_SEND_HOLD) && - (data->req.keepon & KEEP_SEND)) { - struct stream_ctx *stream = H2_STREAM_CTX(data); - data->req.keepon &= ~KEEP_SEND_HOLD; - if(stream) { - drain_stream(cf, data, stream); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after SETTINGS", - stream_id)); + if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) { + uint32_t max_conn = ctx->max_concurrent_streams; + ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + ctx->enable_push = nghttp2_session_get_remote_settings( + session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; + CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", + ctx->max_concurrent_streams); + CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", + ctx->enable_push ? "TRUE" : "false"); + if(data && max_conn != ctx->max_concurrent_streams) { + /* only signal change if the value actually changed */ + CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u", + ctx->max_concurrent_streams); + Curl_multi_connchanged(data->multi); + } + /* Since the initial stream window is 64K, a request might be on HOLD, + * due to exhaustion. The (initial) SETTINGS may announce a much larger + * window and *assume* that we treat this like a WINDOW_UPDATE. Some + * servers send an explicit WINDOW_UPDATE, but not all seem to do that. + * To be safe, we UNHOLD a stream in order not to stall. */ + if((data->req.keepon & KEEP_SEND_HOLD) && + (data->req.keepon & KEEP_SEND)) { + struct stream_ctx *stream = H2_STREAM_CTX(data); + data->req.keepon &= ~KEEP_SEND_HOLD; + if(stream) { + drain_stream(cf, data, stream); + CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS", + stream_id); + } } } break; @@ -1144,26 +1248,20 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, ctx->goaway_error = frame->goaway.error_code; ctx->last_stream_id = frame->goaway.last_stream_id; if(data) { - DEBUGF(LOG_CF(data, cf, "FRAME[GOAWAY, error=%d, last_stream=%u]", - ctx->goaway_error, ctx->last_stream_id)); infof(data, "received GOAWAY, error=%d, last_stream=%u", ctx->goaway_error, ctx->last_stream_id); - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); } break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF(LOG_CF(data, cf, "FRAME[WINDOW_UPDATE]")); - break; default: - DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type)); + break; } return 0; } data_s = nghttp2_session_get_stream_user_data(session, stream_id); if(!data_s) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No Curl_easy associated", - stream_id)); + CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id); return 0; } @@ -1190,8 +1288,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* Receiving a Stream ID not in the hash should not happen - unless we have aborted a transfer artificially and there were more data in the pipeline. Silently ignore. */ - DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%d] Data for unknown", - stream_id)); + CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", + stream_id); /* consumed explicitly as no one will read it */ nghttp2_session_consume(session, stream_id, len); return 0; @@ -1233,8 +1331,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, return 0; } stream = H2_STREAM_CTX(data_s); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] on_stream_close(), %s (err %d)", - stream_id, nghttp2_http2_strerror(error_code), error_code)); if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1244,6 +1340,11 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->reset = TRUE; data_s->req.keepon &= ~KEEP_SEND_HOLD; + if(stream->error) + CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", + stream_id, nghttp2_http2_strerror(error_code), error_code); + else + CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); drain_stream(cf, data_s, stream); /* remove `data_s` from the nghttp2 stream */ @@ -1253,7 +1354,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream_id); DEBUGASSERT(0); } - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] closed now", stream_id)); return 0; } @@ -1270,8 +1370,6 @@ static int on_begin_headers(nghttp2_session *session, return 0; } - DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called")); - if(frame->hd.type != NGHTTP2_HEADERS) { return 0; } @@ -1377,10 +1475,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] trailer: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); result = Curl_dynhds_add(&stream->resp_trailers, (const char *)name, namelen, (const char *)value, valuelen); @@ -1417,8 +1513,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] status: HTTP/2 %03d", - stream->id, stream->status_code)); + CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", + stream->id, stream->status_code); return 0; } @@ -1441,10 +1537,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(CF_DATA_CURRENT(cf) != data_s) Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] header: %.*s: %.*s", - stream->id, - (int)namelen, name, - (int)valuelen, value)); + CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", + stream->id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ } @@ -1490,9 +1584,9 @@ static ssize_t req_body_read_callback(nghttp2_session *session, if(nread > 0 && stream->upload_left != -1) stream->upload_left -= nread; - DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%" - CURL_FORMAT_CURL_OFF_T " -> %zd, %d", - stream_id, length, stream->upload_left, 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) *data_flags = NGHTTP2_DATA_FLAG_EOF; @@ -1565,7 +1659,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf, if(!ctx || !ctx->h2 || !stream) goto out; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] data done send", stream->id)); + CURL_TRC_CF(data, cf, "[%d] data done send", stream->id); if(!stream->send_closed) { stream->send_closed = TRUE; if(stream->upload_left) { @@ -1590,8 +1684,8 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, ssize_t rv = 0; if(stream->error == NGHTTP2_REFUSED_STREAM) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] REFUSED_STREAM, try again on a new " - "connection", stream->id)); + 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 */ data->state.refused_stream = TRUE; *err = CURLE_SEND_ERROR; /* trigger Curl_retry_request() later */ @@ -1652,7 +1746,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, rv = 0; out: - DEBUGF(LOG_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err)); + CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err); return rv; } @@ -1709,8 +1803,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, nghttp2_priority_spec pri_spec; h2_pri_spec(data, &pri_spec); - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, stream->id, &pri_spec); @@ -1724,26 +1817,24 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, out: if(nghttp2_is_fatal(rv)) { - DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); + CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d", + nghttp2_strerror(rv), rv); return CURLE_SEND_ERROR; } return nw_out_flush(cf, data); } static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + struct stream_ctx *stream, char *buf, size_t len, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - struct stream_ctx *stream = H2_STREAM_CTX(data); ssize_t nread = -1; *err = CURLE_AGAIN; if(!Curl_bufq_is_empty(&stream->recvbuf)) { nread = Curl_bufq_read(&stream->recvbuf, (unsigned char *)buf, len, err); - DEBUGF(LOG_CF(data, cf, "recvbuf read(len=%zu) -> %zd, %d", - len, nread, *err)); if(nread < 0) goto out; DEBUGASSERT(nread > 0); @@ -1751,13 +1842,13 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0) { if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning CLOSE", stream->id)); + CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); nread = http2_handle_stream_close(cf, data, stream, err); } else if(stream->reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || (ctx->goaway && ctx->last_stream_id < stream->id)) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning ERR", stream->id)); + CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; nread = -1; } @@ -1768,8 +1859,9 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "stream_recv(len=%zu) -> %zd, %d", - len, nread, *err)); + if(nread < 0 && *err != CURLE_AGAIN) + CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", + stream->id, len, nread, *err); return nread; } @@ -1783,8 +1875,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, /* Process network input buffer fist */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { - DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer", - Curl_bufq_len(&ctx->inbufq))); + CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer", + Curl_bufq_len(&ctx->inbufq)); if(h2_process_pending_input(cf, data, &result) < 0) return result; } @@ -1804,8 +1896,6 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, } nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - /* DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result)); */ if(nread < 0) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data: %d(%s)", result, @@ -1815,9 +1905,14 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, break; } else if(nread == 0) { + CURL_TRC_CF(data, cf, "[0] ingress: connection closed"); ctx->conn_closed = TRUE; break; } + else { + CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", + nread); + } if(h2_process_pending_input(cf, data, &result)) return result; @@ -1839,9 +1934,21 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; struct cf_call_data save; + if(!stream) { + /* Abnormal call sequence: either this transfer has never opened a stream + * (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); + *err = CURLE_HTTP2; + return -1; + } + CF_DATA_SAVE(save, cf, data); - nread = stream_recv(cf, data, buf, len, err); + nread = stream_recv(cf, data, stream, buf, len, err); if(nread < 0 && *err != CURLE_AGAIN) goto out; @@ -1850,7 +1957,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*err) goto out; - nread = stream_recv(cf, data, buf, len, err); + nread = stream_recv(cf, data, stream, buf, len, err); } if(nread > 0) { @@ -1872,28 +1979,34 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } if(stream->closed) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] closed stream, set drain", - stream->id)); + CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); drain_stream(cf, data, stream); } } out: result = h2_progress_egress(cf, data); - if(result && result != CURLE_AGAIN) { + 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); + } + else if(result) { *err = result; nread = -1; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_recv(len=%zu) -> %zd %d, " - "buffered=%zu, window=%d/%d, connection %d/%d", - stream->id, len, nread, *err, - Curl_bufq_len(&stream->recvbuf), - nghttp2_session_get_stream_effective_recv_data_length( - ctx->h2, stream->id), - nghttp2_session_get_stream_effective_local_window_size( - ctx->h2, stream->id), - nghttp2_session_get_local_window_size(ctx->h2), - HTTP2_HUGE_WINDOW_SIZE)); + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " + "buffered=%zu, window=%d/%d, connection %d/%d", + stream->id, len, nread, *err, + Curl_bufq_len(&stream->recvbuf), + nghttp2_session_get_stream_effective_recv_data_length( + ctx->h2, stream->id), + nghttp2_session_get_stream_effective_local_window_size( + ctx->h2, stream->id), + nghttp2_session_get_local_window_size(ctx->h2), + HTTP2_HUGE_WINDOW_SIZE); CF_DATA_RESTORE(cf, save); return nread; @@ -1905,7 +2018,6 @@ static ssize_t h2_submit(struct stream_ctx **pstream, { struct cf_h2_ctx *ctx = cf->ctx; struct stream_ctx *stream = NULL; - struct h1_req_parser h1; struct dynhds h2_headers; nghttp2_nv *nva = NULL; const void *body = NULL; @@ -1915,7 +2027,6 @@ static ssize_t h2_submit(struct stream_ctx **pstream, nghttp2_priority_spec pri_spec; ssize_t nwritten; - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = http2_data_setup(cf, data, &stream); @@ -1924,17 +2035,22 @@ static ssize_t h2_submit(struct stream_ctx **pstream, goto out; } - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(nghttp2_nv) * nheader); @@ -1953,29 +2069,9 @@ static ssize_t h2_submit(struct stream_ctx **pstream, nva[i].flags = NGHTTP2_NV_FLAG_NONE; } -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - - for(i = 0; i < nheader; ++i) { - acc += nva[i].namelen + nva[i].valuelen; - - infof(data, "h2 [%.*s: %.*s]", - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); - } - } - h2_pri_spec(data, &pri_spec); - - DEBUGF(LOG_CF(data, cf, "send request allowed %d", - nghttp2_session_check_request_allowed(ctx->h2))); + if(!nghttp2_session_check_request_allowed(ctx->h2)) + CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); switch(data->state.httpreq) { case HTTPREQ_POST: @@ -1999,19 +2095,35 @@ static ssize_t h2_submit(struct stream_ctx **pstream, NULL, data); } - Curl_safefree(nva); - if(stream_id < 0) { - DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", - nghttp2_strerror(stream_id), stream_id)); + CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + nghttp2_strerror(stream_id), stream_id); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) submit %s", - stream_id, len, data->state.url)); - infof(data, "Using Stream ID: %u", stream_id); +#define MAX_ACC 60000 /* <64KB to account for some overhead */ + if(Curl_trc_is_verbose(data)) { + size_t acc = 0; + + infof(data, "[HTTP/2] [%d] OPENED stream for %s", + stream_id, data->state.url); + for(i = 0; i < nheader; ++i) { + acc += nva[i].namelen + nva[i].valuelen; + + infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + + if(acc > MAX_ACC) { + infof(data, "[HTTP/2] Warning: The cumulative length of all " + "headers exceeds %d bytes and that could cause the " + "stream to be rejected.", MAX_ACC); + } + } + stream->id = stream_id; stream->local_window_size = H2_STREAM_WINDOW_SIZE; if(data->set.max_recv_speed) { @@ -2042,10 +2154,10 @@ static ssize_t h2_submit(struct stream_ctx **pstream, } out: - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] submit -> %zd, %d", - stream? stream->id : -1, nwritten, *err)); + CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d", + stream? stream->id : -1, nwritten, *err); + Curl_safefree(nva); *pstream = stream; - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -2059,46 +2171,55 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, int rv; ssize_t nwritten; CURLcode result; - int blocked = 0; + int blocked = 0, was_blocked = 0; CF_DATA_SAVE(save, cf, data); if(stream && stream->id != -1) { - if(stream->close_handled) { - infof(data, "stream %u closed", stream->id); - *err = CURLE_HTTP2_STREAM; - nwritten = -1; - goto out; - } - else if(stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); - goto out; - } - else if(stream->upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the + 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 - * happend. We are not prepared to handle that. */ - failf(data, "HTTP/2 send again with decreased length"); + * 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; + 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) { - if(*err != CURLE_AGAIN) - goto out; - nwritten = 0; - } + if(nwritten < 0 && *err != CURLE_AGAIN) + goto out; } if(!Curl_bufq_is_empty(&stream->sendbuf)) { @@ -2124,8 +2245,10 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 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) { - nwritten = http2_handle_stream_close(cf, data, stream, err); + if(stream && stream->closed && !was_blocked) { + infof(data, "stream %u closed", stream->id); + *err = CURLE_SEND_ERROR; + nwritten = -1; goto out; } else if(result == CURLE_AGAIN) { @@ -2143,7 +2266,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, blocked = 1; } - if(stream && blocked) { + 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. */ @@ -2153,19 +2276,19 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* H2 flow window exhaustion. We need to HOLD upload until we get * a WINDOW_UPDATE from the server. */ data->req.keepon |= KEEP_SEND_HOLD; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] holding send as remote flow " - "window is exhausted", stream->id)); + CURL_TRC_CF(data, cf, "[%d] holding send as remote flow " + "window is exhausted", 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. */ stream->upload_blocked_len = nwritten; - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) BLOCK: win %u/%zu " - "blocked_len=%zu", - stream->id, len, - nghttp2_session_get_remote_window_size(ctx->h2), rwin, - nwritten)); + CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " + "blocked_len=%zu", + stream->id, len, + nghttp2_session_get_remote_window_size(ctx->h2), rwin, + nwritten); *err = CURLE_AGAIN; nwritten = -1; goto out; @@ -2177,7 +2300,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, nwritten = http2_handle_stream_close(cf, data, stream, err); } else { - DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session")); + CURL_TRC_CF(data, cf, "send: nothing to do in this session"); *err = CURLE_HTTP2; nwritten = -1; } @@ -2185,24 +2308,24 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, out: if(stream) { - DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, " - "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " - "h2 windows %d-%d (stream-conn), " - "buffers %zu-%zu (stream-conn)", - stream->id, len, nwritten, *err, - (ssize_t)stream->upload_left, - 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))); + 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), " + "buffers %zu-%zu (stream-conn)", + stream->id, len, nwritten, *err, + (ssize_t)stream->upload_left, + 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 { - DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " - "connection-window=%d, nw_send_buffer(%zu)", - len, nwritten, *err, - nghttp2_session_get_remote_window_size(ctx->h2), - Curl_bufq_len(&ctx->outbufq))); + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " + "connection-window=%d, nw_send_buffer(%zu)", + len, nwritten, *err, + nghttp2_session_get_remote_window_size(ctx->h2), + Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); return nwritten; @@ -2273,8 +2396,12 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, if(result) goto out; + /* Send out our SETTINGS and ACKs and such. If that blocks, we + * have it buffered and can count this filter as being connected */ result = h2_progress_egress(cf, data); - if(result) + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result) goto out; *done = TRUE; @@ -2282,6 +2409,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, result = CURLE_OK; out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -2419,8 +2547,8 @@ static bool cf_h2_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); - DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending)); + CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d", + result, *input_pending); CF_DATA_RESTORE(cf, save); return result; } @@ -2471,7 +2599,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_nghttp2 = { "HTTP/2", CF_TYPE_MULTIPLEX, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, cf_h2_close, @@ -2599,7 +2727,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data, conn->httpversion = 20; /* we know we're on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf->next) { bool done; @@ -2627,7 +2755,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf_h2->next) { bool done; @@ -2684,7 +2812,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, conn->httpversion = 20; /* we know we're on HTTP/2 now */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; - multi_connchanged(data->multi); + Curl_multi_connchanged(data->multi); if(cf->next) { bool done; diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 8060162..f39d02c 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) #include "urldata.h" #include "strcase.h" @@ -44,16 +44,16 @@ #include "slist.h" -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - ret = Curl_hmacit(Curl_HMAC_SHA256, \ - (unsigned char *)k, \ - kl, \ - (unsigned char *)d, \ - dl, o); \ - if(ret) { \ - goto fail; \ - } \ +#define HMAC_SHA256(k, kl, d, dl, o) \ + do { \ + result = Curl_hmacit(Curl_HMAC_SHA256, \ + (unsigned char *)k, \ + kl, \ + (unsigned char *)d, \ + dl, o); \ + if(result) { \ + goto fail; \ + } \ } while(0) #define TIMESTAMP_SIZE 17 @@ -199,10 +199,41 @@ static CURLcode make_headers(struct Curl_easy *data, head = tmp_head; } + /* copy user headers to our header list. the logic is based on how http.c + handles user headers. + + user headers in format 'name:' with no value are used to signal that an + internal header of that name should be removed. those user headers are not + added to this list. + + user headers in format 'name;' with no value are used to signal that a + header of that name with no value should be sent. those user headers are + added to this list but in the format that they will be sent, ie the + semi-colon is changed to a colon for format 'name:'. + + user headers with a value of whitespace only, or without a colon or + semi-colon, are not added to this list. + */ for(l = data->set.headers; l; l = l->next) { - tmp_head = curl_slist_append(head, l->data); - if(!tmp_head) + char *dupdata, *ptr; + char *sep = strchr(l->data, ':'); + if(!sep) + sep = strchr(l->data, ';'); + if(!sep || (*sep == ':' && !*(sep + 1))) + continue; + for(ptr = sep + 1; ISSPACE(*ptr); ++ptr) + ; + if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ + continue; + dupdata = strdup(l->data); + if(!dupdata) goto fail; + dupdata[sep - l->data] = ':'; + tmp_head = Curl_slist_append_nodup(head, dupdata); + if(!tmp_head) { + free(dupdata); + goto fail; + } head = tmp_head; } @@ -214,23 +245,22 @@ static CURLcode make_headers(struct Curl_easy *data, if(!tmp_head) goto fail; head = tmp_head; - *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp); + *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); } else { char *value; - *date_header = strdup(*date_header); - if(!*date_header) - goto fail; - value = strchr(*date_header, ':'); - if(!value) + if(!value) { + *date_header = NULL; goto fail; + } ++value; while(ISBLANK(*value)) ++value; strncpy(timestamp, value, TIMESTAMP_SIZE - 1); timestamp[TIMESTAMP_SIZE - 1] = 0; + *date_header = NULL; } /* alpha-sort in a case sensitive manner */ @@ -370,9 +400,112 @@ fail: return ret; } +struct pair { + const char *p; + size_t len; +}; + +static int compare_func(const void *a, const void *b) +{ + const struct pair *aa = a; + const struct pair *bb = b; + return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len); +} + +#define MAX_QUERYPAIRS 64 + +static CURLcode canon_query(struct Curl_easy *data, + const char *query, struct dynbuf *dq) +{ + CURLcode result = CURLE_OK; + int entry = 0; + int i; + const char *p = query; + struct pair array[MAX_QUERYPAIRS]; + struct pair *ap = &array[0]; + if(!query) + return result; + + /* sort the name=value pairs first */ + do { + char *amp; + entry++; + ap->p = p; + amp = strchr(p, '&'); + if(amp) + ap->len = amp - p; /* excluding the ampersand */ + else { + ap->len = strlen(p); + break; + } + ap++; + p = amp + 1; + } while(entry < MAX_QUERYPAIRS); + if(entry == MAX_QUERYPAIRS) { + /* too many query pairs for us */ + failf(data, "aws-sigv4: too many query pairs in URL"); + return CURLE_URL_MALFORMAT; + } + + qsort(&array[0], entry, sizeof(struct pair), compare_func); + + ap = &array[0]; + for(i = 0; !result && (i < entry); i++, ap++) { + size_t len; + const char *q = ap->p; + 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 '~': + 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: { + /* 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; + } + } + } + } + if(i < entry - 1) { + /* insert ampersands between query pairs */ + result = Curl_dyn_addn(dq, "&", 1); + } + } + return result; +} + + CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) { - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct connectdata *conn = data->conn; size_t len; const char *arg; @@ -388,6 +521,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) char date[9]; struct dynbuf canonical_headers; struct dynbuf signed_headers; + struct dynbuf canonical_query; char *date_header = NULL; Curl_HttpReq httpreq; const char *method = NULL; @@ -416,6 +550,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* we init those buffers here, so goto fail will free initialized dynbuf */ 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); /* @@ -431,15 +566,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) /* provider1[:provider2[:region[:service]]] No string can be longer than N bytes of non-whitespace - */ + */ (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "[^:]" ":%" MAX_SIGV4_LEN_TXT "s", provider0, provider1, region, service); if(!provider0[0]) { - failf(data, "first provider can't be empty"); - ret = CURLE_BAD_FUNCTION_ARGUMENT; + failf(data, "first aws-sigv4 provider can't be empty"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto fail; } else if(!provider1[0]) @@ -448,35 +583,38 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!service[0]) { char *hostdot = strchr(hostname, '.'); if(!hostdot) { - failf(data, "service missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostdot - hostname; if(len > MAX_SIGV4_LEN) { - failf(data, "service too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: service too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } strncpy(service, hostname, len); service[len] = '\0'; + infof(data, "aws_sigv4: picked service %s from host", service); + if(!region[0]) { const char *reg = hostdot + 1; const char *hostreg = strchr(reg, '.'); if(!hostreg) { - failf(data, "region missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region missing in parameters and hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } len = hostreg - reg; if(len > MAX_SIGV4_LEN) { - failf(data, "region too long in hostname"); - ret = CURLE_URL_MALFORMAT; + failf(data, "aws-sigv4: region too long in hostname"); + result = CURLE_URL_MALFORMAT; goto fail; } strncpy(region, reg, len); region[len] = '\0'; + infof(data, "aws_sigv4: picked region %s from host", region); } } @@ -491,11 +629,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) if(!payload_hash) { if(sign_as_s3) - ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, - sha_hex, content_sha256_hdr); + result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, + sha_hex, content_sha256_hdr); else - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; payload_hash = sha_hex; @@ -514,21 +652,20 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) #else time(&clock); #endif - ret = Curl_gmtime(clock, &tm); - if(ret) { + result = Curl_gmtime(clock, &tm); + if(result) { goto fail; } if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto fail; } - ret = make_headers(data, hostname, timestamp, provider1, - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); - if(ret) + result = make_headers(data, hostname, timestamp, provider1, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers); + if(result) goto fail; - ret = CURLE_OUT_OF_MEMORY; if(*content_sha256_hdr) { /* make_headers() needed this without the \r\n for canonicalization */ @@ -540,6 +677,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) memcpy(date, timestamp, sizeof(date)); date[sizeof(date) - 1] = 0; + result = canon_query(data, data->state.up.query, &canonical_query); + if(result) + goto fail; + result = CURLE_OUT_OF_MEMORY; + canonical_request = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ @@ -549,13 +691,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "%.*s", /* HashedRequestPayload in hex */ method, data->state.up.path, - data->state.up.query ? data->state.up.query : "", + 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; + DEBUGF(infof(data, "Canonical request: %s", canonical_request)); + /* provider 0 lowercase */ Curl_strntolower(provider0, provider0, strlen(provider0)); request_type = curl_maprintf("%s4_request", provider0); @@ -612,14 +757,19 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" - "%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 ? date_header : "", content_sha256_hdr); if(!auth_headers) { goto fail; @@ -628,9 +778,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) Curl_safefree(data->state.aptr.userpwd); data->state.aptr.userpwd = auth_headers; data->state.authhost.done = TRUE; - ret = CURLE_OK; + result = CURLE_OK; fail: + Curl_dyn_free(&canonical_query); Curl_dyn_free(&canonical_headers); Curl_dyn_free(&signed_headers); free(canonical_request); @@ -639,7 +790,7 @@ fail: free(str_to_sign); free(secret); free(date_header); - return ret; + return result; } -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */ diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c index 8daad99..2db3125 100644 --- a/Utilities/cmcurl/lib/http_digest.c +++ b/Utilities/cmcurl/lib/http_digest.c @@ -24,7 +24,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) #include "urldata.h" #include "strcase.h" diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h index 7d5cfc1..5f79731 100644 --- a/Utilities/cmcurl/lib/http_digest.h +++ b/Utilities/cmcurl/lib/http_digest.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) /* this is for digest header input */ CURLcode Curl_input_digest(struct Curl_easy *data, @@ -39,6 +39,6 @@ CURLcode Curl_output_digest(struct Curl_easy *data, void Curl_http_auth_cleanup_digest(struct Curl_easy *data); -#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_DIGEST_AUTH */ #endif /* HEADER_CURL_HTTP_DIGEST_H */ diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index 4fd998a..60bbfbe 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c @@ -69,7 +69,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "connect")); + CURL_TRC_CF(data, cf, "connect"); connect_sub: result = cf->next->cft->do_connect(cf->next, data, blocking, done); if(result || !*done) @@ -86,7 +86,7 @@ connect_sub: case CURL_HTTP_VERSION_NONE: case CURL_HTTP_VERSION_1_0: case CURL_HTTP_VERSION_1_1: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/1.1")); + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); result = Curl_cf_h1_proxy_insert_after(cf, data); @@ -96,7 +96,7 @@ connect_sub: break; #ifdef USE_NGHTTP2 case CURL_HTTP_VERSION_2: - DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/2")); + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); infof(data, "CONNECT tunnel: HTTP/2 negotiated"); result = Curl_cf_h2_proxy_insert_after(cf, data); if(result) @@ -105,7 +105,7 @@ connect_sub: break; #endif default: - DEBUGF(LOG_CF(data, cf, "installing subfilter for default HTTP/1.1")); + CURL_TRC_CF(data, cf, "installing subfilter for default HTTP/1.1"); infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); result = CURLE_COULDNT_CONNECT; goto out; @@ -156,7 +156,7 @@ static void http_proxy_cf_destroy(struct Curl_cfilter *cf, struct cf_proxy_ctx *ctx = cf->ctx; (void)data; - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); free(ctx); } @@ -165,7 +165,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf, { struct cf_proxy_ctx *ctx = cf->ctx; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; if(ctx->cf_protocol) { struct Curl_cfilter *f; diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index 5f4b07e..1f31a95 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c @@ -68,27 +68,59 @@ WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, #define IDN_MAX_LENGTH 255 -bool Curl_win32_idn_to_ascii(const char *in, char **out) +static CURLcode win32_idn_to_ascii(const char *in, char **out) { - bool success = FALSE; - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + *out = NULL; if(in_w) { wchar_t punycode[IDN_MAX_LENGTH]; - int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); + int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode, + IDN_MAX_LENGTH); curlx_unicodefree(in_w); if(chars) { char *mstr = curlx_convert_wchar_to_UTF8(punycode); if(mstr) { *out = strdup(mstr); curlx_unicodefree(mstr); - if(*out) - success = TRUE; + if(!*out) + return CURLE_OUT_OF_MEMORY; } + else + return CURLE_OUT_OF_MEMORY; } + else + return CURLE_URL_MALFORMAT; } - return success; + return CURLE_OK; +} + +static CURLcode win32_ascii_to_idn(const char *in, char **output) +{ + char *out = NULL; + + wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + if(in_w) { + WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */ + int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn, + IDN_MAX_LENGTH); + if(chars) { + /* 'chars' is "the number of characters retrieved" */ + char *mstr = curlx_convert_wchar_to_UTF8(idn); + if(mstr) { + out = strdup(mstr); + curlx_unicodefree(mstr); + if(!out) + return CURLE_OUT_OF_MEMORY; + } + } + else + return CURLE_URL_MALFORMAT; + } + else + return CURLE_URL_MALFORMAT; + *output = out; + return CURLE_OK; } #endif /* USE_WIN32_IDN */ @@ -115,10 +147,15 @@ 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_OUT_OF_MEMORY - memory problem + * */ -static char *idn_decode(const char *input) +static CURLcode idn_decode(const char *input, char **output) { char *decoded = NULL; + CURLcode result = CURLE_OK; #ifdef USE_LIBIDN2 if(idn2_check_version(IDN2_VERSION)) { int flags = IDN2_NFC_INPUT @@ -135,26 +172,68 @@ static char *idn_decode(const char *input) compatibility */ rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL); if(rc != IDN2_OK) - decoded = NULL; + result = CURLE_URL_MALFORMAT; } #elif defined(USE_WIN32_IDN) - if(!Curl_win32_idn_to_ascii(input, &decoded)) - decoded = NULL; + result = win32_idn_to_ascii(input, &decoded); +#endif + if(!result) + *output = decoded; + return result; +} + +static CURLcode idn_encode(const char *puny, char **output) +{ + char *enc = NULL; +#ifdef USE_LIBIDN2 + int rc = idn2_to_unicode_8z8z(puny, &enc, 0); + if(rc != IDNA_SUCCESS) + return rc == IDNA_MALLOC_ERROR ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT; +#elif defined(USE_WIN32_IDN) + CURLcode result = win32_ascii_to_idn(puny, &enc); + if(result) + return result; #endif - return decoded; + *output = enc; + return CURLE_OK; } -char *Curl_idn_decode(const char *input) +CURLcode Curl_idn_decode(const char *input, char **output) { - char *d = idn_decode(input); + char *d = NULL; + CURLcode result = idn_decode(input, &d); #ifdef USE_LIBIDN2 - if(d) { + if(!result) { char *c = strdup(d); idn2_free(d); - d = c; + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; } #endif - return d; + if(!result) + *output = d; + return result; +} + +CURLcode Curl_idn_encode(const char *puny, char **output) +{ + char *d = NULL; + CURLcode result = idn_encode(puny, &d); +#ifdef USE_LIBIDN2 + if(!result) { + char *c = strdup(d); + idn2_free(d); + if(c) + d = c; + else + result = CURLE_OUT_OF_MEMORY; + } +#endif + if(!result) + *output = d; + return result; } /* @@ -182,8 +261,9 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) #ifdef USE_IDN /* Check name for non-ASCII and convert hostname if we can */ if(!Curl_is_ASCII_name(host->name)) { - char *decoded = idn_decode(host->name); - if(decoded) { + char *decoded; + CURLcode result = idn_decode(host->name, &decoded); + if(!result) { if(!*decoded) { /* zero length is a bad host name */ Curl_idn_free(decoded); @@ -195,7 +275,7 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host) host->name = host->encalloc; } else - return CURLE_URL_MALFORMAT; + return result; } #endif return CURLE_OK; diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h index 6c0bbb7..74bbcaf 100644 --- a/Utilities/cmcurl/lib/idn.h +++ b/Utilities/cmcurl/lib/idn.h @@ -24,15 +24,13 @@ * ***************************************************************************/ -#ifdef USE_WIN32_IDN -bool Curl_win32_idn_to_ascii(const char *in, char **out); -#endif /* USE_WIN32_IDN */ bool Curl_is_ASCII_name(const char *hostname); CURLcode Curl_idnconvert_hostname(struct hostname *host); #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) #define USE_IDN void Curl_free_idnconverted_hostname(struct hostname *host); -char *Curl_idn_decode(const char *input); +CURLcode Curl_idn_decode(const char *input, char **output); +CURLcode Curl_idn_encode(const char *input, char **output); #ifdef USE_LIBIDN2 #define Curl_idn_free(x) idn2_free(x) #else diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index 6bf0ce1..5249f6c 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c @@ -92,6 +92,8 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) } #endif +#ifndef CURL_DISABLE_BINDLOCAL + #if defined(HAVE_GETIFADDRS) if2ip_result_t Curl_if2ip(int af, @@ -254,3 +256,5 @@ if2ip_result_t Curl_if2ip(int af, } #endif + +#endif /* CURL_DISABLE_BINDLOCAL */ diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 045fe24..de64c2a 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c @@ -45,9 +45,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -1094,10 +1091,19 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, if(imapcode == '*') { /* See if this is an UIDVALIDITY response */ - char tmp[20]; - if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { - Curl_safefree(imapc->mailbox_uidvalidity); - imapc->mailbox_uidvalidity = strdup(tmp); + if(checkprefix("OK [UIDVALIDITY ", line + 2)) { + size_t len = 0; + const char *p = &line[2] + strlen("OK [UIDVALIDITY "); + while((len < 20) && p[len] && ISDIGIT(p[len])) + len++; + if(len && (p[len] == ']')) { + struct dynbuf uid; + Curl_dyn_init(&uid, 20); + if(Curl_dyn_addn(&uid, p, len)) + return CURLE_OUT_OF_MEMORY; + Curl_safefree(imapc->mailbox_uidvalidity); + imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid); + } } } else if(imapcode == IMAP_RESP_OK) { @@ -1109,7 +1115,10 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, } else { /* Note the currently opened mailbox on this connection */ + DEBUGASSERT(!imapc->mailbox); imapc->mailbox = strdup(imap->mailbox); + if(!imapc->mailbox) + return CURLE_OUT_OF_MEMORY; if(imap->custom) result = imap_perform_list(data); @@ -1806,79 +1815,37 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) */ static char *imap_atom(const char *str, bool escape_only) { - /* !checksrc! disable PARENBRACE 1 */ - const char atom_specials[] = "(){ %*]"; - const char *p1; - char *p2; - size_t backsp_count = 0; - size_t quote_count = 0; - bool others_exists = FALSE; - size_t newlen = 0; - char *newstr = NULL; + struct dynbuf line; + size_t nclean; + size_t len; if(!str) return NULL; - /* Look for "atom-specials", counting the backslash and quote characters as - these will need escaping */ - p1 = str; - while(*p1) { - if(*p1 == '\\') - backsp_count++; - else if(*p1 == '"') - quote_count++; - else if(!escape_only) { - const char *p3 = atom_specials; - - while(*p3 && !others_exists) { - if(*p1 == *p3) - others_exists = TRUE; - - p3++; - } - } - - p1++; - } - - /* Does the input contain any "atom-special" characters? */ - if(!backsp_count && !quote_count && !others_exists) + len = strlen(str); + nclean = strcspn(str, "() {%*]\\\""); + if(len == nclean) + /* nothing to escape, return a strdup */ return strdup(str); - /* Calculate the new string length */ - newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2); + Curl_dyn_init(&line, 2000); - /* Allocate the new string */ - newstr = (char *) malloc((newlen + 1) * sizeof(char)); - if(!newstr) + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) return NULL; - /* Surround the string in quotes if necessary */ - p2 = newstr; - if(!escape_only) { - newstr[0] = '"'; - newstr[newlen - 1] = '"'; - p2++; + while(*str) { + if((*str == '\\' || *str == '"') && + Curl_dyn_addn(&line, "\\", 1)) + return NULL; + if(Curl_dyn_addn(&line, str, 1)) + return NULL; + str++; } - /* Copy the string, escaping backslash and quote characters along the way */ - p1 = str; - while(*p1) { - if(*p1 == '\\' || *p1 == '"') { - *p2 = '\\'; - p2++; - } - - *p2 = *p1; - - p1++; - p2++; - } - - /* Terminate the string */ - newstr[newlen] = '\0'; + if(!escape_only && Curl_dyn_addn(&line, "\"", 1)) + return NULL; - return newstr; + return Curl_dyn_ptr(&line); } /*********************************************************************** diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c index fa90773..135f486 100644 --- a/Utilities/cmcurl/lib/inet_ntop.c +++ b/Utilities/cmcurl/lib/inet_ntop.c @@ -117,8 +117,9 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if(words[i] == 0) { - if(cur.base == -1) - cur.base = i, cur.len = 1; + if(cur.base == -1) { + cur.base = i; cur.len = 1; + } else cur.len++; } diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c index c2ba815..a1102e5 100644 --- a/Utilities/cmcurl/lib/krb5.c +++ b/Utilities/cmcurl/lib/krb5.c @@ -72,7 +72,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, char *sptr = s; CURLcode result = CURLE_OK; #ifdef HAVE_GSSAPI - enum protection_level data_sec = conn->data_prot; + unsigned char data_sec = conn->data_prot; #endif if(!cmd) @@ -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_write(data, conn->sock[FIRSTSOCKET], sptr, write_len, + result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len, &bytes_written); #ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); @@ -385,7 +385,7 @@ static const struct Curl_sec_client_mech Curl_krb5_client_mech = { }; static const struct { - enum protection_level level; + unsigned char level; const char *name; } level_names[] = { { PROT_CLEAR, "clear" }, @@ -394,8 +394,7 @@ static const struct { { PROT_PRIVATE, "private" } }; -static enum protection_level -name_to_level(const char *name) +static unsigned char name_to_level(const char *name) { int i; for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) @@ -734,7 +733,7 @@ static int sec_set_protection_level(struct Curl_easy *data) { int code; struct connectdata *conn = data->conn; - enum protection_level level = conn->request_data_prot; + unsigned char level = conn->request_data_prot; DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); @@ -793,7 +792,7 @@ static int sec_set_protection_level(struct Curl_easy *data) int Curl_sec_request_prot(struct connectdata *conn, const char *level) { - enum protection_level l = name_to_level(level); + unsigned char l = name_to_level(level); if(l == PROT_NONE) return -1; DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index 6b30ffb..33a4dea 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c @@ -239,7 +239,7 @@ static int ldap_win_bind_auth(LDAP *server, const char *user, } else #endif -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_DIGEST_AUTH) if(authflags & CURLAUTH_DIGEST) { method = LDAP_AUTH_DIGEST; } @@ -762,7 +762,7 @@ quit: /* no data to transfer */ Curl_setup_transfer(data, -1, -1, FALSE, -1); - connclose(conn, "LDAP connection always disable re-use"); + connclose(conn, "LDAP connection always disable reuse"); return result; } diff --git a/Utilities/cmcurl/lib/macos.c b/Utilities/cmcurl/lib/macos.c index 5fe4e0b..9e8e76e 100644 --- a/Utilities/cmcurl/lib/macos.c +++ b/Utilities/cmcurl/lib/macos.c @@ -24,21 +24,16 @@ #include "curl_setup.h" -#if defined(__APPLE__) - -#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX +#ifdef CURL_MACOS_CALL_COPYPROXIES #include <curl/curl.h> #include "macos.h" -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) #include <SystemConfiguration/SCDynamicStoreCopySpecific.h> -#endif CURLcode Curl_macos_init(void) { -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) { /* * The automagic conversion from IPv4 literals to IPv6 literals only @@ -46,17 +41,15 @@ CURLcode Curl_macos_init(void) * 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 a macOS and is not needed for - * IPv4-only builds, hence the conditions above. + * 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); } -#endif return CURLE_OK; } -#endif /* TARGET_OS_OSX */ - -#endif /* __APPLE__ */ +#endif diff --git a/Utilities/cmcurl/lib/macos.h b/Utilities/cmcurl/lib/macos.h index 3388acd..637860e 100644 --- a/Utilities/cmcurl/lib/macos.h +++ b/Utilities/cmcurl/lib/macos.h @@ -23,9 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + #include "curl_setup.h" -#if defined(__APPLE__) && (!defined(TARGET_OS_OSX) || TARGET_OS_OSX) +#ifdef CURL_MACOS_CALL_COPYPROXIES CURLcode Curl_macos_init(void); diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index 9ff093b..30ab62e 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c @@ -42,6 +42,7 @@ #ifdef USE_WOLFSSL #include <wolfssl/options.h> +#define VOID_MD4_INIT #ifdef NO_MD4 #define WOLFSSL_NO_MD4 #endif @@ -92,9 +93,10 @@ typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { md4_init(ctx); + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -114,9 +116,9 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #elif defined(AN_APPLE_OS) typedef CC_MD4_CTX MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { - (void)CC_MD4_Init(ctx); + return CC_MD4_Init(ctx); } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -137,15 +139,22 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->hCryptProv = 0; ctx->hHash = 0; - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash); + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return 0; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return 0; } + + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -176,10 +185,11 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->data = NULL; ctx->size = 0; + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -258,7 +268,7 @@ struct md4_ctx { }; typedef struct md4_ctx MD4_CTX; -static void MD4_Init(MD4_CTX *ctx); +static int MD4_Init(MD4_CTX *ctx); static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); static void MD4_Final(unsigned char *result, MD4_CTX *ctx); @@ -397,7 +407,7 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size) return ptr; } -static void MD4_Init(MD4_CTX *ctx) +static int MD4_Init(MD4_CTX *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -406,6 +416,7 @@ static void MD4_Init(MD4_CTX *ctx) ctx->lo = 0; ctx->hi = 0; + return 1; } static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) @@ -496,14 +507,21 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) #endif /* CRYPTO LIBS */ -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len) +CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, + const size_t len) { MD4_CTX ctx; +#ifdef VOID_MD4_INIT MD4_Init(&ctx); +#else + if(!MD4_Init(&ctx)) + return CURLE_FAILED_INIT; +#endif + MD4_Update(&ctx, input, curlx_uztoui(len)); MD4_Final(output, &ctx); + return CURLE_OK; } #endif /* USE_CURL_NTLM_CORE */ diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index 0a02cc0..01415af 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c @@ -24,7 +24,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \ + || !defined(CURL_DISABLE_DIGEST_AUTH) #include <string.h> #include <curl/curl.h> @@ -213,7 +214,8 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) { CryptReleaseContext(ctx->hCryptProv, 0); - return CURLE_OUT_OF_MEMORY; + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; } return CURLE_OK; @@ -651,4 +653,4 @@ CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) return CURLE_OK; } -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* Using NTLM (without SSPI) || Digest */ diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 0a57e1e..842b2da 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c @@ -1167,14 +1167,16 @@ static void mime_subparts_unbind(void *ptr) void Curl_mime_cleanpart(curl_mimepart *part) { - cleanup_part_content(part); - curl_slist_free_all(part->curlheaders); - if(part->flags & MIME_USERHEADERS_OWNER) - curl_slist_free_all(part->userheaders); - Curl_safefree(part->mimetype); - Curl_safefree(part->name); - Curl_safefree(part->filename); - Curl_mime_initpart(part); + if(part) { + cleanup_part_content(part); + curl_slist_free_all(part->curlheaders); + if(part->flags & MIME_USERHEADERS_OWNER) + curl_slist_free_all(part->userheaders); + Curl_safefree(part->mimetype); + Curl_safefree(part->name); + Curl_safefree(part->filename); + Curl_mime_initpart(part); + } } /* Recursively delete a mime handle and its parts. */ diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index 799a21a..30edf3d 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c @@ -117,11 +117,9 @@ static CURLcode mqtt_send(struct Curl_easy *data, char *buf, size_t len) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct MQTT *mq = data->req.p.mqtt; ssize_t n; - result = Curl_write(data, sockfd, buf, len, &n); + result = Curl_nwrite(data, FIRSTSOCKET, buf, len, &n); if(result) return result; Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 50bf15a..bb57dbc 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c @@ -459,7 +459,7 @@ struct Curl_multi *curl_multi_init(void) CURL_DNS_HASH_SIZE); } -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) { if(!multi->warned) { @@ -638,7 +638,6 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi, CONNCACHE_UNLOCK(data); multi_warn_debug(multi, data); - infof(data, "processing: %s", data->state.url); return CURLM_OK; } @@ -668,8 +667,14 @@ static CURLcode multi_done(struct Curl_easy *data, struct connectdata *conn = data->conn; unsigned int i; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d", + multi_statename[data->mstate], + (int)status, (int)premature, data->state.done)); +#else DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d", (int)status, (int)premature, data->state.done)); +#endif if(data->state.done) /* Stop if multi_done() has already been called */ @@ -752,7 +757,7 @@ static CURLcode multi_done(struct Curl_easy *data, 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 re-using, so we're forced to close it. In a perfect world + 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. */ @@ -769,7 +774,7 @@ static CURLcode multi_done(struct Curl_easy *data, #endif ) || conn->bits.close || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { - DEBUGF(infof(data, "multi_done, not re-using connection=%" + 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, @@ -1105,8 +1110,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { int bitmap; #ifdef __clang_analyzer_ /* to prevent "The left operand of '>=' is a garbage value" warnings */ @@ -1115,30 +1119,21 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, bitmap = multi_getsock(data, sockbunch); for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - FD_SET(sockbunch[i], read_fd_set); - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) { + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { if(!FDSET_SOCK(sockbunch[i])) /* pretend it doesn't exist */ continue; - FD_SET(sockbunch[i], write_fd_set); - s = sockbunch[i]; + if(bitmap & GETSOCK_READSOCK(i)) + FD_SET(sockbunch[i], read_fd_set); + if(bitmap & GETSOCK_WRITESOCK(i)) + FD_SET(sockbunch[i], write_fd_set); + if((int)sockbunch[i] > this_max_fd) + this_max_fd = (int)sockbunch[i]; } - if(s == CURL_SOCKET_BAD) - /* this socket is unused, break out of loop */ + else { break; - if((int)s > this_max_fd) - this_max_fd = (int)s; + } } - - data = data->next; /* check next handle */ } *max_fd = this_max_fd; @@ -1201,27 +1196,17 @@ static CURLMcode multi_wait(struct Curl_multi *multi, return CURLM_BAD_FUNCTION_ARGUMENT; /* Count up how many fds we have from the multi handle */ - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { + for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { ++nfds; - s = sockbunch[i]; } - if(s == CURL_SOCKET_BAD) { + else { break; } } - - data = data->next; /* check next handle */ } /* If the internally desired timeout is actually shorter than requested from @@ -1261,49 +1246,42 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(curlfds) { /* Add the curl handles to our pollfds first */ - data = multi->easyp; - while(data) { + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; + if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) { + struct pollfd *ufd = &ufds[nfds++]; #ifdef USE_WINSOCK - long mask = 0; + long mask = 0; #endif - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; + ufd->fd = sockbunch[i]; + ufd->events = 0; + if(bitmap & GETSOCK_READSOCK(i)) { #ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; + mask |= FD_READ|FD_ACCEPT|FD_CLOSE; #endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLIN; - ++nfds; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; + ufd->events |= POLLIN; + } + if(bitmap & GETSOCK_WRITESOCK(i)) { +#ifdef USE_WINSOCK + mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; + reset_socket_fdwrite(sockbunch[i]); +#endif + ufd->events |= POLLOUT; + } #ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(s); + if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) { + if(ufds_malloc) + free(ufds); + return CURLM_INTERNAL_ERROR; + } #endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLOUT; - ++nfds; } - /* s is only set if either being readable or writable is checked */ - if(s == CURL_SOCKET_BAD) { - /* break on entry not checked for being readable or writable */ + else { break; } -#ifdef USE_WINSOCK - if(WSAEventSelect(s, multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } -#endif } - - data = data->next; /* check next handle */ } } @@ -1412,8 +1390,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi, /* Count up all our own sockets that had activity, and remove them from the event. */ if(curlfds) { - data = multi->easyp; - while(data) { + + for(data = multi->easyp; data; data = data->next) { bitmap = multi_getsock(data, sockbunch); for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { @@ -1430,8 +1408,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, break; } } - - data = data->next; } } @@ -1577,6 +1553,18 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear) return retval; } +/* + * Curl_multi_connchanged() is called to tell that there is a connection in + * this multi handle that has changed state (multiplexing become possible, the + * number of allowed streams changed or similar), and a subsequent use of this + * multi handle should move CONNECT_PEND handles back to CONNECT to have them + * retry. + */ +void Curl_multi_connchanged(struct Curl_multi *multi) +{ + multi->recheckstate = TRUE; +} + CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn) @@ -1611,7 +1599,6 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) DEBUGASSERT(conn->handler); if(conn->handler->do_it) - /* generic protocol-specific function pointer set in curl_connect() */ result = conn->handler->do_it(data, done); return result; @@ -1787,9 +1774,8 @@ static CURLcode protocol_connect(struct Curl_easy *data, */ static CURLcode readrewind(struct Curl_easy *data) { - struct connectdata *conn = data->conn; curl_mimepart *mimepart = &data->set.mimepost; - DEBUGASSERT(conn); + DEBUGASSERT(data->conn); data->state.rewindbeforesend = FALSE; /* we rewind now */ @@ -1802,12 +1788,12 @@ static CURLcode readrewind(struct Curl_easy *data) /* We have sent away data. If not using CURLOPT_POSTFIELDS or CURLOPT_HTTPPOST, call app to rewind */ - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - struct HTTP *http = data->req.p.http; - - if(http->sendit) - mimepart = http->sendit; +#ifndef CURL_DISABLE_HTTP + if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) { + if(data->state.mimepost) + mimepart = data->state.mimepost; } +#endif if(data->set.postfields || (data->state.httpreq == HTTPREQ_GET) || (data->state.httpreq == HTTPREQ_HEAD)) @@ -2460,7 +2446,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race - * condition and the server closed the re-used connection exactly when + * condition and the server closed the reused connection exactly when * we wanted to use it, so figure out if that is indeed the case. */ CURLcode ret = Curl_retry_request(data, &newurl); @@ -2502,7 +2488,7 @@ 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 re-used. This is because we can't possibly + * 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 * a protocol which uses two "channels" like FTP, as then the error * happened in the data connection. @@ -2933,7 +2919,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi, /* walk over the sockets we got right now */ for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); + (curraction & GETSOCK_MASK_RW(i)); i++) { unsigned char action = CURL_POLL_NONE; unsigned char prevaction = 0; diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h index cae02cb..7f08ecc 100644 --- a/Utilities/cmcurl/lib/multiif.h +++ b/Utilities/cmcurl/lib/multiif.h @@ -41,6 +41,8 @@ void Curl_set_in_callback(struct Curl_easy *data, bool value); bool Curl_is_in_callback(struct Curl_easy *easy); CURLcode Curl_preconnect(struct Curl_easy *data); +void Curl_multi_connchanged(struct Curl_multi *multi); + /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, @@ -57,6 +59,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, /* set the bit for the given sock number to make the bitmap for readable */ #define GETSOCK_READSOCK(x) (1 << (x)) +/* mask for checking if read and/or write is set for index x */ +#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x)) + #ifdef DEBUGBUILD /* * Curl_multi_dump is not a stable public function, this is only meant to diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index f3f7cb9..136985f 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c @@ -204,8 +204,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, #ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif - result = Curl_write(data, conn->sock[FIRSTSOCKET], s, write_len, - &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written); if(result) return result; #ifdef HAVE_GSSAPI @@ -341,7 +340,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, ssize_t clipamount = 0; bool restart = FALSE; - data->req.headerbytecount += (long)gotbytes; + data->req.headerbytecount += (unsigned int)gotbytes; pp->nread_resp += gotbytes; for(i = 0; i < gotbytes; ptr++, i++) { @@ -467,11 +466,10 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, struct pingpong *pp) { /* we have a piece of a command still left to send */ - struct connectdata *conn = data->conn; ssize_t written; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - CURLcode result = Curl_write(data, sock, pp->sendthis + pp->sendsize - - pp->sendleft, pp->sendleft, &written); + CURLcode result = Curl_nwrite(data, FIRSTSOCKET, + pp->sendthis + pp->sendsize - pp->sendleft, + pp->sendleft, &written); if(result) return result; diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index ddb98bf..a9d5fdd6 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c @@ -47,9 +47,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -422,7 +419,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, return result; } -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH /*********************************************************************** * * pop3_perform_apop() @@ -566,7 +563,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data, } if(!result && progress == SASL_IDLE) { -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) /* Perform APOP authentication */ result = pop3_perform_apop(data, conn); @@ -834,7 +831,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, pop3_state(data, POP3_STOP); /* Authenticated */ break; case SASL_IDLE: /* No mechanism left after cancellation */ -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) /* Perform APOP authentication */ result = pop3_perform_apop(data, conn); @@ -855,7 +852,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data, return result; } -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH /* For APOP responses */ static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, pop3state instate) @@ -1018,7 +1015,7 @@ static CURLcode pop3_statemachine(struct Curl_easy *data, result = pop3_state_auth_resp(data, pop3code, pop3c->state); break; -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH case POP3_APOP: result = pop3_state_apop_resp(data, pop3code, pop3c->state); break; diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 7d24765..a5620ea 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c @@ -36,6 +36,7 @@ uint32_t arc4random(void); #endif #include <curl/curl.h> +#include "urldata.h" #include "vtls/vtls.h" #include "sendf.h" #include "timeval.h" @@ -187,7 +188,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, Gskit 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 diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index cbe0567..45ce3e7 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h @@ -24,20 +24,6 @@ * ***************************************************************************/ -/* - * Curl_rand() stores 'num' number of random unsigned characters in the buffer - * 'rnd' points to. - * - * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (Gskit or mbedTLS), this function will use "weak" random. - * - * When built *with* TLS support and a backend that offers strong random, it - * will return error if it cannot provide strong random values. - * - * NOTE: 'data' may be passed in as NULL when coming from external API without - * easy handle! - * - */ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num); /* diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 437fa74..d79ad08 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c @@ -139,27 +139,27 @@ static size_t convert_lineends(struct Curl_easy *data, #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ /* - * Curl_write() is an internal write function that sends data to the - * server. Works with plain sockets, SCP, SSL or kerberos. + * Curl_nwrite() is an internal write function that sends data to the + * server. Works with a socket index for the connection. * - * If the write would block (CURLE_AGAIN), we return CURLE_OK and - * (*written == 0). Otherwise we return regular CURLcode value. + * If the write would block (CURLE_AGAIN), it returns CURLE_OK and + * (*nwritten == 0). Otherwise we return regular CURLcode value. */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) +CURLcode Curl_nwrite(struct Curl_easy *data, + int sockindex, + const void *buf, + size_t blen, + ssize_t *pnwritten) { - ssize_t bytes_written; + ssize_t nwritten; CURLcode result = CURLE_OK; struct connectdata *conn; - int num; + + DEBUGASSERT(sockindex >= 0 && sockindex < 2); + DEBUGASSERT(pnwritten); DEBUGASSERT(data); DEBUGASSERT(data->conn); conn = data->conn; - num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); - #ifdef CURLDEBUG { /* Allow debug builds to override this logic to force short sends @@ -168,31 +168,47 @@ CURLcode Curl_write(struct Curl_easy *data, if(p) { size_t altsize = (size_t)strtoul(p, NULL, 10); if(altsize) - len = CURLMIN(len, altsize); + blen = CURLMIN(blen, altsize); } } #endif - bytes_written = conn->send[num](data, num, mem, len, &result); - - *written = bytes_written; - if(bytes_written >= 0) - /* we completely ignore the curlcode value when subzero is not returned */ - return CURLE_OK; + nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result); + if(result == CURLE_AGAIN) { + nwritten = 0; + result = CURLE_OK; + } + else if(result) { + nwritten = -1; /* make sure */ + } + else { + DEBUGASSERT(nwritten >= 0); + } - /* handle CURLE_AGAIN or a send failure */ - switch(result) { - case CURLE_AGAIN: - *written = 0; - return CURLE_OK; + *pnwritten = nwritten; + return result; +} - case CURLE_OK: - /* general send failure */ - return CURLE_SEND_ERROR; +/* + * Curl_write() is an internal write function that sends data to the + * server. Works with plain sockets, SCP, SSL or kerberos. + * + * If the write would block (CURLE_AGAIN), we return CURLE_OK and + * (*written == 0). Otherwise we return regular CURLcode value. + */ +CURLcode Curl_write(struct Curl_easy *data, + curl_socket_t sockfd, + const void *mem, + size_t len, + ssize_t *written) +{ + struct connectdata *conn; + int num; - default: - /* we got a specific curlcode, forward it */ - return result; - } + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + conn = data->conn; + num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]); + return Curl_nwrite(data, num, mem, len, written); } static CURLcode pausewrite(struct Curl_easy *data, @@ -421,4 +437,3 @@ CURLcode Curl_read(struct Curl_easy *data, /* transfer */ out: return result; } - diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h index d0c9275..5ef2b91 100644 --- a/Utilities/cmcurl/lib/sendf.h +++ b/Utilities/cmcurl/lib/sendf.h @@ -26,7 +26,7 @@ #include "curl_setup.h" -#include "curl_log.h" +#include "curl_trc.h" #define CLIENTWRITE_BODY (1<<0) @@ -51,4 +51,11 @@ CURLcode Curl_write(struct Curl_easy *data, const void *mem, size_t len, ssize_t *written); +/* internal write-function, using sockindex for connection destination */ +CURLcode Curl_nwrite(struct Curl_easy *data, + int sockindex, + const void *buf, + size_t blen, + ssize_t *pnwritten); + #endif /* HEADER_CURL_SENDF_H */ diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index b05162a..2cef1b3 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c @@ -666,17 +666,20 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.method = HTTPREQ_GET; break; -#ifndef CURL_DISABLE_MIME +#ifndef CURL_DISABLE_FORM_API case CURLOPT_HTTPPOST: /* - * Set to make us do HTTP POST + * Set to make us do HTTP POST. Legacy API-style. */ data->set.httppost = va_arg(param, struct curl_httppost *); data->set.method = HTTPREQ_POST_FORM; data->set.opt_no_body = FALSE; /* this is implied */ + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); break; #endif +#if !defined(CURL_DISABLE_AWS) case CURLOPT_AWS_SIGV4: /* * String that is merged to some authentication @@ -690,6 +693,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(data->set.str[STRING_AWS_SIGV4]) data->set.httpauth = CURLAUTH_AWS_SIGV4; break; +#endif case CURLOPT_REFERER: /* @@ -985,6 +989,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(!result) { data->set.method = HTTPREQ_POST_MIME; data->set.opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); +#endif } break; @@ -1237,6 +1245,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->set.out = va_arg(param, void *); break; +#ifdef CURL_LIST_ONLY_PROTOCOL case CURLOPT_DIRLISTONLY: /* * An option that changes the command to one that asks for a list only, no @@ -1244,7 +1253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; break; - +#endif case CURLOPT_APPEND: /* * We want to upload and append to an existing file. Used for FTP and @@ -1885,6 +1894,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_DEVICE], va_arg(param, char *)); break; +#ifndef CURL_DISABLE_BINDLOCAL case CURLOPT_LOCALPORT: /* * Set what local port to bind the socket to when performing an operation. @@ -1903,6 +1913,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.localportrange = curlx_sltous(arg); break; +#endif case CURLOPT_GSSAPI_DELEGATION: /* * GSS-API credential delegation bitmask diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index 7595834..53e9177 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h @@ -34,6 +34,9 @@ /* No OS/400 header file defines u_int32_t. */ typedef unsigned long u_int32_t; +/* OS/400 has no idea of a tty! */ +#define isatty(fd) 0 + /* System API wrapper prototypes & definitions to support ASCII parameters. */ @@ -57,94 +60,6 @@ extern int Curl_getnameinfo_a(const struct sockaddr *sa, int flags); #define getnameinfo Curl_getnameinfo_a - -/* GSKit wrappers. */ - -extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); -#define gsk_environment_open Curl_gsk_environment_open - -extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, - gsk_handle * my_session_handle); -#define gsk_secure_soc_open Curl_gsk_secure_soc_open - -extern int Curl_gsk_environment_close(gsk_handle * my_env_handle); -#define gsk_environment_close Curl_gsk_environment_close - -extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle); -#define gsk_secure_soc_close Curl_gsk_secure_soc_close - -extern int Curl_gsk_environment_init(gsk_handle my_env_handle); -#define gsk_environment_init Curl_gsk_environment_init - -extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle); -#define gsk_secure_soc_init Curl_gsk_secure_soc_init - -extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char *buffer, - int bufSize); -#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a - -extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE enumValue); -#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum - -extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int numValue); -#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value - -extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, - GSK_CALLBACK_ID callBackID, - void *callBackAreaPtr); -#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback - -extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char **buffer, - int *bufSize); -#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a - -extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE *enumValue); -#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum - -extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int *numValue); -#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value - -extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, - GSK_CERT_ID certID, - const gsk_cert_data_elem **certDataElem, - int *certDataElementCount); -#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info - -extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, - GSK_MISC_ID miscID); -#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc - -extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle, - char *readBuffer, - int readBufSize, int *amtRead); -#define gsk_secure_soc_read Curl_gsk_secure_soc_read - -extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle, - char *writeBuffer, - int writeBufSize, int *amtWritten); -#define gsk_secure_soc_write Curl_gsk_secure_soc_write - -extern const char * Curl_gsk_strerror_a(int gsk_return_value); -#define gsk_strerror Curl_gsk_strerror_a - -extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, - int IOCompletionPort, - Qso_OverlappedIO_t * communicationsArea); -#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit - - /* GSSAPI wrappers. */ extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h index 46657b2..645cc1a 100644 --- a/Utilities/cmcurl/lib/setup-vms.h +++ b/Utilities/cmcurl/lib/setup-vms.h @@ -262,7 +262,6 @@ static struct passwd *vms_getpwuid(uid_t uid) #define PKCS12_parse PKCS12_PARSE #define RAND_add RAND_ADD #define RAND_bytes RAND_BYTES -#define RAND_egd RAND_EGD #define RAND_file_name RAND_FILE_NAME #define RAND_load_file RAND_LOAD_FILE #define RAND_status RAND_STATUS diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index 767d879..4a02045 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c @@ -25,7 +25,8 @@ #include "curl_setup.h" -#ifndef CURL_DISABLE_CRYPTO_AUTH +#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \ + || defined(USE_LIBSSH2) #include "warnless.h" #include "curl_sha256.h" @@ -110,7 +111,10 @@ static CURLcode my_sha256_init(my_sha256_ctx *ctx) if(!ctx->openssl_ctx) return CURLE_OUT_OF_MEMORY; - EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL); + if(!EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL)) { + EVP_MD_CTX_destroy(ctx->openssl_ctx); + return CURLE_FAILED_INIT; + } return CURLE_OK; } @@ -218,9 +222,14 @@ typedef struct sha256_ctx my_sha256_ctx; static CURLcode my_sha256_init(my_sha256_ctx *ctx) { - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); + if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + return CURLE_OUT_OF_MEMORY; + + if(!CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash)) { + CryptReleaseContext(ctx->hCryptProv, 0); + ctx->hCryptProv = 0; + return CURLE_FAILED_INIT; } return CURLE_OK; @@ -533,4 +542,4 @@ const struct HMAC_params Curl_HMAC_SHA256[] = { }; -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* AWS, DIGEST, or libSSH2 */ diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index bc4e883..afcc99d 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c @@ -564,12 +564,11 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, size_t upload_size) { struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; CURLcode result; - result = Curl_write(data, sockfd, data->state.ulbuf, + result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf, len, &bytes_written); if(result) return result; @@ -587,7 +586,6 @@ static CURLcode smb_send(struct Curl_easy *data, ssize_t len, static CURLcode smb_flush(struct Curl_easy *data) { struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct smb_conn *smbc = &conn->proto.smbc; ssize_t bytes_written; ssize_t len = smbc->send_size - smbc->sent; @@ -596,9 +594,9 @@ static CURLcode smb_flush(struct Curl_easy *data) if(!smbc->send_size) return CURLE_OK; - result = Curl_write(data, sockfd, - data->state.ulbuf + smbc->sent, - len, &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, + data->state.ulbuf + smbc->sent, + len, &bytes_written); if(result) return result; diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index afcdd10..81a17e3 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c @@ -49,9 +49,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c index bd9cc53..be41914 100644 --- a/Utilities/cmcurl/lib/strerror.c +++ b/Utilities/cmcurl/lib/strerror.c @@ -938,7 +938,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) #ifndef CURL_DISABLE_VERBOSE_STRINGS if(!get_winapi_error(err, buf, buflen)) { - msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err); + msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err); } #else { diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h index 24899cb..6482643 100644 --- a/Utilities/cmcurl/lib/system_win32.h +++ b/Utilities/cmcurl/lib/system_win32.h @@ -42,7 +42,8 @@ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; /* This is used to dynamically load DLLs */ HMODULE Curl_load_library(LPCTSTR filename); - -#endif /* WIN32 */ +#else /* WIN32 */ +#define Curl_win32_init(x) CURLE_OK +#endif /* !WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 1d7a592..850f88c 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c @@ -1266,10 +1266,10 @@ static CURLcode send_telnet_data(struct Curl_easy *data, break; default: /* write! */ bytes_written = 0; - result = Curl_write(data, conn->sock[FIRSTSOCKET], - outbuf + total_written, - outlen - total_written, - &bytes_written); + result = Curl_nwrite(data, FIRSTSOCKET, + outbuf + total_written, + outlen - total_written, + &bytes_written); total_written += bytes_written; break; } diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index b678004..d0602b8 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c @@ -431,6 +431,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, curl_off_t max_recv = data->set.max_recv_speed? data->set.max_recv_speed : CURL_OFF_T_MAX; char *buf = data->state.buffer; + bool data_eof_handled = FALSE; DEBUGASSERT(buf); *done = FALSE; @@ -448,8 +449,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, to ensure that http2_handle_stream_close is called when we read all incoming bytes for a particular stream. */ bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET); - bool data_eof_handled = is_http3 - || Curl_conn_is_http2(data, conn, FIRSTSOCKET); + data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET); if(!data_eof_handled && k->size != -1 && !k->header) { /* make sure we don't read too much */ @@ -492,15 +492,16 @@ static CURLcode readwrite_data(struct Curl_easy *data, if(0 < nread || is_empty_data) { buf[nread] = 0; } - else { + if(!nread) { /* if we receive 0 or less here, either the data transfer is done or the server closed the connection and we bail out from this! */ if(data_eof_handled) DEBUGF(infof(data, "nread == 0, stream closed, bailing")); else DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); - k->keepon &= ~KEEP_RECV; - break; + k->keepon = 0; /* stop sending as well */ + if(!is_empty_data) + break; } /* Default buffer to use when we write the buffer, it may be changed @@ -761,7 +762,7 @@ static CURLcode readwrite_data(struct Curl_easy *data, } if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && - conn->bits.close) { + (conn->bits.close || data_eof_handled)) { /* 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 on from our side, we need to stop that immediately. */ @@ -824,9 +825,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data, bool sending_http_headers = FALSE; struct SingleRequest *k = &data->req; - if((k->bytecount == 0) && (k->writebytecount == 0)) - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - *didwhat |= KEEP_SEND; do { @@ -1335,7 +1333,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) } data->state.prefer_ascii = data->set.prefer_ascii; +#ifdef CURL_LIST_ONLY_PROTOCOL data->state.list_only = data->set.list_only; +#endif data->state.httpreq = data->set.method; data->state.url = data->set.str[STRING_SET_URL]; @@ -1397,7 +1397,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) Curl_pgrsResetTransferSizes(data); Curl_pgrsStartNow(data); - /* In case the handle is re-used and an authentication method was picked + /* In case the handle is reused and an authentication method was picked in the session we need to make sure we only use the one(s) we now consider to be fine */ data->state.authhost.picked &= data->state.authhost.want; @@ -1787,7 +1787,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif ) - /* We got no data, we attempted to re-use a connection. For HTTP this + /* We got no data, we attempted to reuse a connection. For HTTP this can be a retry so we try again regardless if we expected a body. For other protocols we only try again only if we expected a body. diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index e3e7f45..4f5673e 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -414,7 +414,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_hsts_cleanup(&data->hsts); curl_slist_free_all(data->set.hstslist); /* clean up list */ #endif -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif Curl_safefree(data->info.contenttype); @@ -457,6 +457,11 @@ CURLcode Curl_close(struct Curl_easy **datap) } #endif + Curl_mime_cleanpart(data->state.formp); +#ifndef CURL_DISABLE_HTTP + Curl_safefree(data->state.formp); +#endif + /* destruct wildcard structures if it is needed */ Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); @@ -490,7 +495,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->filesize = -1; /* we don't know the size */ set->postfieldsize = -1; /* unknown size */ - set->maxredirs = -1; /* allow any amount by default */ + set->maxredirs = 30; /* sensible default */ set->method = HTTPREQ_GET; /* Default HTTP request */ #ifndef CURL_DISABLE_RTSP @@ -948,7 +953,7 @@ static bool extract_if_dead(struct connectdata *conn, * that we expect - in general - no waiting input data. Input * waiting might be a TLS Notify Close, for example. We reject * that. - * For protocols where data from other other end may arrive at + * For protocols where data from other end may arrive at * any time (HTTP/2 PING for example), the protocol handler needs * to install its own `connection_check` callback. */ @@ -1073,6 +1078,9 @@ ConnectionExists(struct Curl_easy *data, bool wantProxyNTLMhttp = FALSE; #endif #endif + /* plain HTTP with upgrade */ + bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) && + (needle->handler->protocol & CURLPROTO_HTTP); *force_reuse = FALSE; *waitpipe = FALSE; @@ -1095,7 +1103,7 @@ ConnectionExists(struct Curl_easy *data, infof(data, "Server doesn't support multiplex yet, wait"); *waitpipe = TRUE; CONNCACHE_UNLOCK(data); - return FALSE; /* no re-use */ + return FALSE; /* no reuse */ } infof(data, "Server doesn't support multiplex (yet)"); @@ -1135,7 +1143,7 @@ ConnectionExists(struct Curl_easy *data, } if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { + && data->set.ipver != check->ip_version) { /* skip because the connection is not via the requested IP version */ continue; } @@ -1150,16 +1158,11 @@ ConnectionExists(struct Curl_easy *data, continue; } - if(Curl_resolver_asynch()) { - /* primary_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't re-use this connection */ - if(!check->primary_ip[0]) { - infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T " is still " - "name resolving, can't reuse", - check->connection_id); - continue; - } - } + if(Curl_resolver_asynch() && + /* primary_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_ip[0]) + continue; } if(!Curl_conn_is_connected(check, FIRSTSOCKET)) { @@ -1238,6 +1241,17 @@ ConnectionExists(struct Curl_easy *data, } #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(!canmultiplex && CONN_INUSE(check)) /* this request can't be multiplexed but the checked connection is already in use so we skip it */ @@ -1254,14 +1268,14 @@ ConnectionExists(struct Curl_easy *data, if(needle->localdev || needle->localport) { /* If we are bound to a specific local end (IP+port), we must not - re-use a random other one, although if we didn't ask for a + 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 re-uses of bound connections - will most likely also re-use the exact same binding parameters and + 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) || @@ -1294,7 +1308,7 @@ ConnectionExists(struct Curl_easy *data, (((check->httpversion >= 20) && (data->state.httpwant < CURL_HTTP_VERSION_2_0)) || ((check->httpversion >= 30) && - (data->state.httpwant < CURL_HTTP_VERSION_3)))) + (data->state.httpwant < CURL_HTTP_VERSION_3)))) continue; #ifdef USE_SSH else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) { @@ -1485,13 +1499,7 @@ void Curl_verboseconnect(struct Curl_easy *data, { if(data->set.verbose) infof(data, "Connected to %s (%s) port %u", -#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, - conn->primary_ip, conn->port); + CURL_CONN_HOST_DISPNAME(conn), conn->primary_ip, conn->port); } #endif @@ -1587,8 +1595,10 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) if(!conn->localdev) goto error; } +#ifndef CURL_DISABLE_BINDLOCAL conn->localportrange = data->set.localportrange; conn->localport = data->set.localport; +#endif /* the close socket stuff needs to be copied to the connection struct as it may live on without (this specific) Curl_easy */ @@ -2120,7 +2130,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 re-use an existing connection + * host name, 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, @@ -2441,7 +2451,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, /*********************************************************************** * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can re-use an existing + * name, proxy type and port number, so that we can reuse an existing * connection that may exist registered to the same proxy host. ***********************************************************************/ if(proxy || socksproxy) { @@ -3259,7 +3269,7 @@ static CURLcode resolve_server(struct Curl_easy *data, /* Resolve the name of the server or proxy */ if(conn->bits.reuse) { /* We're reusing the connection - no need to resolve anything, and - idnconvert_hostname() was called already in create_conn() for the re-use + idnconvert_hostname() was called already in create_conn() for the reuse case. */ *async = FALSE; return CURLE_OK; @@ -3278,7 +3288,7 @@ static void reuse_conn(struct Curl_easy *data, struct connectdata *existing) { /* get the user+password information from the temp struct since it may - * be new for this request even when we re-use an existing connection */ + * be new for this request even when we reuse an existing connection */ if(temp->user) { /* use the new user name and password though */ Curl_safefree(existing->user); @@ -3338,14 +3348,14 @@ static void reuse_conn(struct Curl_easy *data, existing->hostname_resolve = temp->hostname_resolve; temp->hostname_resolve = NULL; - /* re-use init */ - existing->bits.reuse = TRUE; /* yes, we're re-using here */ + /* reuse init */ + existing->bits.reuse = TRUE; /* yes, we're reusing here */ conn_free(data, temp); } /** - * create_conn() sets up a new connectdata struct, or re-uses an already + * create_conn() sets up a new connectdata struct, or reuses an already * existing one, and resolves host name. * * if this function returns CURLE_OK and *async is set to TRUE, the resolve @@ -3657,7 +3667,7 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * Check the current list of connections to see if we can - * re-use an already existing one or if we have to create a + * reuse an already existing one or if we have to create a * new one. *************************************************************/ @@ -3665,7 +3675,7 @@ static CURLcode create_conn(struct Curl_easy *data, DEBUGASSERT(conn->passwd); /* reuse_fresh is TRUE if we are told to use a new connection by force, but - we only acknowledge this option if this is not a re-used connection + we only acknowledge this option if this is not a reused connection already (which happens due to follow-location or during an HTTP authentication phase). CONNECT_ONLY transfers also refuse reuse. */ if((data->set.reuse_fresh && !data->state.followlocation) || diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index e0c5476..80299e7 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c @@ -1109,6 +1109,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { /* This cannot be done with strcpy, as the memory chunks overlap! */ path++; + pathlen--; } #endif @@ -1384,6 +1385,7 @@ CURLU *curl_url_dup(const CURLU *in) DUP(u, in, path); DUP(u, in, query); DUP(u, in, fragment); + DUP(u, in, zoneid); u->portnum = in->portnum; } return u; @@ -1401,6 +1403,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, bool urldecode = (flags & CURLU_URLDECODE)?1:0; bool urlencode = (flags & CURLU_URLENCODE)?1:0; bool punycode = FALSE; + bool depunyfy = FALSE; bool plusdecode = FALSE; (void)flags; if(!u) @@ -1431,6 +1434,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ptr = u->host; ifmissing = CURLUE_NO_HOST; punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; break; case CURLUPART_ZONEID: ptr = u->zoneid; @@ -1481,6 +1485,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char *port = u->port; char *allochost = NULL; punycode = (flags & CURLU_PUNYCODE)?1:0; + depunyfy = (flags & CURLU_PUNY2IDN)?1:0; if(u->scheme && strcasecompare("file", u->scheme)) { url = aprintf("file://%s%s%s", u->path, @@ -1540,9 +1545,23 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, #ifndef USE_IDN return CURLUE_LACKS_IDN; #else - allochost = Curl_idn_decode(u->host); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; + CURLcode result = Curl_idn_decode(u->host, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + CURLcode result = Curl_idn_encode(u->host, &allochost); + if(result) + /* this is the most likely error */ + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; #endif } } @@ -1616,9 +1635,26 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, #ifndef USE_IDN return CURLUE_LACKS_IDN; #else - char *allochost = Curl_idn_decode(*part); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; + char *allochost; + CURLcode result = Curl_idn_decode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + free(*part); + *part = allochost; +#endif + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) { +#ifndef USE_IDN + return CURLUE_LACKS_IDN; +#else + char *allochost; + CURLcode result = Curl_idn_encode(*part, &allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; free(*part); *part = allochost; #endif @@ -1780,6 +1816,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, char *oldurl; char *redired_url; + if(!nalloc) + /* a blank URL is not a valid URL */ + 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'), * then replace the existing with the new. */ diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index c45913b..4bfb3b4 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h @@ -101,6 +101,12 @@ typedef unsigned int curl_prot_t; #define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) #define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_POP3) +/* these protocols support CURLOPT_DIRLISTONLY */ +#define CURL_LIST_ONLY_PROTOCOL 1 +#endif + #define DEFAULT_CONNCACHE_SIZE 5 /* length of longest IPv6 address string including the trailing null */ @@ -330,6 +336,7 @@ struct Curl_ssl_session { #include "curl_sspi.h" #endif +#ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { #if defined(USE_WINDOWS_SSPI) @@ -353,6 +360,7 @@ struct digestdata { BIT(userhash); #endif }; +#endif typedef enum { NTLMSTATE_NONE, @@ -489,7 +497,7 @@ struct ConnectBits { #endif /* always modify bits.close with the connclose() and connkeep() macros! */ BIT(close); /* if set, we close the connection after this request */ - BIT(reuse); /* if set, this is a re-used connection */ + BIT(reuse); /* if set, this is a reused connection */ BIT(altused); /* this is an alt-svc "redirect" */ BIT(conn_to_host); /* if set, this connection has a "connect to host" that overrides the host in the URL */ @@ -629,17 +637,16 @@ struct SingleRequest { curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ - curl_off_t headerbytecount; /* only count received headers */ - curl_off_t deductheadercount; /* this amount of bytes doesn't 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 a - following second response code) result in a - CURLE_GOT_NOTHING error code */ - curl_off_t pendingheader; /* this many bytes left to send is actually header and not body */ struct curltime start; /* transfer started at this time */ + unsigned int headerbytecount; /* only count received headers */ + unsigned int deductheadercount; /* this amount of bytes doesn't 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 + a following second response code) result + in a CURLE_GOT_NOTHING error code */ enum { HEADER_NORMAL, /* no bad header at all */ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest @@ -701,7 +708,9 @@ struct SingleRequest { struct curltime last_sndbuf_update; /* last time readwrite_upload called win_update_buffer_size */ #endif +#ifndef CURL_DISABLE_COOKIES unsigned char setcookies; +#endif unsigned char writer_stack_depth; /* Unencoding stack depth. */ BIT(header); /* incoming data has HTTP header */ BIT(content_range); /* set TRUE if Content-Range: was found */ @@ -747,7 +756,7 @@ struct Curl_handler { * 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 - * should call the curl_connecting() function until it is. + * should call the ->connecting() function until it is. */ CURLcode (*connect_it)(struct Curl_easy *data, bool *done); @@ -887,7 +896,7 @@ struct connectdata { /* '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 - multi_done(). This entry will be NULL if the connection is re-used as then + multi_done(). This entry will be NULL if the connection is reused as then there is no name resolve done. */ struct Curl_dns_entry *dns_entry; @@ -1024,14 +1033,19 @@ struct connectdata { #ifndef CURL_DISABLE_SMB struct smb_conn smbc; #endif +#ifdef USE_LIBRTMP void *rtmp; +#endif +#ifdef USE_OPENLDAP struct ldapconninfo *ldapc; +#endif #ifndef CURL_DISABLE_MQTT struct mqtt_conn mqtt; #endif #ifdef USE_WEBSOCKETS struct websocket *ws; #endif + unsigned int unused:1; /* avoids empty union */ } proto; struct connectbundle *bundle; /* The bundle we are member of */ @@ -1045,7 +1059,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 re-using + that subsequent bound-requested connections aren't accidentally reusing wrong connections. */ char *localdev; unsigned short localportrange; @@ -1077,6 +1091,18 @@ struct connectdata { unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */ }; +#ifndef CURL_DISABLE_PROXY +#define CURL_CONN_HOST_DISPNAME(c) \ + ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ + (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname) +#else +#define CURL_CONN_HOST_DISPNAME(c) \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname +#endif + /* The end of connectdata. */ /* @@ -1089,7 +1115,6 @@ struct PureInfo { int httpversion; /* the http version number X.Y = X*10+Y */ time_t filetime; /* If requested, this is might get set. Set to -1 if the time was unretrievable. */ - curl_off_t header_size; /* size of read header(s) in bytes */ curl_off_t request_size; /* the amount of bytes sent in the request(s) */ unsigned long proxyauthavail; /* what proxy auth types were announced */ unsigned long httpauthavail; /* what host auth types were announced */ @@ -1097,6 +1122,7 @@ struct PureInfo { char *contenttype; /* the content type of the object */ char *wouldredirect; /* URL this would've 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 */ /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' and, 'conn_local_port' are copied over from the connectdata struct in @@ -1116,10 +1142,8 @@ struct PureInfo { int conn_local_port; const char *conn_scheme; unsigned int conn_protocol; - struct curl_certinfo certs; /* info about the certs, only populated in - OpenSSL, GnuTLS, Schannel, NSS and GSKit - builds. Asked for with CURLOPT_CERTINFO - / CURLINFO_CERTINFO */ + 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 thus made the document NOT get fetched */ @@ -1325,7 +1349,7 @@ struct UrlState { /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ void (*prev_signal)(int sig); #endif -#ifndef CURL_DISABLE_CRYPTO_AUTH +#ifndef CURL_DISABLE_DIGEST_AUTH struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif @@ -1383,6 +1407,9 @@ struct UrlState { struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP + curl_mimepart *mimepost; + curl_mimepart *formp; /* storage for old API form-posting, alloced on + demand */ size_t trailers_bytes_sent; struct dynbuf trailers_buf; /* a buffer containing the compiled trailing headers */ @@ -1454,9 +1481,13 @@ struct UrlState { when multi_done() is called, to prevent multi_done() to get invoked twice when the multi interface is used. */ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ +#ifndef CURL_DISABLE_COOKIES BIT(cookie_engine); +#endif BIT(prefer_ascii); /* ASCII rather than binary */ +#ifdef CURL_LIST_ONLY_PROTOCOL BIT(list_only); /* list directory contents */ +#endif BIT(url_alloc); /* URL string is malloc()'ed */ BIT(referer_alloc); /* referer string is malloc()ed */ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ @@ -1613,10 +1644,12 @@ struct UserDefined { curl_off_t postfieldsize; /* if POST, this might have a size to use instead of strlen(), and then the data *may* be binary (contain zero bytes) */ +#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 bind()ed */ +#endif curl_write_callback fwrite_func; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ @@ -1804,7 +1837,9 @@ struct UserDefined { BIT(tftp_no_options); /* do not send TFTP options requests */ #endif BIT(sep_headers); /* handle host and proxy headers separately */ +#ifndef CURL_DISABLE_COOKIES BIT(cookiesession); /* new cookie session? */ +#endif BIT(crlf); /* convert crlf on ftp upload(?) */ BIT(ssh_compression); /* enable SSH compression */ @@ -1819,7 +1854,9 @@ struct UserDefined { BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */ BIT(prefer_ascii); /* ASCII rather than binary */ BIT(remote_append); /* append, not overwrite, on upload */ +#ifdef CURL_LIST_ONLY_PROTOCOL BIT(list_only); /* list directory */ +#endif #ifndef CURL_DISABLE_FTP BIT(ftp_use_port); /* use the FTP PORT command */ BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */ @@ -1843,7 +1880,7 @@ struct UserDefined { BIT(verbose); /* output verbosity */ BIT(krb); /* Kerberos connection requested */ BIT(reuse_forbid); /* forbidden to be reused, close after use */ - BIT(reuse_fresh); /* do not re-use an existing connection */ + BIT(reuse_fresh); /* do not reuse an existing connection */ BIT(no_signal); /* do not use any signal/alarm handler */ BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ BIT(ignorecl); /* ignore content length */ diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c index 5894ed4..91fb261 100644 --- a/Utilities/cmcurl/lib/vauth/cram.c +++ b/Utilities/cmcurl/lib/vauth/cram.c @@ -26,7 +26,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #include <curl/curl.h> #include "urldata.h" @@ -94,4 +94,4 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, return CURLE_OK; } -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index fda2d91..12c6f7d 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c @@ -27,7 +27,7 @@ #include "curl_setup.h" -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #include <curl/curl.h> @@ -420,7 +420,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ - spn = Curl_auth_build_spn(service, realm, NULL); + spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); if(!spn) return CURLE_OUT_OF_MEMORY; @@ -992,4 +992,4 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } #endif /* !USE_WINDOWS_SSPI */ -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h index 68fdb28..99ce1f9 100644 --- a/Utilities/cmcurl/lib/vauth/digest.h +++ b/Utilities/cmcurl/lib/vauth/digest.h @@ -26,7 +26,7 @@ #include <curl/curl.h> -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH #define DIGEST_MAX_VALUE_LENGTH 256 #define DIGEST_MAX_CONTENT_LENGTH 1024 diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index 8fb8669..02e36ea 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c @@ -27,7 +27,7 @@ #include "curl_setup.h" -#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH) +#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) #include <curl/curl.h> @@ -665,4 +665,4 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) Curl_safefree(digest->passwd); } -#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index 93096ba..ed7cee8 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c @@ -45,12 +45,6 @@ #include "rand.h" #include "vtls/vtls.h" -/* SSL backend-specific #if branches in this file must be kept in the order - documented in curl_ntlm_core. */ -#if defined(NTLM_NEEDS_NSS_INIT) -#include "vtls/nssg.h" /* for Curl_nss_force_init() */ -#endif - #define BUILDING_CURL_NTLM_MSGS_C #include "vauth/vauth.h" #include "vauth/ntlm.h" @@ -274,12 +268,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, const unsigned char *type2 = Curl_bufref_ptr(type2ref); size_t type2len = Curl_bufref_len(type2ref); -#if defined(NTLM_NEEDS_NSS_INIT) - /* Make sure the crypto backend is initialized */ - result = Curl_nss_force_init(data); - if(result) - return result; -#elif defined(CURL_DISABLE_VERBOSE_STRINGS) +#if defined(CURL_DISABLE_VERBOSE_STRINGS) (void)data; #endif diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index d8cff24..9da0540 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h @@ -30,7 +30,7 @@ struct Curl_easy; -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#if !defined(CURL_DISABLE_DIGEST_AUTH) struct digestdata; #endif @@ -86,7 +86,7 @@ CURLcode Curl_auth_create_login_message(const char *value, CURLcode Curl_auth_create_external_message(const char *user, struct bufref *out); -#if !defined(CURL_DISABLE_CRYPTO_AUTH) +#ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, const char *userp, @@ -119,7 +119,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* This is used to clean up the digest specific data */ void Curl_auth_digest_cleanup(struct digestdata *digest); -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ #ifdef USE_GSASL /* This is used to evaluate if MECH is supported by gsasl */ diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c index 02b5334..6bd0d23 100644 --- a/Utilities/cmcurl/lib/vquic/curl_msh3.c +++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c @@ -30,7 +30,7 @@ #include "timeval.h" #include "multiif.h" #include "sendf.h" -#include "curl_log.h" +#include "curl_trc.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" @@ -173,7 +173,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, msh3_lock_initialize(&stream->recv_lock); Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup")); + CURL_TRC_CF(data, cf, "data setup"); return CURLE_OK; } @@ -183,7 +183,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "easy handle is done")); + CURL_TRC_CF(data, cf, "easy handle is done"); Curl_bufq_free(&stream->recvbuf); free(stream); H3_STREAM_LCTX(data) = NULL; @@ -235,7 +235,7 @@ static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)Connection; - DEBUGF(LOG_CF(data, cf, "[MSH3] connected")); + CURL_TRC_CF(data, cf, "[MSH3] connected"); ctx->handshake_succeeded = true; ctx->connected = true; ctx->handshake_complete = true; @@ -249,7 +249,7 @@ static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf); (void)Connection; - DEBUGF(LOG_CF(data, cf, "[MSH3] shutdown complete")); + CURL_TRC_CF(data, cf, "[MSH3] shutdown complete"); ctx->connected = false; ctx->handshake_complete = true; } @@ -474,18 +474,18 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) { failf(data, "HTTP/3 stream reset by server"); *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "cf_recv, was reset -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err); goto out; } else if(stream->error3) { failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", (ssize_t)stream->error3); *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err); goto out; } else { - DEBUGF(LOG_CF(data, cf, "cf_recv, closed ok -> %d", *err)); + CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err); } *err = CURLE_OK; nread = 0; @@ -523,7 +523,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1; } CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len)); + CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len); msh3_lock_acquire(&stream->recv_lock); @@ -538,8 +538,8 @@ static ssize_t cf_msh3_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); - DEBUGF(LOG_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", - len, nread, *err)); + CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", + len, nread, *err); if(nread < 0) goto out; if(stream->closed) @@ -550,7 +550,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } else { - DEBUGF(LOG_CF(data, cf, "req: nothing here, call again")); + CURL_TRC_CF(data, cf, "req: nothing here, call again"); *err = CURLE_AGAIN; } @@ -581,7 +581,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* Sizes must match for cast below to work" */ DEBUGASSERT(stream); - DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len)); + CURL_TRC_CF(data, cf, "req: send %zu bytes", len); if(!stream->req) { /* The first send on the request contains the headers and possibly some @@ -630,7 +630,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, break; } - DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader)); + CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, nva, nheader, eos ? MSH3_REQUEST_FLAG_FIN : @@ -646,7 +646,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { /* request is open */ - DEBUGF(LOG_CF(data, cf, "req: send %zu body bytes", len)); + CURL_TRC_CF(data, cf, "req: send %zu body bytes", len); if(len > 0xFFFFFFFF) { len = 0xFFFFFFFF; } @@ -694,7 +694,7 @@ static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, drain_stream(cf, data); } } - DEBUGF(LOG_CF(data, cf, "select_sock -> %d", bitmap)); + CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap); CF_DATA_RESTORE(cf, save); return bitmap; } @@ -711,8 +711,8 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf, (void)cf; if(stream && stream->req) { msh3_lock_acquire(&stream->recv_lock); - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu", - Curl_bufq_len(&stream->recvbuf))); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu", + Curl_bufq_len(&stream->recvbuf)); pending = !Curl_bufq_is_empty(&stream->recvbuf); msh3_lock_release(&stream->recv_lock); if(pending) @@ -774,7 +774,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, h3_data_done(cf, data); break; case CF_CTRL_DATA_DONE_SEND: - DEBUGF(LOG_CF(data, cf, "req: send done")); + CURL_TRC_CF(data, cf, "req: send done"); if(stream) { stream->upload_done = TRUE; if(stream->req) { @@ -787,7 +787,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, } break; case CF_CTRL_CONN_INFO_UPDATE: - DEBUGF(LOG_CF(data, cf, "req: update info")); + CURL_TRC_CF(data, cf, "req: update info"); cf_msh3_active(cf, data); break; default: @@ -813,17 +813,17 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, /* TODO: need a way to provide trust anchors to MSH3 */ #ifdef DEBUGBUILD /* we need this for our test cases to run */ - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "switching off verifypeer in DEBUG mode")); + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "switching off verifypeer in DEBUG mode"); verify = 0; #else - DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, " - "attempting with built-in verification")); + CURL_TRC_CF(data, cf, "non-standard CA not supported, " + "attempting with built-in verification"); #endif } - DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)", - cf->conn->host.name, (int)cf->conn->remote_port, verify)); + CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)", + cf->conn->host.name, (int)cf->conn->remote_port, verify); ctx->api = MsH3ApiOpen(); if(!ctx->api) { @@ -888,7 +888,7 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, if(ctx->handshake_complete) { ctx->handshake_at = Curl_now(); if(ctx->handshake_succeeded) { - DEBUGF(LOG_CF(data, cf, "handshake succeeded")); + 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; @@ -918,7 +918,7 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data); if(ctx) { - DEBUGF(LOG_CF(data, cf, "destroying")); + CURL_TRC_CF(data, cf, "destroying"); if(ctx->qconn) { MsH3ConnectionClose(ctx->qconn); ctx->qconn = NULL; @@ -935,13 +935,13 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) */ ctx->active = FALSE; if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { - DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active", - (int)ctx->sock[SP_LOCAL])); + CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active", + (int)ctx->sock[SP_LOCAL]); cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; } else { - DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at " - "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL])); + CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at " + "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; } if(cf->sockindex == FIRSTSOCKET) diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index a430aa1..03e911d 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c @@ -58,6 +58,7 @@ #include "dynbuf.h" #include "http1.h" #include "select.h" +#include "inet_pton.h" #include "vquic.h" #include "vquic_int.h" #include "vtls/keylog.h" @@ -162,6 +163,9 @@ struct cf_ngtcp2_ctx { size_t max_stream_window; /* max flow window for one stream */ int qlogfd; BIT(got_first_byte); /* if first byte was received */ +#ifdef USE_OPENSSL + BIT(x509_store_setup); /* if x509 store has been set up */ +#endif }; /* How to access `call_data` from a cf_ngtcp2 filter */ @@ -176,6 +180,7 @@ struct h3_stream_ctx { int64_t id; /* HTTP/3 protocol identifier */ struct bufq sendbuf; /* h3 request body */ 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 */ @@ -223,9 +228,9 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); stream->recv_buf_nonflow = 0; + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); H3_STREAM_LCTX(data) = stream; - DEBUGF(LOG_CF(data, cf, "data setup")); return CURLE_OK; } @@ -235,10 +240,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); Curl_bufq_free(&stream->sendbuf); Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); free(stream); H3_STREAM_LCTX(data) = NULL; } @@ -392,6 +397,7 @@ static int init_ngh3_conn(struct Curl_cfilter *cf); static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; struct connectdata *conn = cf->conn; CURLcode result = CURLE_FAILED_INIT; SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); @@ -440,10 +446,6 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); } - result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); - if(result) - goto out; - /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with @@ -453,6 +455,15 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + /* When a user callback is installed to modify the SSL_CTX, + * we need to do the full initialization before calling it. + * See: #11800 */ + if(!ctx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); + if(result) + goto out; + ctx->x509_store_setup = TRUE; + } Curl_set_in_callback(data, true); result = (*data->set.ssl.fsslctx)(data, ssl_ctx, data->set.ssl.fsslctxp); @@ -501,8 +512,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; const uint8_t *alpn = NULL; size_t alpnlen = 0; + unsigned char checkip[16]; - (void)data; DEBUGASSERT(!ctx->ssl); ctx->ssl = SSL_new(ctx->sslctx); @@ -516,7 +527,19 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); /* set SNI */ - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) +#ifdef ENABLE_IPV6 + && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) +#endif + ) { + char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); + if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + failf(data, "Failed set SNI"); + SSL_free(ctx->ssl); + ctx->ssl = NULL; + return CURLE_QUIC_CONNECT_ERROR; + } + } return CURLE_OK; } #elif defined(USE_GNUTLS) @@ -544,15 +567,15 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref); if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { - DEBUGF(LOG_CF(data, cf, - "ngtcp2_crypto_gnutls_configure_client_session failed\n")); + CURL_TRC_CF(data, cf, + "ngtcp2_crypto_gnutls_configure_client_session failed\n"); return CURLE_QUIC_CONNECT_ERROR; } rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); if(rc < 0) { - DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc))); + CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n", + gnutls_strerror(rc)); return CURLE_QUIC_CONNECT_ERROR; } @@ -596,7 +619,7 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { char error_buffer[256]; ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); + failf(data, "wolfSSL_CTX_set_cipher_list: %s", error_buffer); goto out; } @@ -728,8 +751,8 @@ static void report_consumed_data(struct Curl_cfilter *cf, } } if(consumed > 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes", - stream->id, consumed)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA", + stream->id, consumed); ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, consumed); ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); @@ -751,8 +774,8 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, nconsumed = nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd", - stream_id, buflen, nconsumed)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", + stream_id, buflen, nconsumed); if(nconsumed < 0) { ngtcp2_ccerr_set_application_error( &ctx->last_error, @@ -810,8 +833,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%" - PRIu64 ") -> %d", stream3_id, app_error_code, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" + PRIu64 ") -> %d", stream3_id, app_error_code, rv); if(rv) { ngtcp2_ccerr_set_application_error( &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); @@ -835,7 +858,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, (void)data; rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1064,8 +1087,6 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) rv |= GETSOCK_WRITESOCK(0); - /* DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)", - rv, (int)socks[0])); */ CF_DATA_RESTORE(cf, save); return rv; } @@ -1095,21 +1116,23 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, struct h3_stream_ctx *stream = H3_STREAM_CTX(data); (void)conn; (void)stream_id; - (void)app_error_code; - (void)cf; /* we might be called by nghttp3 after we already cleaned up */ if(!stream) return 0; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")", - stream_id, app_error_code)); stream->closed = TRUE; stream->error3 = app_error_code; - if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) { + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, + stream->id, stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); } + data->req.keepon &= ~KEEP_SEND_HOLD; h3_drain_stream(cf, data); return 0; } @@ -1133,9 +1156,6 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; } nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) " - "-> %zd, %d", stream->id, memlen, nwritten, result)); - */ if(nwritten < 0) { return result; } @@ -1158,14 +1178,24 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, { struct Curl_cfilter *cf = user_data; struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); CURLcode result; (void)conn; (void)stream3_id; + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + result = write_resp_raw(cf, data, buf, buflen, TRUE); + if(result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", + stream->id, buflen, result); + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen); h3_drain_stream(cf, data); - return result? -1 : 0; + return 0; } static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, @@ -1204,8 +1234,8 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, return -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d", - stream_id, stream->status_code)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; } @@ -1244,8 +1274,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, return -1; ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", stream->status_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s", - stream_id, line)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); result = write_resp_raw(cf, data, line, ncopy, FALSE); if(result) { return -1; @@ -1253,9 +1282,9 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, } else { /* store as an HTTP1-style header */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base); result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); if(result) { return -1; @@ -1307,7 +1336,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, app_error_code); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1404,35 +1433,17 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) { failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); - *err = CURLE_PARTIAL_FILE; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); + *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } - else if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - failf(data, - "HTTP/3 stream %" PRId64 " was not closed cleanly: " - "(err %"PRId64")", stream->id, stream->error3); - *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly" - " -> %d", stream->id, *err)); - goto out; - } - - if(!stream->resp_hds_complete) { + else if(!stream->resp_hds_complete) { failf(data, "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" " all response header fields, treated as error", stream->id); *err = CURLE_HTTP3; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); goto out; } - else { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); - } *err = CURLE_OK; nread = 0; @@ -1469,10 +1480,11 @@ static ssize_t cf_ngtcp2_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); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); goto out; + } report_consumed_data(cf, data, nread); } @@ -1486,10 +1498,11 @@ static ssize_t cf_ngtcp2_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); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); - if(nread < 0) + if(nread < 0) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); goto out; + } report_consumed_data(cf, data, nread); } @@ -1517,8 +1530,8 @@ out: nread = -1; } } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1555,8 +1568,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, (data->req.keepon & KEEP_SEND)) { data->req.keepon &= ~KEEP_SEND_HOLD; h3_drain_stream(cf, data); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks", - stream_id)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id); } } return 0; @@ -1613,18 +1625,18 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> AGAIN", - stream->id)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" - CURL_FORMAT_CURL_OFF_T ")", - stream->id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%d vecs%s with %zu (buffered=%zu, left=%" + CURL_FORMAT_CURL_OFF_T ")", + stream->id, (int)nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); return (nghttp3_ssize)nvecs; } @@ -1639,7 +1651,6 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = NULL; - struct h1_req_parser h1; struct dynhds h2_headers; size_t nheader; nghttp3_nv *nva = NULL; @@ -1649,7 +1660,6 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nghttp3_data_reader reader; nghttp3_data_reader *preader = NULL; - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); *err = h3_data_setup(cf, data); @@ -1658,24 +1668,22 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, stream = H3_STREAM_CTX(data); DEBUGASSERT(stream); - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); - if(rc) { - failf(data, "can get bidi streams"); - *err = CURLE_SEND_ERROR; - goto out; - } - - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(nghttp3_nv) * nheader); @@ -1694,6 +1702,13 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nva[i].flags = NGHTTP3_NV_FLAG_NONE; } + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); + if(rc) { + failf(data, "can get bidi streams"); + *err = CURLE_SEND_ERROR; + goto out; + } + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -1723,12 +1738,12 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, " - "connection is closing", stream->id)); + CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " + "connection is closing", stream->id); break; default: - DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", - stream->id, rc, ngtcp2_strerror(rc))); + CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", + stream->id, rc, ngtcp2_strerror(rc)); break; } *err = CURLE_SEND_ERROR; @@ -1736,13 +1751,18 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, goto out; } - infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream->id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream->id, data->state.url)); + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } out: free(nva); - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -1773,12 +1793,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!stream || stream->id < 0) { sent = h3_stream_open(cf, data, buf, len, err); if(sent < 0) { - DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err)); + CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); goto out; } + stream = H3_STREAM_CTX(data); } else if(stream->upload_blocked_len) { - /* the data in `buf` has alread been submitted or added to the + /* 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) { @@ -1793,15 +1814,27 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 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 + * 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, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + sent = (ssize_t)len; + goto out; + } *err = CURLE_HTTP3; sent = -1; goto out; } else { sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %zd, %d", - stream->id, len, sent, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %zd, %d", + stream->id, len, sent, *err); if(sent < 0) { goto out; } @@ -1821,9 +1854,9 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * "written" into our various internal connection buffers. * We put the stream upload on HOLD, until this gets ACKed. */ stream->upload_blocked_len = sent; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu), " - "%zu bytes in flight -> EGAIN", stream->id, len, - stream->sendbuf_len_in_flight)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " + "%zu bytes in flight -> EGAIN", stream->id, len, + stream->sendbuf_len_in_flight); *err = CURLE_AGAIN; sent = -1; data->req.keepon |= KEEP_SEND_HOLD; @@ -1835,8 +1868,8 @@ out: *err = result; sent = -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, sent, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, sent, *err); CF_DATA_RESTORE(cf, save); return sent; } @@ -1909,12 +1942,12 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, remote_addrlen); - pi.ecn = (uint32_t)ecn; + pi.ecn = (uint8_t)ecn; rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); if(rv) { - DEBUGF(LOG_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", - ngtcp2_strerror(rv))); + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s", + ngtcp2_strerror(rv)); if(!ctx->last_error.error_code) { if(rv == NGTCP2_ERR_CRYPTO) { ngtcp2_ccerr_set_tls_alert(&ctx->last_error, @@ -1954,6 +1987,15 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, pktx->ts = timestamp(); } +#ifdef USE_OPENSSL + if(!ctx->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx); + if(result) + return result; + ctx->x509_store_setup = TRUE; + } +#endif + for(i = 0; i < pkts_max; i += pkts_chunk) { pktx->pkt_count = 0; result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, @@ -2129,8 +2171,6 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* add the next packet to send, if any, to our buffer */ nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, read_pkt_to_send, pktx, &curlcode); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - max_payload_size, nread, curlcode)); */ if(nread < 0) { if(curlcode != CURLE_AGAIN) return curlcode; @@ -2246,9 +2286,16 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, } break; } - case CF_CTRL_DATA_IDLE: - result = check_and_set_expiry(cf, data, NULL); + case CF_CTRL_DATA_IDLE: { + struct h3_stream_ctx *stream = H3_STREAM_CTX(data); + CURL_TRC_CF(data, cf, "data idle"); + if(stream && !stream->closed) { + result = check_and_set_expiry(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result); + } break; + } default: break; } @@ -2305,7 +2352,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) ngtcp2_tstamp ts; ngtcp2_ssize rc; - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); ts = timestamp(); rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ NULL, /* pkt_info */ @@ -2329,7 +2376,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_call_data save; CF_DATA_SAVE(save, cf, data); - DEBUGF(LOG_CF(data, cf, "destroy")); + CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_ngtcp2_ctx_clear(ctx); free(ctx); @@ -2350,7 +2397,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, int rc; int rv; CURLcode result; - const struct Curl_sockaddr_ex *sockaddr; + const struct Curl_sockaddr_ex *sockaddr = NULL; int qfd; ctx->version = NGTCP2_PROTO_VER_MAX; @@ -2396,6 +2443,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL, NULL, NULL, NULL); + if(!sockaddr) + return CURLE_QUIC_CONNECT_ERROR; ctx->q.local_addrlen = sizeof(ctx->q.local_addr); rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, &ctx->q.local_addrlen); @@ -2460,7 +2509,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; } @@ -2484,11 +2533,11 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { ctx->handshake_at = now; - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); result = qng_verify_peer(cf, data); if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); + CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -2510,8 +2559,8 @@ out: */ int reconn_delay_ms = 200; - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); + CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms); Curl_conn_cf_close(cf->next, data); cf_ngtcp2_ctx_clear(ctx); result = Curl_conn_cf_connect(cf->next, data, FALSE, done); @@ -2526,8 +2575,8 @@ out: #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); @@ -2538,7 +2587,8 @@ out: if(!result && ctx->qconn) { result = check_and_set_expiry(cf, data, &pktx); } - DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done)); + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -2562,7 +2612,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, INT_MAX : (int)rp->initial_max_streams_bidi; else /* not arrived yet? */ *pres1 = Curl_multi_max_concurrent_streams(data->multi); - DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1)); + CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); CF_DATA_RESTORE(cf, save); return CURLE_OK; } diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index 39cc16e..3598de1 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c @@ -45,8 +45,10 @@ #include "vquic_int.h" #include "curl_quiche.h" #include "transfer.h" +#include "inet_pton.h" #include "vtls/openssl.h" #include "vtls/keylog.h" +#include "vtls/vtls.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -56,7 +58,7 @@ /* #define DEBUG_QUICHE */ #define QUIC_MAX_STREAMS (100) -#define QUIC_IDLE_TIMEOUT (5 * 1000) /* milliseconds */ +#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) @@ -88,54 +90,6 @@ static void keylog_callback(const SSL *ssl, const char *line) Curl_tls_keylog_write_line(line); } -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - SSL_CTX_set_alpn_protos(ssl_ctx, - (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - - SSL_CTX_set_default_verify_paths(ssl_ctx); - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - { - struct connectdata *conn = data->conn; - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - } - return ssl_ctx; -} - struct cf_quiche_ctx { struct cf_quic_ctx q; quiche_conn *qconn; @@ -154,6 +108,7 @@ struct cf_quiche_ctx { size_t sends_on_hold; /* # of streams with SEND_HOLD set */ BIT(goaway); /* got GOAWAY from server */ BIT(got_first_byte); /* if first byte was received */ + BIT(x509_store_setup); /* if x509 store has been set up */ }; #ifdef DEBUG_QUICHE @@ -181,12 +136,96 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) } } +static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + + if(!ctx->x509_store_setup) { + if(cf->conn->ssl_config.verifypeer) { + const char * const ssl_cafile = cf->conn->ssl_config.CAfile; + const char * const ssl_capath = cf->conn->ssl_config.CApath; + if(ssl_cafile || ssl_capath) { + SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL); + /* tell OpenSSL where to find CA certificates that are used to verify + the server's certificate. */ + if(!SSL_CTX_load_verify_locations( + ctx->sslctx, ssl_cafile, ssl_capath)) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } +#ifdef CURL_CA_FALLBACK + else { + /* verifying the peer without any CA certificates won't work so + use openssl's built-in default as fallback */ + SSL_CTX_set_default_verify_paths(ssl_ctx); + } +#endif + } + ctx->x509_store_setup = TRUE; + } + return CURLE_OK; +} + +static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + unsigned char checkip[16]; + + DEBUGASSERT(!ctx->sslctx); + ctx->sslctx = SSL_CTX_new(TLS_method()); + if(!ctx->sslctx) + return CURLE_OUT_OF_MEMORY; + + SSL_CTX_set_alpn_protos(ctx->sslctx, + (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, + sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); + + SSL_CTX_set_default_verify_paths(ctx->sslctx); + + /* Open the file if a TLS or QUIC backend has not done this before. */ + Curl_tls_keylog_open(); + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback); + } + + ctx->ssl = SSL_new(ctx->sslctx); + if(!ctx->ssl) + return CURLE_QUIC_CONNECT_ERROR; + + SSL_set_app_data(ctx->ssl, cf); + + if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) +#ifdef ENABLE_IPV6 + && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) +#endif + ) { + char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); + if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { + failf(data, "Failed set SNI"); + SSL_free(ctx->ssl); + ctx->ssl = NULL; + return CURLE_QUIC_CONNECT_ERROR; + } + } + + return CURLE_OK; +} + /** * All about the H3 internals of a stream */ struct stream_ctx { int64_t id; /* HTTP/3 protocol stream identifier */ struct bufq recvbuf; /* h3 response */ + struct h1_req_parser h1; /* h1 request parsing */ uint64_t error3; /* HTTP/3 stream error code */ curl_off_t upload_left; /* number of request bytes left to upload */ bool closed; /* TRUE on stream close */ @@ -217,11 +256,10 @@ static void stream_send_suspend(struct Curl_cfilter *cf, data->req.keepon |= KEEP_SEND_HOLD; ++ctx->sends_on_hold; if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] suspend sending", - H3_STREAM_ID(data))); + CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending", + H3_STREAM_ID(data)); else - DEBUGF(LOG_CF(data, cf, "[%s] suspend sending", - data->state.url)); + CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url); } } @@ -234,11 +272,10 @@ static void stream_send_resume(struct Curl_cfilter *cf, data->req.keepon &= ~KEEP_SEND_HOLD; --ctx->sends_on_hold; if(H3_STREAM_ID(data) >= 0) - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] resume sending", - H3_STREAM_ID(data))); + CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending", + H3_STREAM_ID(data)); else - DEBUGF(LOG_CF(data, cf, "[%s] resume sending", - data->state.url)); + CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url); Curl_expire(data, 0, EXPIRE_RUN_NOW); } } @@ -277,7 +314,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->id = -1; Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); - DEBUGF(LOG_CF(data, cf, "data setup")); + Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); return CURLE_OK; } @@ -288,13 +325,13 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf; if(stream) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done", - stream->id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); if(stream_send_is_suspended(data)) { data->req.keepon &= ~KEEP_SEND_HOLD; --ctx->sends_on_hold; } Curl_bufq_free(&stream->recvbuf); + Curl_h1_req_parse_free(&stream->h1); free(stream); H3_STREAM_LCTX(data) = NULL; } @@ -379,8 +416,12 @@ static int cb_each_header(uint8_t *name, size_t name_len, struct stream_ctx *stream = H3_STREAM_CTX(x->data); CURLcode result; - (void)stream; + if(!stream) + return CURLE_OK; + if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s", + stream->id, (int)value_len, value); result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); if(!result) result = write_resp_raw(x->cf, x->data, value, value_len); @@ -388,6 +429,9 @@ 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, "[%" PRId64 "] header: %.*s: %.*s", + stream->id, (int)name_len, name, + (int)value_len, value); result = write_resp_raw(x->cf, x->data, name, name_len); if(!result) result = write_resp_raw(x->cf, x->data, ": ", 2); @@ -397,10 +441,8 @@ 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) { - DEBUGF(LOG_CF(x->data, x->cf, - "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d", - stream? stream->id : -1, (int)name_len, name, - (int)value_len, value, result)); + CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d", + stream->id, result); } return result; } @@ -455,8 +497,8 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result); if(nwritten < 0 && result != CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd", - stream->id, nwritten)); + CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd", + stream->id, nwritten); failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]", result, stream->id); stream->closed = TRUE; @@ -514,7 +556,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, rc, stream3_id); return CURLE_RECV_ERROR; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id); break; case QUICHE_H3_EVENT_DATA: @@ -524,7 +566,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_RESET: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -532,7 +574,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break; case QUICHE_H3_EVENT_FINISHED: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id); if(!stream->resp_hds_complete) { result = write_resp_raw(cf, data, "\r\n", 2); if(result) @@ -541,15 +583,16 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, } stream->closed = TRUE; streamclose(cf->conn, "End of stream"); + data->req.keepon &= ~KEEP_SEND_HOLD; break; case QUICHE_H3_EVENT_GOAWAY: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id); break; default: - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d", - stream3_id, quiche_h3_event_type(ev))); + CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d", + stream3_id, quiche_h3_event_type(ev)); break; } return result; @@ -571,26 +614,25 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, break; } else if(stream3_id < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error poll: %"PRId64, - stream? stream->id : -1, stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64, + stream? stream->id : -1, stream3_id); return CURLE_HTTP3; } sdata = get_stream_easy(cf, data, stream3_id); if(!sdata) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] discard event %s for " - "unknown [h3sid=%"PRId64"]", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id)); + CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for " + "unknown [%"PRId64"]", + stream? stream->id : -1, cf_ev_name(ev), stream3_id); } else { result = h3_process_event(cf, sdata, stream3_id, ev); drain_stream(cf, sdata); if(result) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error processing event %s " - "for [h3sid=%"PRId64"] -> %d", - stream? stream->id : -1, cf_ev_name(ev), - stream3_id, result)); + CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s " + "for [%"PRId64"] -> %d", + stream? stream->id : -1, cf_ev_name(ev), + stream3_id, result); if(data == sdata) { /* Only report this error to the caller if it is about the * transfer we were called with. Otherwise we fail a transfer @@ -633,7 +675,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, &recv_info); if(nread < 0) { if(QUICHE_ERR_DONE == nread) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche is DONE")); + CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); return CURLE_OK; } else if(QUICHE_ERR_TLS_FAIL == nread) { @@ -650,8 +692,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, } } else if((size_t)nread < pktlen) { - DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", - nread, pktlen)); + CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", + nread, pktlen); } return CURLE_OK; @@ -665,6 +707,10 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, CURLcode result; DEBUGASSERT(ctx->qconn); + result = quic_x509_store_setup(cf, data); + if(result) + return result; + rctx.cf = cf; rctx.data = data; rctx.pkts = 0; @@ -720,10 +766,20 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; ssize_t nread; CURLcode result; + int64_t expiry_ns; int64_t timeout_ns; struct read_ctx readx; size_t pkt_count, gsolen; + expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn); + if(!expiry_ns) { + quiche_conn_on_timeout(ctx->qconn); + if(quiche_conn_is_closed(ctx->qconn)) { + failf(data, "quiche_conn_on_timeout closed the connection"); + return CURLE_SEND_ERROR; + } + } + result = vquic_flush(cf, data, &ctx->q); if(result) { if(result == CURLE_AGAIN) { @@ -742,9 +798,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, /* add the next packet to send, if any, to our buffer */ nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, read_pkt_to_send, &readx, &result); - /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d", - (size_t)0, nread, result)); */ - if(nread < 0) { if(result != CURLE_AGAIN) return result; @@ -795,8 +848,8 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->id); *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d", - stream->id, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d", + stream->id, *err); } else if(!stream->resp_got_header) { failf(data, @@ -805,14 +858,12 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, stream->id); /* *err = CURLE_PARTIAL_FILE; */ *err = CURLE_RECV_ERROR; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete" + " -> %d", stream->id, *err); } else { *err = CURLE_OK; nread = 0; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok" - " -> %d", stream->id, *err)); } return nread; } @@ -833,14 +884,14 @@ 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); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; } if(cf_process_ingress(cf, data)) { - DEBUGF(LOG_CF(data, cf, "cf_recv, error on ingress")); + CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); *err = CURLE_RECV_ERROR; nread = -1; goto out; @@ -850,8 +901,8 @@ 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); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " + "-> %zd, %d", stream->id, len, nread, *err); if(nread < 0) goto out; } @@ -878,16 +929,15 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: result = cf_flush_egress(cf, data); if(result) { - DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed")); + CURL_TRC_CF(data, cf, "cf_recv, flush egress failed"); *err = result; nread = -1; } if(nread > 0) ctx->data_recvd += nread; - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%" - CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", - stream ? stream->id : (int64_t)0, - ctx->data_recvd, nread, *err)); + CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%" + CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", + stream->id, ctx->data_recvd, nread, *err); return nread; } @@ -904,7 +954,6 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, struct stream_ctx *stream = H3_STREAM_CTX(data); size_t nheader, i; int64_t stream3_id; - struct h1_req_parser h1; struct dynhds h2_headers; quiche_h3_header *nva = NULL; ssize_t nwritten; @@ -918,21 +967,25 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); } - Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); DEBUGASSERT(stream); - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); if(nwritten < 0) goto out; - DEBUGASSERT(h1.done); - DEBUGASSERT(h1.req); + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); + *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); if(*err) { nwritten = -1; goto out; } + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(quiche_h3_header) * nheader); @@ -975,16 +1028,16 @@ 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. */ - DEBUGF(LOG_CF(data, cf, "send_request(%s) rejected with BLOCKED", - data->state.url)); + CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED", + data->state.url); stream_send_suspend(cf, data); *err = CURLE_AGAIN; nwritten = -1; goto out; } else { - DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64, - data->state.url, stream3_id)); + CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, + data->state.url, stream3_id); } *err = CURLE_SEND_ERROR; nwritten = -1; @@ -997,13 +1050,18 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, stream->closed = FALSE; stream->reset = FALSE; - infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream3_id); - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s", - stream3_id, data->state.url)); + if(Curl_trc_is_verbose(data)) { + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, data->state.url); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].name_len, nva[i].name, + (int)nva[i].value_len, nva[i].value); + } + } out: free(nva); - Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); return nwritten; } @@ -1037,24 +1095,40 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* 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)) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> window exhausted", stream->id, len)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> window exhausted", stream->id, len); stream_send_suspend(cf, data); } *err = CURLE_AGAIN; nwritten = -1; goto out; } + else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE && + stream->closed && stream->resp_hds_complete) { + /* sending request body on a stream that has been closed by the + * server. If the server has send us a final response, we should + * silently discard the send data. + * This happens for example on redirects where the server, instead + * of reading the full request body just closed the stream after + * 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, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + *err = CURLE_OK; + nwritten = (ssize_t)len; + goto out; + } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> exceeds size", stream->id, len); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; } else if(nwritten < 0) { - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> quiche err %zd", stream->id, len, nwritten); *err = CURLE_SEND_ERROR; nwritten = -1; goto out; @@ -1068,9 +1142,9 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream->upload_left == 0) stream->send_closed = TRUE; - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, " - "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", - stream->id, len, stream->upload_left, nwritten)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, " + "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", + stream->id, len, stream->upload_left, nwritten); *err = CURLE_OK; } } @@ -1081,8 +1155,8 @@ out: *err = result; nwritten = -1; } - DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream? stream->id : -1, len, nwritten, *err)); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", + stream? stream->id : -1, len, nwritten, *err); return nwritten; } @@ -1171,16 +1245,20 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, stream->upload_left = 0; body[0] = 'X'; sent = cf_quiche_send(cf, data, body, 0, &result); - DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d", - stream->id, sent, result)); + CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d", + stream->id, sent, result); } break; } - case CF_CTRL_DATA_IDLE: - result = cf_flush_egress(cf, data); - if(result) - DEBUGF(LOG_CF(data, cf, "data idle, flush egress -> %d", result)); + case CF_CTRL_DATA_IDLE: { + struct stream_ctx *stream = H3_STREAM_CTX(data); + if(stream && !stream->closed) { + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result); + } break; + } default: break; } @@ -1210,7 +1288,7 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf, goto out; } else - DEBUGF(LOG_CF(data, cf, "Skipped certificate verification")); + CURL_TRC_CF(data, cf, "Skipped certificate verification"); ctx->h3config = quiche_h3_config_new(); if(!ctx->h3config) { @@ -1298,15 +1376,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->ssl); DEBUGASSERT(!ctx->sslctx); - ctx->sslctx = quic_ssl_ctx(data); - if(!ctx->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - ctx->ssl = SSL_new(ctx->sslctx); - if(!ctx->ssl) - return CURLE_QUIC_CONNECT_ERROR; - - SSL_set_app_data(ctx->ssl, cf); - SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name); + result = quic_ssl_setup(cf, data); + if(result) + return result; result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); if(result) @@ -1357,8 +1429,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, offset += 1 + alpn_len; } - DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", - alpn_protocols + 1)); + CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", + alpn_protocols + 1); } return CURLE_OK; @@ -1389,7 +1461,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { /* Not time yet to attempt the next connect */ - DEBUGF(LOG_CF(data, cf, "waiting for reconnect time")); + CURL_TRC_CF(data, cf, "waiting for reconnect time"); goto out; } @@ -1412,12 +1484,12 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, goto out; if(quiche_conn_is_established(ctx->qconn)) { - DEBUGF(LOG_CF(data, cf, "handshake complete after %dms", - (int)Curl_timediff(now, ctx->started_at))); + CURL_TRC_CF(data, cf, "handshake complete after %dms", + (int)Curl_timediff(now, ctx->started_at)); ctx->handshake_at = now; result = cf_verify_peer(cf, data); if(!result) { - DEBUGF(LOG_CF(data, cf, "peer verified")); + CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; cf->conn->alpn = CURL_HTTP_VERSION_3; *done = TRUE; @@ -1436,8 +1508,8 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, */ int reconn_delay_ms = 200; - DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms", - reconn_delay_ms)); + CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms", + reconn_delay_ms); Curl_conn_cf_close(cf->next, data); cf_quiche_ctx_clear(ctx); result = Curl_conn_cf_connect(cf->next, data, FALSE, done); @@ -1503,7 +1575,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; - DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1)); + CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 399de0b..9a1a1bb 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -43,12 +43,14 @@ #include "bufq.h" #include "dynbuf.h" #include "cfilters.h" -#include "curl_log.h" +#include "curl_trc.h" #include "curl_msh3.h" #include "curl_ngtcp2.h" #include "curl_quiche.h" +#include "rand.h" #include "vquic.h" #include "vquic_int.h" +#include "strerror.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -88,6 +90,16 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) #else qctx->no_gso = TRUE; #endif +#ifdef DEBUGBUILD + { + char *p = getenv("CURL_DBG_QUIC_WBLOCK"); + if(p) { + long l = strtol(p, NULL, 10); + if(l >= 0 && l <= 100) + qctx->wblock_percent = (int)l; + } + } +#endif return CURLE_OK; } @@ -230,6 +242,17 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { +#ifdef DEBUGBUILD + /* simulate network blocking/partial writes */ + if(qctx->wblock_percent > 0) { + unsigned char c; + Curl_rand(data, &c, 1); + if(c >= ((100-qctx->wblock_percent)*256/100)) { + CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); + return CURLE_AGAIN; + } + } +#endif if(qctx->no_gso && pktlen > gsolen) { return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } @@ -253,11 +276,9 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len; } - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)", - blen, gsolen)); result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); - DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", - blen, gsolen, result, sent)); + CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", + blen, gsolen, result, sent); if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -288,9 +309,9 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; qctx->split_gsolen = gsolen; qctx->gsolen = tail_gsolen; - DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", - qctx->split_len, qctx->split_gsolen, - tail_len, qctx->gsolen)); + CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", + qctx->split_len, qctx->split_gsolen, + tail_len, qctx->gsolen); return vquic_flush(cf, data, qctx); } @@ -308,6 +329,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr[MMSG_NUM]; size_t total_nread, pkts; int mcount, i, n; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; DEBUGASSERT(max_pkts > 0); @@ -330,12 +352,12 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, ; if(mcount == -1) { if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN")); + CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -343,13 +365,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)", - mcount, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)", + mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } - DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount)); + CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); pkts += mcount; for(i = 0; i < mcount; ++i) { total_nread += mmsg[i].msg_len; @@ -362,8 +385,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } @@ -380,6 +404,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr; size_t total_nread, pkts; ssize_t nread; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; msg_iov.iov_base = buf; @@ -401,8 +426,8 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -410,8 +435,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -425,8 +451,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } @@ -443,6 +470,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, socklen_t remote_addrlen = sizeof(remote_addr); size_t total_nread, pkts; ssize_t nread; + char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; DEBUGASSERT(max_pkts > 0); @@ -454,12 +482,12 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, ; if(nread == -1) { if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN")); + CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); goto out; } if(!cf->connected && SOCKERRNO == ECONNREFUSED) { - const char *r_ip; - int r_port; + const char *r_ip = NULL; + int r_port = 0; Curl_cf_socket_peek(cf->next, data, NULL, NULL, &r_ip, &r_port, NULL, NULL); failf(data, "QUIC: connection to %s port %u refused", @@ -467,8 +495,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT; goto out; } - failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)", - nread, SOCKERRNO); + Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)", + nread, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -482,8 +511,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, } out: - DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result)); + if(total_nread || result) + CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", + pkts, total_nread, result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h index 8e08784..dbcd009 100644 --- a/Utilities/cmcurl/lib/vquic/vquic_int.h +++ b/Utilities/cmcurl/lib/vquic/vquic_int.h @@ -41,6 +41,9 @@ struct cf_quic_ctx { size_t gsolen; /* length of individual packets in send buf */ size_t split_len; /* if != 0, buffer length after which GSO differs */ size_t split_gsolen; /* length of individual packets after split_len */ +#ifdef DEBUGBUILD + int wblock_percent; /* percent of writes doing EAGAIN */ +#endif bool no_gso; /* do not use gso on sending */ }; diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index 1cecb64..dea0084 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c @@ -40,9 +40,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -2186,7 +2183,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) myssh_setup_connection(data, conn); /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); if(conn->handler->protocol & CURLPROTO_SCP) { diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index 98fce51..37040b4 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c @@ -43,9 +43,6 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -2537,7 +2534,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) if(from > size) { failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize); + CURL_FORMAT_CURL_OFF_T ")", from, + (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } if(from > to) { @@ -2563,7 +2561,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, attrs.filesize); + data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } /* download from where? */ @@ -2573,7 +2571,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) 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 ")", - data->state.resume_from, attrs.filesize); + data->state.resume_from, (curl_off_t)attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } } @@ -3254,7 +3252,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) } /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); sshc = &conn->proto.sshc; diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c index b47c231..306d299 100644 --- a/Utilities/cmcurl/lib/vssh/wolfssh.c +++ b/Utilities/cmcurl/lib/vssh/wolfssh.c @@ -374,7 +374,7 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done) wssh_setup_connection(data, conn); /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ + function to make the reuse checks properly be able to check this bit. */ connkeep(conn, "SSH default"); if(conn->handler->protocol & CURLPROTO_SCP) { diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c index 6ed453b..934149c 100644 --- a/Utilities/cmcurl/lib/vtls/bearssl.c +++ b/Utilities/cmcurl/lib/vtls/bearssl.c @@ -587,6 +587,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, const bool verifyhost = conn_config->verifyhost; CURLcode ret; unsigned version_min, version_max; + int session_set = 0; #ifdef ENABLE_IPV6 struct in6_addr addr; #else @@ -594,6 +595,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, #endif DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step1"); switch(conn_config->version) { case CURL_SSLVERSION_SSLv2: @@ -624,38 +626,34 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if(ca_info_blob) { - struct cafile_source source; - source.type = CAFILE_SOURCE_BLOB; - source.data = ca_info_blob->data; - source.len = ca_info_blob->len; + if(verifypeer) { + if(ca_info_blob) { + struct cafile_source source; + source.type = CAFILE_SOURCE_BLOB; + source.data = ca_info_blob->data; + source.len = ca_info_blob->len; - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { + CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { failf(data, "error importing CA certificate blob"); return ret; } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); } - } - if(ssl_cafile) { - struct cafile_source source; - source.type = CAFILE_SOURCE_PATH; - source.data = ssl_cafile; - source.len = 0; + if(ssl_cafile) { + struct cafile_source source; + source.type = CAFILE_SOURCE_PATH; + source.data = ssl_cafile; + source.len = 0; - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { + CURL_TRC_CF(data, cf, "connect_step1, load cafile"); + ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); + if(ret != CURLE_OK) { failf(data, "error setting certificate verify locations." " CAfile: %s", ssl_cafile); return ret; } - infof(data, "error setting certificate verify locations," - " continuing anyway:"); } } @@ -669,6 +667,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(conn_config->cipher_list) { /* Override the ciphers as specified. For the default cipher list see the BearSSL source code of br_ssl_client_init_full() */ + CURL_TRC_CF(data, cf, "connect_step1, set ciphers"); ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, conn_config->cipher_list); if(ret) @@ -684,10 +683,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(ssl_config->primary.sessionid) { void *session; + CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) { br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); - infof(data, "BearSSL: re-using session ID"); + session_set = 1; + infof(data, "BearSSL: reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -724,6 +725,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } hostname = snihost; + CURL_TRC_CF(data, cf, "connect_step1, SNI set"); } /* give application a chance to interfere with SSL set up. */ @@ -738,7 +740,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, } } - if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) + if(!br_ssl_client_reset(&backend->ctx, hostname, session_set)) return CURLE_FAILED_INIT; backend->active = TRUE; @@ -747,6 +749,28 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK; } +static int bearssl_get_select_socks(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t *socks) +{ + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data); + + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + else { + 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) { + socks[0] = sock; + return GETSOCK_WRITESOCK(0); + } + } + socks[0] = sock; + return GETSOCK_READSOCK(0); +} + static CURLcode bearssl_run_until(struct Curl_cfilter *cf, struct Curl_easy *data, unsigned target) @@ -798,6 +822,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, 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); + CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); if(ret <= 0) { return result; } @@ -806,6 +831,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf, else if(state & BR_SSL_RECVREC) { buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); + CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); if(ret == 0) { failf(data, "SSL: EOF without close notify"); return CURLE_READ_ERROR; @@ -827,16 +853,26 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, CURLcode ret; DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step2"); ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) return CURLE_OK; if(ret == CURLE_OK) { + unsigned int tver; if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); return CURLE_SSL_CONNECT_ERROR; } connssl->connecting_state = ssl_connect_3; + /* Informational message */ + tver = br_ssl_engine_get_version(&backend->ctx.eng); + if(tver == 0x0303) + infof(data, "SSL connection using TLSv1.2"); + else if(tver == 0x0304) + infof(data, "SSL connection using TLSv1.3"); + else + infof(data, "SSL connection using TLS 0x%x", tver); } return ret; } @@ -852,6 +888,7 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); + CURL_TRC_CF(data, cf, "connect_step3"); if(connssl->alpn) { const char *proto; @@ -960,8 +997,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, timediff_t timeout_ms; int what; + CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking); /* check if the connection has already been established */ if(ssl_connection_complete == connssl->state) { + CURL_TRC_CF(data, cf, "connect_common, connected"); *done = TRUE; return CURLE_OK; } @@ -993,8 +1032,10 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf, curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state?sockfd:CURL_SOCKET_BAD; + CURL_TRC_CF(data, cf, "connect_common, check socket"); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, nonblocking?0:timeout_ms); + CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -1169,7 +1210,7 @@ const struct Curl_ssl Curl_ssl_bearssl = { Curl_none_cert_status_request, /* cert_status_request */ bearssl_connect, /* connect */ bearssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ + bearssl_get_select_socks, /* getsock */ bearssl_get_internals, /* get_internals */ bearssl_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c deleted file mode 100644 index c128293..0000000 --- a/Utilities/cmcurl/lib/vtls/gskit.c +++ /dev/null @@ -1,1329 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_GSKIT - -#include <gskssl.h> -#include <qsoasync.h> -#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ -#include "socketpair.h" -#include "strerror.h" - -/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ -#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST -#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 -#endif - -#ifndef GSK_TLSV10_CIPHER_SPECS -#define GSK_TLSV10_CIPHER_SPECS 236 -#endif - -#ifndef GSK_TLSV11_CIPHER_SPECS -#define GSK_TLSV11_CIPHER_SPECS 237 -#endif - -#ifndef GSK_TLSV12_CIPHER_SPECS -#define GSK_TLSV12_CIPHER_SPECS 238 -#endif - -#ifndef GSK_PROTOCOL_TLSV11 -#define GSK_PROTOCOL_TLSV11 437 -#endif - -#ifndef GSK_PROTOCOL_TLSV12 -#define GSK_PROTOCOL_TLSV12 438 -#endif - -#ifndef GSK_FALSE -#define GSK_FALSE 0 -#endif - -#ifndef GSK_TRUE -#define GSK_TRUE 1 -#endif - - -#include <limits.h> - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "gskit.h" -#include "vtls.h" -#include "vtls_int.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "timediff.h" -#include "x509asn1.h" -#include "curl_printf.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - - -/* Directions. */ -#define SOS_READ 0x01 -#define SOS_WRITE 0x02 - -/* SSL version flags. */ -#define CURL_GSKPROTO_SSLV2 0 -#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2) -#define CURL_GSKPROTO_SSLV3 1 -#define CURL_GSKPROTO_SSLV3_MASK (1 << CURL_GSKPROTO_SSLV3) -#define CURL_GSKPROTO_TLSV10 2 -#define CURL_GSKPROTO_TLSV10_MASK (1 << CURL_GSKPROTO_TLSV10) -#define CURL_GSKPROTO_TLSV11 3 -#define CURL_GSKPROTO_TLSV11_MASK (1 << CURL_GSKPROTO_TLSV11) -#define CURL_GSKPROTO_TLSV12 4 -#define CURL_GSKPROTO_TLSV12_MASK (1 << CURL_GSKPROTO_TLSV12) -#define CURL_GSKPROTO_LAST 5 - -struct gskit_ssl_backend_data { - gsk_handle handle; - int iocport; - int localfd; - int remotefd; -}; - -#define BACKEND ((struct gskit_ssl_backend_data *)connssl->backend) - -/* Supported ciphers. */ -struct gskit_cipher { - const char *name; /* Cipher name. */ - const char *gsktoken; /* Corresponding token for GSKit String. */ - unsigned int versions; /* SSL version flags. */ -}; - -static const struct gskit_cipher ciphertable[] = { - { "null-md5", "01", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha", "02", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc4-md5", "03", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "rc4-md5", "04", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-sha", "05", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc2-cbc-md5", "06", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "exp-des-cbc-sha", "09", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK }, - { "des-cbc3-sha", "0A", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha", "2F", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha", "35", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-gcm-sha256", - "9C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-gcm-sha384", - "9D", CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK }, - { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc2-md5", "4", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc-md5", "6", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc3-md5", "7", CURL_GSKPROTO_SSLV2_MASK }, - { (const char *) NULL, (const char *) NULL, 0 } -}; - - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - - -static CURLcode gskit_status(struct Curl_easy *data, int rc, - const char *procname, CURLcode defcode) -{ - char buffer[STRERROR_LEN]; - /* Process GSKit status and map it to a CURLcode. */ - switch(rc) { - case GSK_OK: - case GSK_OS400_ASYNCHRONOUS_SOC_INIT: - return CURLE_OK; - case GSK_KEYRING_OPEN_ERROR: - case GSK_OS400_ERROR_NO_ACCESS: - return CURLE_SSL_CACERT_BADFILE; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - case GSK_ERROR_BAD_V2_CIPHER: - case GSK_ERROR_BAD_V3_CIPHER: - case GSK_ERROR_NO_CIPHERS: - return CURLE_SSL_CIPHER; - case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: - case GSK_ERROR_CERT_VALIDATION: - return CURLE_PEER_FAILED_VERIFICATION; - case GSK_OS400_ERROR_TIMED_OUT: - return CURLE_OPERATION_TIMEDOUT; - case GSK_WOULD_BLOCK: - return CURLE_AGAIN; - case GSK_OS400_ERROR_NOT_REGISTERED: - break; - case GSK_ERROR_IO: - switch(errno) { - case ENOMEM: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "%s I/O error: %s", procname, - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - } - break; - default: - failf(data, "%s: %s", procname, gsk_strerror(rc)); - break; - } - return defcode; -} - - -static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, - GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_enum(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_enum() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buf, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buf, 0); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_buffer() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_numeric(struct Curl_easy *data, - gsk_handle h, GSK_NUM_ID id, int value) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_numeric_value(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - default: - failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_ciphers(struct Curl_cfilter *cf, struct Curl_easy *data, - gsk_handle h, unsigned int *protoflags) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct connectdata *conn = data->conn; - const char *cipherlist = conn_config->cipher_list; - const char *clp; - const struct gskit_cipher *ctp; - int i; - int l; - bool unsupported; - CURLcode result; - struct { - char *buf; - char *ptr; - } ciphers[CURL_GSKPROTO_LAST]; - - /* Compile cipher list into GSKit-compatible cipher lists. */ - - if(!cipherlist) - return CURLE_OK; - while(is_separator(*cipherlist)) /* Skip initial separators. */ - cipherlist++; - if(!*cipherlist) - return CURLE_OK; - - /* We allocate GSKit buffers of the same size as the input string: since - GSKit tokens are always shorter than their cipher names, allocated buffers - will always be large enough to accommodate the result. */ - l = strlen(cipherlist) + 1; - memset(ciphers, 0, sizeof(ciphers)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - ciphers[i].buf = malloc(l); - if(!ciphers[i].buf) { - while(i--) - free(ciphers[i].buf); - return CURLE_OUT_OF_MEMORY; - } - ciphers[i].ptr = ciphers[i].buf; - *ciphers[i].ptr = '\0'; - } - - /* Process each cipher in input string. */ - unsupported = FALSE; - result = CURLE_OK; - for(;;) { - for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) - cipherlist++; - l = cipherlist - clp; - if(!l) - break; - /* Search the cipher in our table. */ - for(ctp = ciphertable; ctp->name; ctp++) - if(strncasecompare(ctp->name, clp, l) && !ctp->name[l]) - break; - if(!ctp->name) { - failf(data, "Unknown cipher %.*s", l, clp); - result = CURLE_SSL_CIPHER; - } - else { - unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK | - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(ctp->versions & (1 << i)) { - strcpy(ciphers[i].ptr, ctp->gsktoken); - ciphers[i].ptr += strlen(ctp->gsktoken); - } - } - } - - /* Advance to next cipher name or end of string. */ - while(is_separator(*cipherlist)) - cipherlist++; - } - - /* Disable protocols with empty cipher lists. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) { - *protoflags &= ~(1 << i); - ciphers[i].buf[0] = '\0'; - } - } - - /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */ - if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) { - result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.1-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { - result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.2-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - - /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to - the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */ - if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { - result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr, - ciphers[CURL_GSKPROTO_TLSV10].ptr); - } - } - - /* Set-up other ciphers. */ - if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) - result = set_buffer(data, h, GSK_V3_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); - if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) - result = set_buffer(data, h, GSK_V2_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); - - /* Clean-up. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) - free(ciphers[i].buf); - - return result; -} - - -static int gskit_init(void) -{ - /* No initialization needed. */ - return 1; -} - - -static void gskit_cleanup(void) -{ - /* Nothing to do. */ -} - - -static CURLcode init_environment(struct Curl_easy *data, - gsk_handle *envir, const char *appid, - const char *file, const char *label, - const char *password) -{ - int rc; - CURLcode result; - gsk_handle h; - - /* Creates the GSKit environment. */ - - rc = gsk_environment_open(&h); - switch(rc) { - case GSK_OK: - break; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - - result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); - if(!result && appid) - result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); - if(!result && file) - result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); - if(!result && label) - result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); - if(!result && password) - result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); - - if(!result) { - /* Locate CAs, Client certificate and key according to our settings. - Note: this call may be blocking for some tenths of seconds. */ - result = gskit_status(data, gsk_environment_init(h), - "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); - if(!result) { - *envir = h; - return result; - } - } - /* Error: rollback. */ - gsk_environment_close(&h); - return result; -} - - -static void cancel_async_handshake(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - Qso_OverlappedIO_t cstat; - - (void)data; - DEBUGASSERT(BACKEND); - - if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0) - QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); -} - - -static void close_async_handshake(struct ssl_connect_data *connssl) -{ - DEBUGASSERT(BACKEND); - QsoDestroyIOCompletionPort(BACKEND->iocport); - BACKEND->iocport = -1; -} - -static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data, - int directions) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - struct gskit_ssl_backend_data *backend_next; - struct pollfd fds[2]; - int n; - int m; - int i; - int ret = 0; - char buf[CURL_MAX_WRITE_SIZE]; - - DEBUGASSERT(BACKEND); - - if(!connssl_next) - return 0; /* No SSL over SSL: OK. */ - - DEBUGASSERT(connssl_next->backend); - backend_next = (struct gskit_ssl_backend_data *)connssl_next->backend; - - n = 1; - fds[0].fd = BACKEND->remotefd; - fds[1].fd = Curl_conn_cf_get_socket(cf, data); - - if(directions & SOS_READ) { - fds[0].events |= POLLOUT; - } - if(directions & SOS_WRITE) { - n = 2; - fds[0].events |= POLLIN; - fds[1].events |= POLLOUT; - } - i = Curl_poll(fds, n, 0); - if(i < 0) - return -1; /* Select error. */ - - if(fds[0].revents & POLLOUT) { - /* Try getting data from HTTPS proxy and pipe it upstream. */ - n = 0; - i = gsk_secure_soc_read(backend_next->handle, buf, sizeof(buf), &n); - switch(i) { - case GSK_OK: - if(n) { - i = write(BACKEND->remotefd, buf, n); - if(i < 0) - return -1; - ret = 1; - } - break; - case GSK_OS400_ERROR_TIMED_OUT: - case GSK_WOULD_BLOCK: - break; - default: - return -1; - } - } - - if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { - /* Pipe data to HTTPS proxy. */ - n = read(BACKEND->remotefd, buf, sizeof(buf)); - if(n < 0) - return -1; - if(n) { - i = gsk_secure_soc_write(backend_next->handle, buf, n, &m); - if(i != GSK_OK || n != m) - return -1; - ret = 1; - } - } - - return ret; /* OK */ -} - - -static void close_one(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - - DEBUGASSERT(BACKEND); - if(BACKEND->handle) { - gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), - "gsk_secure_soc_close()", 0); - /* Last chance to drain output. */ - while(pipe_ssloverssl(cf, data, SOS_WRITE) > 0) - ; - BACKEND->handle = (gsk_handle) NULL; - if(BACKEND->localfd >= 0) { - close(BACKEND->localfd); - BACKEND->localfd = -1; - } - if(BACKEND->remotefd >= 0) { - close(BACKEND->remotefd); - BACKEND->remotefd = -1; - } - } - if(BACKEND->iocport >= 0) - close_async_handshake(connssl); -} - - -static ssize_t gskit_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, CURLcode *curlcode) -{ - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - CURLcode cc = CURLE_SEND_ERROR; - int written; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(cf, data, SOS_WRITE) >= 0) { - cc = gskit_status(data, - gsk_secure_soc_write(BACKEND->handle, - (char *) mem, (int) len, &written), - "gsk_secure_soc_write()", CURLE_SEND_ERROR); - if(cc == CURLE_OK) - if(pipe_ssloverssl(cf, data, SOS_WRITE) < 0) - cc = CURLE_SEND_ERROR; - } - if(cc != CURLE_OK) { - *curlcode = cc; - written = -1; - } - return (ssize_t) written; /* number of bytes */ -} - - -static ssize_t gskit_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t buffersize, CURLcode *curlcode) -{ - struct connectdata *conn = cf->conn; - struct ssl_connect_data *connssl = cf->ctx; - int nread; - CURLcode cc = CURLE_RECV_ERROR; - - (void)data; - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(cf, data, SOS_READ) >= 0) { - int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; - cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, - buf, buffsize, &nread), - "gsk_secure_soc_read()", CURLE_RECV_ERROR); - } - switch(cc) { - case CURLE_OK: - break; - case CURLE_OPERATION_TIMEDOUT: - cc = CURLE_AGAIN; - default: - *curlcode = cc; - nread = -1; - break; - } - return (ssize_t) nread; -} - -static CURLcode -set_ssl_version_min_max(unsigned int *protoflags, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct connectdata *conn = data->conn; - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; - long i = ssl_version; - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_TLSv1_2; - break; - } - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - *protoflags |= CURL_GSKPROTO_TLSV10_MASK; - break; - case CURL_SSLVERSION_TLSv1_1: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_2: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "GSKit: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode gskit_connect_step1(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 ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - gsk_handle envir; - CURLcode result; - const char * const keyringfile = conn_config->CAfile; - const char * const keyringpwd = ssl_config->key_passwd; - const char * const keyringlabel = ssl_config->primary.clientcert; - const long int ssl_version = conn_config->version; - const bool verifypeer = conn_config->verifypeer; - const char *hostname = connssl->hostname; - const char *sni; - unsigned int protoflags = 0; - Qso_OverlappedIO_t commarea; - int sockpair[2]; - static const int sobufsize = CURL_MAX_WRITE_SIZE; - - /* Create SSL environment, start (preferably asynchronous) handshake. */ - DEBUGASSERT(BACKEND); - - BACKEND->handle = (gsk_handle) NULL; - BACKEND->iocport = -1; - BACKEND->localfd = -1; - BACKEND->remotefd = -1; - - /* GSKit supports two ways of specifying an SSL context: either by - * application identifier (that should have been defined at the system - * level) or by keyring file, password and certificate label. - * Local certificate name (CURLOPT_SSLCERT) is used to hold either the - * application identifier of the certificate label. - * Key password (CURLOPT_KEYPASSWD) holds the keyring password. - * It is not possible to have different keyrings for the CAs and the - * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify - * the keyring file. - * If no key password is given and the keyring is the system keyring, - * application identifier mode is tried first, as recommended in IBM doc. - */ - - envir = (gsk_handle) NULL; - - if(keyringlabel && *keyringlabel && !keyringpwd && - !strcmp(keyringfile, CURL_CA_BUNDLE)) { - /* Try application identifier mode. */ - init_environment(data, &envir, keyringlabel, (const char *) NULL, - (const char *) NULL, (const char *) NULL); - } - - if(!envir) { - /* Use keyring mode. */ - result = init_environment(data, &envir, (const char *) NULL, - keyringfile, keyringlabel, keyringpwd); - if(result) - return result; - } - - /* Create secure session. */ - result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle), - "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); - gsk_environment_close(&envir); - if(result) - return result; - - /* Establish a pipelining socket pair for SSL over SSL. */ - if(connssl_next) { - if(Curl_socketpair(0, 0, 0, sockpair)) - return CURLE_SSL_CONNECT_ERROR; - BACKEND->localfd = sockpair[0]; - BACKEND->remotefd = sockpair[1]; - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - curlx_nonblock(BACKEND->localfd, TRUE); - curlx_nonblock(BACKEND->remotefd, TRUE); - } - - /* Determine which SSL/TLS version should be enabled. */ - sni = hostname; - switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - protoflags = CURL_GSKPROTO_SSLV2_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_SSLv3: - protoflags = CURL_GSKPROTO_SSLV3_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - protoflags = CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK; - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - result = set_ssl_version_min_max(&protoflags, cf, data); - if(result != CURLE_OK) - return result; - break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ - if(sni) { - char *snihost = Curl_ssl_snihost(data, sni, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) - result = CURLE_OK; - } - - /* Set session parameters. */ - if(!result) { - /* Compute the handshake timeout. Since GSKit granularity is 1 second, - we round up the required value. */ - timediff_t timeout = Curl_timeleft(data, NULL, TRUE); - if(timeout < 0) - result = CURLE_OPERATION_TIMEDOUT; - else - result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT, - (timeout + 999) / 1000); - } - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1); - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0? - BACKEND->localfd: Curl_conn_cf_get_socket(cf, data)); - if(!result) - result = set_ciphers(cf, data, BACKEND->handle, &protoflags); - if(!protoflags) { - failf(data, "No SSL protocol/cipher combination enabled"); - result = CURLE_SSL_CIPHER; - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2, - (protoflags & CURL_GSKPROTO_SSLV2_MASK)? - GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3, - (protoflags & CURL_GSKPROTO_SSLV3_MASK)? - GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1, - (protoflags & CURL_GSKPROTO_TLSV10_MASK)? - GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11, - (protoflags & CURL_GSKPROTO_TLSV11_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV11_MASK) { - failf(data, "TLS 1.1 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12, - (protoflags & CURL_GSKPROTO_TLSV12_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV12_MASK) { - failf(data, "TLS 1.2 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE, - verifypeer? GSK_SERVER_AUTH_FULL: - GSK_SERVER_AUTH_PASSTHRU, FALSE); - - if(!result) { - /* Start handshake. Try asynchronous first. */ - memset(&commarea, 0, sizeof(commarea)); - BACKEND->iocport = QsoCreateIOCompletionPort(); - if(BACKEND->iocport != -1) { - result = gskit_status(data, - gsk_secure_soc_startInit(BACKEND->handle, - BACKEND->iocport, - &commarea), - "gsk_secure_soc_startInit()", - CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; - } - else - close_async_handshake(connssl); - } - else if(errno != ENOBUFS) - result = gskit_status(data, GSK_ERROR_IO, - "QsoCreateIOCompletionPort()", 0); - else if(connssl_next) { - /* Cannot pipeline while handshaking synchronously. */ - result = CURLE_SSL_CONNECT_ERROR; - } - else { - /* No more completion port available. Use synchronous IO. */ - result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle), - "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_3; - return CURLE_OK; - } - } - } - - /* Error: rollback. */ - close_one(cf, data); - return result; -} - - -static CURLcode gskit_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool nonblocking) -{ - struct ssl_connect_data *connssl = cf->ctx; - Qso_OverlappedIO_t cstat; - struct timeval stmv; - CURLcode result; - - /* Poll or wait for end of SSL asynchronous handshake. */ - DEBUGASSERT(BACKEND); - - for(;;) { - timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); - stmv.tv_sec = 0; - stmv.tv_usec = 0; - if(timeout_ms < 0) - timeout_ms = 0; - switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, - curlx_mstotv(&stmv, timeout_ms))) { - case 1: /* Operation complete. */ - break; - case -1: /* An error occurred: handshake still in progress. */ - if(errno == EINTR) { - if(nonblocking) - return CURLE_OK; - continue; /* Retry. */ - } - if(errno != ETIME) { - char buffer[STRERROR_LEN]; - failf(data, "QsoWaitForIOCompletion() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - cancel_async_handshake(cf, data); - close_async_handshake(connssl); - return CURLE_SSL_CONNECT_ERROR; - } - /* FALL INTO... */ - case 0: /* Handshake in progress, timeout occurred. */ - if(nonblocking) - return CURLE_OK; - cancel_async_handshake(cf, data); - close_async_handshake(connssl); - return CURLE_OPERATION_TIMEDOUT; - } - break; - } - result = gskit_status(data, cstat.returnValue, "SSL handshake", - CURLE_SSL_CONNECT_ERROR); - if(!result) - connssl->connecting_state = ssl_connect_3; - close_async_handshake(connssl); - return result; -} - - -static CURLcode gskit_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - const gsk_cert_data_elem *cdev; - int cdec; - const gsk_cert_data_elem *p; - const char *cert = (const char *) NULL; - const char *certend = (const char *) NULL; - const char *ptr; - CURLcode result; - - /* SSL handshake done: gather certificate info and verify host. */ - DEBUGASSERT(BACKEND); - - if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, - GSK_PARTNER_CERT_INFO, - &cdev, &cdec), - "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == - CURLE_OK) { - int i; - - infof(data, "Server certificate:"); - p = cdev; - for(i = 0; i++ < cdec; p++) - switch(p->cert_data_id) { - case CERT_BODY_DER: - cert = p->cert_data_p; - certend = cert + cdev->cert_data_l; - break; - case CERT_DN_PRINTABLE: - infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_ISSUER_DN_PRINTABLE: - infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_FROM: - infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_TO: - infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p); - break; - } - } - - /* Verify host. */ - result = Curl_verifyhost(cf, data, cert, certend); - if(result) - return result; - - /* The only place GSKit can get the whole CA chain is a validation - callback where no user data pointer is available. Therefore it's not - possible to copy this chain into our structures for CAINFO. - However the server certificate may be available, thus we can return - info about it. */ - if(data->set.ssl.certinfo) { - result = Curl_ssl_init_certinfo(data, 1); - if(result) - return result; - - if(cert) { - result = Curl_extract_certinfo(data, 0, cert, certend); - if(result) - return result; - } - } - - /* Check pinned public key. */ - ptr = Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - if(!result && ptr) { - struct Curl_X509certificate x509; - struct Curl_asn1Element *p; - - memset(&x509, 0, sizeof(x509)); - if(Curl_parseX509(&x509, cert, certend)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - p = &x509.subjectPublicKeyInfo; - result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - - -static CURLcode gskit_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool nonblocking, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *done = connssl->state == ssl_connection_complete; - if(*done) - return CURLE_OK; - - /* Step 1: create session, start handshake. */ - if(connssl->connecting_state == ssl_connect_1) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step1(cf, data); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 2: check if handshake is over. */ - if(!result && connssl->connecting_state == ssl_connect_2) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step2(cf, data, nonblocking); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 3: gather certificate info, verify host. */ - if(!result && connssl->connecting_state == ssl_connect_3) - result = gskit_connect_step3(cf, data); - - if(result) - close_one(cf, data); - else if(connssl->connecting_state == ssl_connect_done) { - connssl->state = ssl_connection_complete; - connssl->connecting_state = ssl_connect_1; - *done = TRUE; - } - - return result; -} - - -static CURLcode gskit_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - - result = gskit_connect_common(cf, data, TRUE, done); - if(*done || result) - connssl->connecting_state = ssl_connect_1; - return result; -} - - -static CURLcode gskit_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - bool done; - - connssl->connecting_state = ssl_connect_1; - result = gskit_connect_common(cf, data, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - - -static void gskit_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - close_one(cf, data); -} - - -static int gskit_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - int what; - int rc; - char buf[120]; - int loop = 10; /* don't get stuck */ - - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - close_one(cf, data); - rc = 0; - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), - SSL_SHUTDOWN_TIMEOUT); - - while(loop--) { - ssize_t nread; - - 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 gsk_secure_soc_read() now, so - use read(). */ - - nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf)); - - if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer))); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0); - } - - return rc; -} - - -static size_t gskit_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GSKit"); -} - - -static int gskit_check_cxn(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - int err; - int errlen; - - (void)data; - /* The only thing that can be tested here is at the socket level. */ - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; /* connection has been closed */ - - err = 0; - errlen = sizeof(err); - - if(getsockopt(Curl_conn_cf_get_socket(cf, data), SOL_SOCKET, SO_ERROR, - (unsigned char *) &err, &errlen) || - errlen != sizeof(err) || err) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void *gskit_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - (void)info; - DEBUGASSERT(BACKEND); - return BACKEND->handle; -} - -const struct Curl_ssl Curl_ssl_gskit = { - { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */ - - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct gskit_ssl_backend_data), - - gskit_init, /* init */ - gskit_cleanup, /* cleanup */ - gskit_version, /* version */ - gskit_check_cxn, /* check_cxn */ - gskit_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - gskit_connect, /* connect */ - gskit_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ - gskit_get_internals, /* get_internals */ - gskit_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* No session handling for GSKit */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - gskit_recv, /* recv decrypted data */ - gskit_send, /* send data to encrypt */ -}; - -#endif /* USE_GSKIT */ diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h deleted file mode 100644 index c71e6a0..0000000 --- a/Utilities/cmcurl/lib/vtls/gskit.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef HEADER_CURL_GSKIT_H -#define HEADER_CURL_GSKIT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -/* - * This header should only be needed to get included by vtls.c and gskit.c - */ - -#include "urldata.h" - -#ifdef USE_GSKIT - -extern const struct Curl_ssl Curl_ssl_gskit; - -#endif /* USE_GSKIT */ - -#endif /* HEADER_CURL_GSKIT_H */ diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index f6f1e10..e483469 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c @@ -735,7 +735,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) ssl_sessionid, ssl_idsize); /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c index d061c63..2726dca 100644 --- a/Utilities/cmcurl/lib/vtls/hostcheck.c +++ b/Utilities/cmcurl/lib/vtls/hostcheck.c @@ -24,8 +24,7 @@ #include "curl_setup.h" -#if defined(USE_OPENSSL) \ - || defined(USE_GSKIT) \ +#if defined(USE_OPENSSL) \ || defined(USE_SCHANNEL) /* these backends use functions from this file */ @@ -133,4 +132,4 @@ bool Curl_cert_hostcheck(const char *match, size_t matchlen, return FALSE; } -#endif /* OPENSSL, GSKIT or schannel+wince */ +#endif /* OPENSSL or SCHANNEL */ diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 8d0fa39..f45636e 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c @@ -165,8 +165,8 @@ static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen) DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d", - blen, nwritten, result)); + CURL_TRC_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d", + blen, nwritten, result); if(nwritten < 0 && CURLE_AGAIN == result) { nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; } @@ -186,8 +186,8 @@ static int bio_cf_read(void *bio, unsigned char *buf, size_t blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d", - blen, nread, result)); + CURL_TRC_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d", + blen, nread, result); if(nread < 0 && CURLE_AGAIN == result) { nread = MBEDTLS_ERR_SSL_WANT_READ; } @@ -619,7 +619,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } - infof(data, "mbedTLS re-using session"); + infof(data, "mbedTLS reusing session"); } Curl_ssl_sessionid_unlock(data); } diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c deleted file mode 100644 index 322f507..0000000 --- a/Utilities/cmcurl/lib/vtls/nss.c +++ /dev/null @@ -1,2551 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* - * Source file for all NSS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_NSS - -#include "urldata.h" -#include "sendf.h" -#include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ -#include "connect.h" -#include "strcase.h" -#include "select.h" -#include "vtls.h" -#include "vtls_int.h" -#include "llist.h" -#include "multiif.h" -#include "curl_printf.h" -#include "nssg.h" -#include <nspr.h> -#include <nss.h> -#include <ssl.h> -#include <sslerr.h> -#include <secerr.h> -#include <secmod.h> -#include <sslproto.h> -#include <prtypes.h> -#include <pk11pub.h> -#include <prio.h> -#include <secitem.h> -#include <secport.h> -#include <certdb.h> -#include <base64.h> -#include <cert.h> -#include <prerror.h> -#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */ -#include <private/pprio.h> /* for PR_ImportTCPSocket */ - -#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) - -#if NSSVERNUM >= 0x030f00 /* 3.15.0 */ -#include <ocsp.h> -#endif - -#include "warnless.h" -#include "x509asn1.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define SSL_DIR "/etc/pki/nssdb" - -/* enough to fit the string "PEM Token #[0|1]" */ -#define SLOTSIZE 13 - -struct nss_ssl_backend_data { - PRFileDesc *handle; - char *client_nickname; - struct Curl_easy *data; - struct Curl_llist obj_list; - PK11GenericObject *obj_clicert; -}; - -static PRLock *nss_initlock = NULL; -static PRLock *nss_crllock = NULL; -static PRLock *nss_findslot_lock = NULL; -static PRLock *nss_trustload_lock = NULL; -static struct Curl_llist nss_crl_list; -static NSSInitContext *nss_context = NULL; -static volatile int initialized = 0; - -/* type used to wrap pointers as list nodes */ -struct ptr_list_wrap { - void *ptr; - struct Curl_llist_element node; -}; - -struct cipher_s { - const char *name; - int num; -}; - -#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ - CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ - ptr->type = (_type); \ - ptr->pValue = (_val); \ - ptr->ulValueLen = (_len); \ -} while(0) - -#define CERT_NewTempCertificate __CERT_NewTempCertificate - -#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) -static const struct cipher_s cipherlist[] = { - /* SSL2 cipher suites */ - {"rc4", SSL_EN_RC4_128_WITH_MD5}, - {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, - {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, - {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, - {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, - {"des", SSL_EN_DES_64_CBC_WITH_MD5}, - {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, - /* SSL3/TLS cipher suites */ - {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, - {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, - {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, - {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, - {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, - {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, - {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, - {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, - {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, - {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, - {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, - {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, - {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, - {"dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA}, - {"dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA}, - {"dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA}, - /* TLS 1.0: Exportable 56-bit Cipher Suites. */ - {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, - {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, - /* Ephemeral DH with RC4 bulk encryption */ - {"dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA}, - /* AES ciphers. */ - {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA}, - {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA}, - {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, - {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, - {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, - {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, - /* ECC ciphers. */ - {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, - {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, - {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, - {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, - {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, - {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, - {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, - {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, - {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, - {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, - {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, - {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, - {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, -#ifdef TLS_RSA_WITH_NULL_SHA256 - /* new HMAC-SHA256 cipher suites specified in RFC */ - {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256}, - {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256}, - {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256}, - {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, - {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, - {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, - {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, -#endif -#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256 - /* AES GCM cipher suites in RFC 5288 and RFC 5289 */ - {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256}, - {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256}, -#endif -#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - /* cipher suites using SHA384 */ - {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384}, - {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, - {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, -#endif -#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - /* chacha20-poly1305 cipher suites */ - {"ecdhe_rsa_chacha20_poly1305_sha_256", - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, - {"ecdhe_ecdsa_chacha20_poly1305_sha_256", - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, - {"dhe_rsa_chacha20_poly1305_sha_256", - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_AES_256_GCM_SHA384 - {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, - {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, - {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */ - {"dhe_dss_aes_128_sha_256", TLS_DHE_DSS_WITH_AES_128_CBC_SHA256}, - {"dhe_dss_aes_256_sha_256", TLS_DHE_DSS_WITH_AES_256_CBC_SHA256}, -#endif -#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - /* Camellia cipher suites in RFC 4132/5932. - Introduced in NSS release 3.12 */ - {"dhe_rsa_camellia_128_sha", TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_dss_camellia_128_sha", TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_rsa_camellia_256_sha", TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA}, - {"dhe_dss_camellia_256_sha", TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA}, - {"rsa_camellia_128_sha", TLS_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"rsa_camellia_256_sha", TLS_RSA_WITH_CAMELLIA_256_CBC_SHA}, -#endif -#ifdef TLS_RSA_WITH_SEED_CBC_SHA - /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */ - {"rsa_seed_sha", TLS_RSA_WITH_SEED_CBC_SHA}, -#endif -}; - -#if defined(WIN32) -static const char *pem_library = "nsspem.dll"; -static const char *trust_library = "nssckbi.dll"; -#elif defined(__APPLE__) -static const char *pem_library = "libnsspem.dylib"; -static const char *trust_library = "libnssckbi.dylib"; -#else -static const char *pem_library = "libnsspem.so"; -static const char *trust_library = "libnssckbi.so"; -#endif - -static SECMODModule *pem_module = NULL; -static SECMODModule *trust_module = NULL; - -/* NSPR I/O layer we use to detect blocking direction during SSL handshake */ -static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; -static PRIOMethods nspr_io_methods; - -static const char *nss_error_to_name(PRErrorCode code) -{ - const char *name = PR_ErrorToName(code); - if(name) - return name; - - return "unknown error"; -} - -static void nss_print_error_message(struct Curl_easy *data, PRUint32 err) -{ - failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); -} - -static char *nss_sslver_to_name(PRUint16 nssver) -{ - switch(nssver) { - case SSL_LIBRARY_VERSION_2: - return strdup("SSLv2"); - case SSL_LIBRARY_VERSION_3_0: - return strdup("SSLv3"); - case SSL_LIBRARY_VERSION_TLS_1_0: - return strdup("TLSv1.0"); -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - case SSL_LIBRARY_VERSION_TLS_1_1: - return strdup("TLSv1.1"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - case SSL_LIBRARY_VERSION_TLS_1_2: - return strdup("TLSv1.2"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - case SSL_LIBRARY_VERSION_TLS_1_3: - return strdup("TLSv1.3"); -#endif - default: - return curl_maprintf("0x%04x", nssver); - } -} - -/* the longest cipher name this supports */ -#define MAX_CIPHER_LENGTH 128 - -static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model, - const char *cipher_list) -{ - unsigned int i; - const char *cipher; - - /* use accessors to avoid dynamic linking issues after an update of NSS */ - const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); - const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers(); - if(!implemented_ciphers) - return SECFailure; - - /* First disable all ciphers. This uses a different max value in case - * NSS adds more ciphers later we don't want them available by - * accident - */ - for(i = 0; i < num_implemented_ciphers; i++) { - SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); - } - - cipher = cipher_list; - - while(cipher && cipher[0]) { - const char *end; - char name[MAX_CIPHER_LENGTH + 1]; - size_t len; - bool found = FALSE; - while((*cipher) && (ISBLANK(*cipher))) - ++cipher; - - end = strpbrk(cipher, ":, "); - if(end) - len = end - cipher; - else - len = strlen(cipher); - - if(len > MAX_CIPHER_LENGTH) { - failf(data, "Bad cipher list"); - return SECFailure; - } - else if(len) { - memcpy(name, cipher, len); - name[len] = 0; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - if(strcasecompare(name, cipherlist[i].name)) { - /* Enable the selected cipher */ - if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != - SECSuccess) { - failf(data, "cipher-suite not supported by NSS: %s", name); - return SECFailure; - } - found = TRUE; - break; - } - } - } - - if(!found && len) { - failf(data, "Unknown cipher: %s", name); - return SECFailure; - } - if(end) - cipher = ++end; - else - break; - } - - return SECSuccess; -} - -/* - * Return true if at least one cipher-suite is enabled. Used to determine - * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. - */ -static bool any_cipher_enabled(void) -{ - unsigned int i; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - PRInt32 policy = 0; - SSL_CipherPolicyGet(cipherlist[i].num, &policy); - if(policy) - return TRUE; - } - - return FALSE; -} - -/* - * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. - * - * returns 1 for a file - * returns 0 for not a file (NSS nickname) - */ -static int is_file(const char *filename) -{ - struct_stat st; - - if(!filename) - return 0; - - if(stat(filename, &st) == 0) - if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode)) - return 1; - - return 0; -} - -/* Check if the given string is filename or nickname of a certificate. If the - * given string is recognized as filename, return NULL. If the given string is - * recognized as nickname, return a duplicated string. The returned string - * should be later deallocated using free(). If the OOM failure occurs, we - * return NULL, too. - */ -static char *dup_nickname(struct Curl_easy *data, const char *str) -{ - const char *n; - - if(!is_file(str)) - /* no such file exists, use the string as nickname */ - return strdup(str); - - /* search the first slash; we require at least one slash in a file name */ - n = strchr(str, '/'); - if(!n) { - infof(data, "WARNING: certificate file name \"%s\" handled as nickname; " - "please use \"./%s\" to force file name", str, str); - return strdup(str); - } - - /* we'll use the PEM reader to read the certificate from file */ - return NULL; -} - -/* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition - * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN. For more - * details, go to <https://bugzilla.mozilla.org/1297397>. - */ -static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name) -{ - PK11SlotInfo *slot; - PR_Lock(nss_findslot_lock); - slot = PK11_FindSlotByName(slot_name); - PR_Unlock(nss_findslot_lock); - return slot; -} - -/* wrap 'ptr' as list node and tail-insert into 'list' */ -static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr) -{ - struct ptr_list_wrap *wrap = malloc(sizeof(*wrap)); - if(!wrap) - return CURLE_OUT_OF_MEMORY; - - wrap->ptr = ptr; - Curl_llist_insert_next(list, list->tail, wrap, &wrap->node); - return CURLE_OK; -} - -/* Call PK11_CreateGenericObject() with the given obj_class and filename. If - * the call succeeds, append the object handle to the list of objects so that - * the object can be destroyed in nss_close(). */ -static CURLcode nss_create_object(struct ssl_connect_data *connssl, - CK_OBJECT_CLASS obj_class, - const char *filename, bool cacert) -{ - PK11SlotInfo *slot; - PK11GenericObject *obj; - CK_BBOOL cktrue = CK_TRUE; - CK_BBOOL ckfalse = CK_FALSE; - CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; - int attr_cnt = 0; - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - const int slot_id = (cacert) ? 0 : 1; - char *slot_name = aprintf("PEM Token #%d", slot_id); - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - - DEBUGASSERT(backend); - - if(!slot_name) - return CURLE_OUT_OF_MEMORY; - - slot = nss_find_slot_by_name(slot_name); - free(slot_name); - if(!slot) - return result; - - PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); - PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); - PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, - (CK_ULONG)strlen(filename) + 1); - - if(CKO_CERTIFICATE == obj_class) { - CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); - PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); - } - - /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because - * PK11_DestroyGenericObject() does not release resources allocated by - * PK11_CreateGenericObject() early enough. */ - obj = -#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT - PK11_CreateManagedGenericObject -#else - PK11_CreateGenericObject -#endif - (slot, attrs, attr_cnt, PR_FALSE); - - PK11_FreeSlot(slot); - if(!obj) - return result; - - if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) { - PK11_DestroyGenericObject(obj); - return CURLE_OUT_OF_MEMORY; - } - - if(!cacert && CKO_CERTIFICATE == obj_class) - /* store reference to a client certificate */ - backend->obj_clicert = obj; - - return CURLE_OK; -} - -/* Destroy the NSS object whose handle is given by ptr. This function is - * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy - * NSS objects in nss_close() */ -static void nss_destroy_object(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr; - (void) user; - PK11_DestroyGenericObject(obj); - free(wrap); -} - -/* same as nss_destroy_object() but for CRL items */ -static void nss_destroy_crl_item(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - SECItem *crl_der = (SECItem *) wrap->ptr; - (void) user; - SECITEM_FreeItem(crl_der, PR_TRUE); - free(wrap); -} - -static CURLcode nss_load_cert(struct ssl_connect_data *ssl, - const char *filename, PRBool cacert) -{ - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - /* libnsspem.so leaks memory if the requested file does not exist. For more - * details, go to <https://bugzilla.redhat.com/734760>. */ - if(is_file(filename)) - result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); - - if(!result && !cacert) { - /* we have successfully loaded a client certificate */ - char *nickname = NULL; - char *n = strrchr(filename, '/'); - if(n) - n++; - - /* The following undocumented magic helps to avoid a SIGSEGV on call - * of PK11_ReadRawAttribute() from SelectClientCert() when using an - * immature version of libnsspem.so. For more details, go to - * <https://bugzilla.redhat.com/733685>. */ - nickname = aprintf("PEM Token #1:%s", n); - if(nickname) { - CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL); - if(cert) - CERT_DestroyCertificate(cert); - - free(nickname); - } - } - - return result; -} - -/* add given CRL to cache if it is not already there */ -static CURLcode nss_cache_crl(SECItem *crl_der) -{ - CERTCertDBHandle *db = CERT_GetDefaultCertDB(); - CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0); - if(crl) { - /* CRL already cached */ - SEC_DestroyCrl(crl); - SECITEM_FreeItem(crl_der, PR_TRUE); - return CURLE_OK; - } - - /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ - PR_Lock(nss_crllock); - - if(SECSuccess != CERT_CacheCRL(db, crl_der)) { - /* unable to cache CRL */ - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_SSL_CRL_BADFILE; - } - - /* store the CRL item so that we can free it in nss_cleanup() */ - if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { - if(SECSuccess == CERT_UncacheCRL(db, crl_der)) - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_OUT_OF_MEMORY; - } - - /* we need to clear session cache, so that the CRL could take effect */ - SSL_ClearSessionCache(); - PR_Unlock(nss_crllock); - return CURLE_OK; -} - -static CURLcode nss_load_crl(const char *crlfilename) -{ - PRFileDesc *infile; - PRFileInfo info; - SECItem filedata = { 0, NULL, 0 }; - SECItem *crl_der = NULL; - char *body; - - infile = PR_Open(crlfilename, PR_RDONLY, 0); - if(!infile) - return CURLE_SSL_CRL_BADFILE; - - if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) - goto fail; - - if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) - goto fail; - - if(info.size != PR_Read(infile, filedata.data, info.size)) - goto fail; - - crl_der = SECITEM_AllocItem(NULL, NULL, 0U); - if(!crl_der) - goto fail; - - /* place a trailing zero right after the visible data */ - body = (char *)filedata.data; - body[--filedata.len] = '\0'; - - body = strstr(body, "-----BEGIN"); - if(body) { - /* assume ASCII */ - char *trailer; - char *begin = PORT_Strchr(body, '\n'); - if(!begin) - begin = PORT_Strchr(body, '\r'); - if(!begin) - goto fail; - - trailer = strstr(++begin, "-----END"); - if(!trailer) - goto fail; - - /* retrieve DER from ASCII */ - *trailer = '\0'; - if(ATOB_ConvertAsciiToItem(crl_der, begin)) - goto fail; - - SECITEM_FreeItem(&filedata, PR_FALSE); - } - else - /* assume DER */ - *crl_der = filedata; - - PR_Close(infile); - return nss_cache_crl(crl_der); - -fail: - PR_Close(infile); - SECITEM_FreeItem(crl_der, PR_TRUE); - SECITEM_FreeItem(&filedata, PR_FALSE); - return CURLE_SSL_CRL_BADFILE; -} - -static CURLcode nss_load_key(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *key_file) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - PK11SlotInfo *slot, *tmp; - SECStatus status; - CURLcode result; - - (void)data; - result = nss_create_object(connssl, CKO_PRIVATE_KEY, key_file, FALSE); - if(result) { - PR_SetError(SEC_ERROR_BAD_KEY, 0); - return result; - } - - slot = nss_find_slot_by_name("PEM Token #1"); - if(!slot) - return CURLE_SSL_CERTPROBLEM; - - /* This will force the token to be seen as re-inserted */ - tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); - if(tmp) - PK11_FreeSlot(tmp); - if(!PK11_IsPresent(slot)) { - PK11_FreeSlot(slot); - return CURLE_SSL_CERTPROBLEM; - } - - status = PK11_Authenticate(slot, PR_TRUE, ssl_config->key_passwd); - PK11_FreeSlot(slot); - - return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; -} - -static int display_error(struct Curl_easy *data, PRInt32 err, - const char *filename) -{ - switch(err) { - case SEC_ERROR_BAD_PASSWORD: - failf(data, "Unable to load client key: Incorrect password"); - return 1; - case SEC_ERROR_UNKNOWN_CERT: - failf(data, "Unable to load certificate %s", filename); - return 1; - default: - break; - } - return 0; /* The caller will print a generic error */ -} - -static CURLcode cert_stuff(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *cert_file, char *key_file) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - - if(cert_file) { - result = nss_load_cert(connssl, cert_file, PR_FALSE); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, cert_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client cert: %d (%s)", err, err_name); - } - - return result; - } - } - - if(key_file || (is_file(cert_file))) { - if(key_file) - result = nss_load_key(cf, data, key_file); - else - /* In case the cert file also has the key */ - result = nss_load_key(cf, data, cert_file); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, key_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client key: %d (%s)", err, err_name); - } - - return result; - } - } - - return CURLE_OK; -} - -static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - (void)slot; /* unused */ - - if(retry || !arg) - return NULL; - else - return (char *)PORT_Strdup((char *)arg); -} - -/* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ -static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, - PRBool isServer) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = backend->data; - - DEBUGASSERT(data); -#ifdef SSL_ENABLE_OCSP_STAPLING - if(conn_config->verifystatus) { - SECStatus cacheResult; - - const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); - if(!csa) { - failf(data, "Invalid OCSP response"); - return SECFailure; - } - - if(csa->len == 0) { - failf(data, "No OCSP response received"); - return SECFailure; - } - - cacheResult = CERT_CacheOCSPResponseFromSideChannel( - CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), - PR_Now(), &csa->items[0], arg - ); - - if(cacheResult != SECSuccess) { - failf(data, "Invalid OCSP response"); - return cacheResult; - } - } -#endif - - if(!conn_config->verifypeer) { - infof(data, "skipping SSL peer certificate verification"); - return SECSuccess; - } - - return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); -} - -/** - * Inform the application that the handshake is complete. - */ -static void HandshakeCallback(PRFileDesc *sock, void *arg) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = backend->data; - unsigned int buflenmax = 50; - unsigned char buf[50]; - unsigned int buflen; - SSLNextProtoState state; - - DEBUGASSERT(data); - if(!connssl->alpn) { - return; - } - - if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { - - switch(state) { -#if NSSVERNUM >= 0x031a00 /* 3.26.0 */ - /* used by NSS internally to implement 0-RTT */ - case SSL_NEXT_PROTO_EARLY_VALUE: - /* fall through! */ -#endif - case SSL_NEXT_PROTO_NO_SUPPORT: - case SSL_NEXT_PROTO_NO_OVERLAP: - Curl_alpn_set_negotiated(cf, data, NULL, 0); - return; -#ifdef SSL_ENABLE_ALPN - case SSL_NEXT_PROTO_SELECTED: - Curl_alpn_set_negotiated(cf, data, buf, buflen); - break; -#endif - default: - /* ignore SSL_NEXT_PROTO_NEGOTIATED */ - break; - } - - } -} - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ -static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, - PRBool *canFalseStart) -{ - struct Curl_easy *data = (struct Curl_easy *)client_data; - - SSLChannelInfo channelInfo; - SSLCipherSuiteInfo cipherInfo; - - SECStatus rv; - PRBool negotiatedExtension; - - *canFalseStart = PR_FALSE; - - if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) - return SECFailure; - - if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, - sizeof(cipherInfo)) != SECSuccess) - return SECFailure; - - /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for - * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 - */ - if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) - goto end; - - /* Only allow ECDHE key exchange algorithm. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ - if(cipherInfo.keaType != ssl_kea_ecdh) - goto end; - - /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC - * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt - * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ - if(cipherInfo.symCipher != ssl_calg_aes_gcm) - goto end; - - /* Enforce ALPN to do False Start, as an indicator of server - compatibility. */ - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, - &negotiatedExtension); - if(rv != SECSuccess || !negotiatedExtension) { - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, - &negotiatedExtension); - } - - if(rv != SECSuccess || !negotiatedExtension) - goto end; - - *canFalseStart = PR_TRUE; - - infof(data, "Trying TLS False Start"); - -end: - return SECSuccess; -} -#endif - -static void display_cert_info(struct Curl_easy *data, - CERTCertificate *cert) -{ - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; - - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(data, "subject: %s", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " start date: %s", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " expire date: %s", timeString); - infof(data, " common name: %s", common_name); - infof(data, " issuer: %s", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); -} - -/* A number of certs that will never occur in a real server handshake */ -#define TOO_MANY_CERTS 300 - -static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock) -{ - CURLcode result = CURLE_OK; - SSLChannelInfo channel; - SSLCipherSuiteInfo suite; - CERTCertificate *cert; - CERTCertificate *cert2; - CERTCertificate *cert3; - PRTime now; - - if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) == - SECSuccess && channel.length == sizeof(channel) && - channel.cipherSuite) { - if(SSL_GetCipherSuiteInfo(channel.cipherSuite, - &suite, sizeof(suite)) == SECSuccess) { - infof(data, "SSL connection using %s", suite.cipherSuiteName); - } - } - - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - - if(!data->set.ssl.certinfo) { - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - else { - /* Count certificates in chain. */ - int i = 1; - now = PR_Now(); - if(!cert->isRoot) { - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - while(cert2) { - i++; - if(i >= TOO_MANY_CERTS) { - CERT_DestroyCertificate(cert2); - failf(data, "certificate loop"); - return CURLE_SSL_CERTPROBLEM; - } - if(cert2->isRoot) { - CERT_DestroyCertificate(cert2); - break; - } - cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); - CERT_DestroyCertificate(cert2); - cert2 = cert3; - } - } - - result = Curl_ssl_init_certinfo(data, i); - if(!result) { - for(i = 0; cert; cert = cert2) { - result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data, - (char *)cert->derCert.data + - cert->derCert.len); - if(result) - break; - - if(cert->isRoot) { - CERT_DestroyCertificate(cert); - break; - } - - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - CERT_DestroyCertificate(cert); - } - } - } - } - - return result; -} - -static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -{ - struct Curl_cfilter *cf = (struct Curl_cfilter *)arg; - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = backend->data; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config; - PRErrorCode err = PR_GetError(); - CERTCertificate *cert; - - DEBUGASSERT(data); - ssl_config = Curl_ssl_cf_get_config(cf, data); - /* remember the cert verification result */ - ssl_config->certverifyresult = err; - - if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn_config->verifyhost) - /* we are asked not to verify the host name */ - return SECSuccess; - - /* print only info about the cert, the error is printed off the callback */ - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - - return SECFailure; -} - -/** - * - * Check that the Peer certificate's issuer certificate matches the one found - * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the - * issuer check, so we provide comments that mimic the OpenSSL - * X509_check_issued function (in x509v3/v3_purp.c) - */ -static SECStatus check_issuer_cert(PRFileDesc *sock, - char *issuer_nickname) -{ - CERTCertificate *cert, *cert_issuer, *issuer; - SECStatus res = SECSuccess; - void *proto_win = NULL; - - cert = SSL_PeerCertificate(sock); - cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); - - proto_win = SSL_RevealPinArg(sock); - issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); - - if((!cert_issuer) || (!issuer)) - res = SECFailure; - else if(SECITEM_CompareItem(&cert_issuer->derCert, - &issuer->derCert) != SECEqual) - res = SECFailure; - - CERT_DestroyCertificate(cert); - CERT_DestroyCertificate(issuer); - CERT_DestroyCertificate(cert_issuer); - return res; -} - -static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, - const char *pinnedpubkey) -{ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = NULL; - CERTCertificate *cert; - - DEBUGASSERT(backend); - data = backend->data; - - if(!pinnedpubkey) - /* no pinned public key specified */ - return CURLE_OK; - - /* get peer certificate */ - cert = SSL_PeerCertificate(backend->handle); - if(cert) { - /* extract public key from peer certificate */ - SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); - if(pubkey) { - /* encode the public key as DER */ - SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); - if(cert_der) { - /* compare the public key with the pinned public key */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data, - cert_der->len); - SECITEM_FreeItem(cert_der, PR_TRUE); - } - SECKEY_DestroyPublicKey(pubkey); - } - CERT_DestroyCertificate(cert); - } - - /* report the resulting status */ - switch(result) { - case CURLE_OK: - infof(data, "pinned public key verified successfully"); - break; - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: - failf(data, "failed to verify pinned public key"); - break; - default: - /* OOM, etc. */ - break; - } - - return result; -} - -/** - * - * Callback to pick the SSL client certificate. - */ -static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, - struct CERTDistNamesStr *caNames, - struct CERTCertificateStr **pRetCert, - struct SECKEYPrivateKeyStr **pRetKey) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = NULL; - const char *nickname = NULL; - static const char pem_slotname[] = "PEM Token #1"; - - DEBUGASSERT(backend); - - data = backend->data; - nickname = backend->client_nickname; - - if(backend->obj_clicert) { - /* use the cert/key provided by PEM reader */ - SECItem cert_der = { 0, NULL, 0 }; - void *proto_win = SSL_RevealPinArg(sock); - struct CERTCertificateStr *cert; - struct SECKEYPrivateKeyStr *key; - - PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); - if(!slot) { - failf(data, "NSS: PK11 slot not found: %s", pem_slotname); - return SECFailure; - } - - if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE, - &cert_der) != SECSuccess) { - failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); - PK11_FreeSlot(slot); - return SECFailure; - } - - cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); - SECITEM_FreeItem(&cert_der, PR_FALSE); - if(!cert) { - failf(data, "NSS: client certificate from file not found"); - PK11_FreeSlot(slot); - return SECFailure; - } - - key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); - PK11_FreeSlot(slot); - if(!key) { - failf(data, "NSS: private key from file not found"); - CERT_DestroyCertificate(cert); - return SECFailure; - } - - infof(data, "NSS: client certificate from file"); - display_cert_info(data, cert); - - *pRetCert = cert; - *pRetKey = key; - return SECSuccess; - } - - /* use the default NSS hook */ - if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, - pRetCert, pRetKey) - || !*pRetCert) { - - if(!nickname) - failf(data, "NSS: client certificate not found (nickname not " - "specified)"); - else - failf(data, "NSS: client certificate not found: %s", nickname); - - return SECFailure; - } - - /* get certificate nickname if any */ - nickname = (*pRetCert)->nickname; - if(!nickname) - nickname = "[unknown]"; - - if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { - failf(data, "NSS: refusing previously loaded certificate from file: %s", - nickname); - return SECFailure; - } - - if(!*pRetKey) { - failf(data, "NSS: private key not found for certificate: %s", nickname); - return SECFailure; - } - - infof(data, "NSS: using client certificate: %s", nickname); - display_cert_info(data, *pRetCert); - return SECSuccess; -} - -/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ -static void nss_update_connecting_state(ssl_connect_state state, void *secret) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret; - if(PR_GetError() != PR_WOULD_BLOCK_ERROR) - /* an unrelated error is passing by */ - return; - - switch(connssl->connecting_state) { - case ssl_connect_2: - case ssl_connect_2_reading: - case ssl_connect_2_writing: - break; - default: - /* we are not called from an SSL handshake */ - return; - } - - /* update the state accordingly */ - connssl->connecting_state = state; -} - -/* recv() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRRecvFN recv_fn = fd->lower->methods->recv; - const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_reading, fd->secret); - return rv; -} - -/* send() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRSendFN send_fn = fd->lower->methods->send; - const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_writing, fd->secret); - return rv; -} - -/* close() wrapper to avoid assertion failure due to fd->secret != NULL */ -static PRStatus nspr_io_close(PRFileDesc *fd) -{ - const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close; - fd->secret = NULL; - return close_fn(fd); -} - -/* load a PKCS #11 module */ -static CURLcode nss_load_module(SECMODModule **pmod, const char *library, - const char *name) -{ - char *config_string; - SECMODModule *module = *pmod; - if(module) - /* already loaded */ - return CURLE_OK; - - config_string = aprintf("library=%s name=%s", library, name); - if(!config_string) - return CURLE_OUT_OF_MEMORY; - - module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); - free(config_string); - - if(module && module->loaded) { - /* loaded successfully */ - *pmod = module; - return CURLE_OK; - } - - if(module) - SECMOD_DestroyModule(module); - return CURLE_FAILED_INIT; -} - -/* unload a PKCS #11 module */ -static void nss_unload_module(SECMODModule **pmod) -{ - SECMODModule *module = *pmod; - if(!module) - /* not loaded */ - return; - - if(SECMOD_UnloadUserModule(module) != SECSuccess) - /* unload failed */ - return; - - SECMOD_DestroyModule(module); - *pmod = NULL; -} - -/* data might be NULL */ -static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) -{ - NSSInitParameters initparams; - PRErrorCode err; - const char *err_name; - - if(nss_context) - return CURLE_OK; - - memset((void *) &initparams, '\0', sizeof(initparams)); - initparams.length = sizeof(initparams); - - if(cert_dir) { - char *certpath = aprintf("sql:%s", cert_dir); - if(!certpath) - return CURLE_OUT_OF_MEMORY; - - infof(data, "Initializing NSS with certpath: %s", certpath); - nss_context = NSS_InitContext(certpath, "", "", "", &initparams, - NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); - free(certpath); - - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name); - } - - infof(data, "Initializing NSS with certpath: none"); - nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY - | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - failf(data, "Unable to initialize NSS: %d (%s)", err, err_name); - return CURLE_SSL_CACERT_BADFILE; -} - -/* data might be NULL */ -static CURLcode nss_setup(struct Curl_easy *data) -{ - char *cert_dir; - struct_stat st; - CURLcode result; - - if(initialized) - return CURLE_OK; - - /* list of all CRL items we need to destroy in nss_cleanup() */ - Curl_llist_init(&nss_crl_list, nss_destroy_crl_item); - - /* First we check if $SSL_DIR points to a valid dir */ - cert_dir = getenv("SSL_DIR"); - if(cert_dir) { - if((stat(cert_dir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - cert_dir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!cert_dir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - cert_dir = (char *)SSL_DIR; - } - } - - if(nspr_io_identity == PR_INVALID_IO_LAYER) { - /* allocate an identity for our own NSPR I/O layer */ - nspr_io_identity = PR_GetUniqueIdentity("libcurl"); - if(nspr_io_identity == PR_INVALID_IO_LAYER) - return CURLE_OUT_OF_MEMORY; - - /* the default methods just call down to the lower I/O layer */ - memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(), - sizeof(nspr_io_methods)); - - /* override certain methods in the table by our wrappers */ - nspr_io_methods.recv = nspr_io_recv; - nspr_io_methods.send = nspr_io_send; - nspr_io_methods.close = nspr_io_close; - } - - result = nss_init_core(data, cert_dir); - if(result) - return result; - - if(!any_cipher_enabled()) - NSS_SetDomesticPolicy(); - - initialized = 1; - - return CURLE_OK; -} - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -static int nss_init(void) -{ - /* curl_global_init() is not thread-safe so this test is ok */ - if(!nss_initlock) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); - nss_initlock = PR_NewLock(); - nss_crllock = PR_NewLock(); - nss_findslot_lock = PR_NewLock(); - nss_trustload_lock = PR_NewLock(); - } - - /* We will actually initialize NSS later */ - - return 1; -} - -/* data might be NULL */ -CURLcode Curl_nss_force_init(struct Curl_easy *data) -{ - CURLcode result; - if(!nss_initlock) { - if(data) - failf(data, "unable to initialize NSS, curl_global_init() should have " - "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); - return CURLE_FAILED_INIT; - } - - PR_Lock(nss_initlock); - result = nss_setup(data); - PR_Unlock(nss_initlock); - - return result; -} - -/* Global cleanup */ -static void nss_cleanup(void) -{ - /* This function isn't required to be threadsafe and this is only done - * as a safety feature. - */ - PR_Lock(nss_initlock); - if(initialized) { - /* Free references to client certificates held in the SSL session cache. - * Omitting this hampers destruction of the security module owning - * the certificates. */ - SSL_ClearSessionCache(); - - nss_unload_module(&pem_module); - nss_unload_module(&trust_module); - NSS_ShutdownContext(nss_context); - nss_context = NULL; - } - - /* destroy all CRL items */ - Curl_llist_destroy(&nss_crl_list, NULL); - - PR_Unlock(nss_initlock); - - PR_DestroyLock(nss_initlock); - PR_DestroyLock(nss_crllock); - PR_DestroyLock(nss_findslot_lock); - PR_DestroyLock(nss_trustload_lock); - nss_initlock = NULL; - - initialized = 0; -} - -static void close_one(struct ssl_connect_data *connssl) -{ - /* before the cleanup, check whether we are using a client certificate */ - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - bool client_cert = true; - - DEBUGASSERT(backend); - - client_cert = (backend->client_nickname != NULL) - || (backend->obj_clicert != NULL); - - 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)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0, - PR_INTERVAL_NO_WAIT); - } - - free(backend->client_nickname); - backend->client_nickname = NULL; - - /* destroy all NSS objects in order to avoid failure of NSS shutdown */ - Curl_llist_destroy(&backend->obj_list, NULL); - backend->obj_clicert = NULL; - - if(backend->handle) { - if(client_cert) - /* A server might require different authentication based on the - * particular path being requested by the client. To support this - * scenario, we must ensure that a connection will never reuse the - * authentication data from a previous connection. */ - SSL_InvalidateSession(backend->handle); - - PR_Close(backend->handle); - backend->handle = NULL; - } -} - -/* - * This function is called when an SSL connection is closed. - */ -static void nss_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - (void)data; - DEBUGASSERT(backend); - - if(backend->handle) { - /* NSS closes the socket we previously handed to it, so we must mark it - as closed to avoid double close */ - fake_sclose(cf->conn->sock[cf->sockindex]); - cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; - } - - close_one(connssl); -} - -/* return true if NSS can provide error code (and possibly msg) for the - error */ -static bool is_nss_error(CURLcode err) -{ - switch(err) { - case CURLE_PEER_FAILED_VERIFICATION: - case CURLE_SSL_CERTPROBLEM: - case CURLE_SSL_CONNECT_ERROR: - case CURLE_SSL_ISSUER_ERROR: - return true; - - default: - return false; - } -} - -/* return true if the given error code is related to a client certificate */ -static bool is_cc_error(PRInt32 err) -{ - switch(err) { - case SSL_ERROR_BAD_CERT_ALERT: - case SSL_ERROR_EXPIRED_CERT_ALERT: - case SSL_ERROR_REVOKED_CERT_ALERT: - return true; - - default: - return false; - } -} - -static CURLcode nss_load_ca_certificates(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); - const char *cafile = conn_config->CAfile; - const char *capath = conn_config->CApath; - bool use_trust_module; - CURLcode result = CURLE_OK; - - /* treat empty string as unset */ - if(cafile && !cafile[0]) - cafile = NULL; - if(capath && !capath[0]) - capath = NULL; - - infof(data, " CAfile: %s", cafile ? cafile : "none"); - infof(data, " CApath: %s", capath ? capath : "none"); - - /* load libnssckbi.so if no other trust roots were specified */ - use_trust_module = !cafile && !capath; - - PR_Lock(nss_trustload_lock); - if(use_trust_module && !trust_module) { - /* libnssckbi.so needed but not yet loaded --> load it! */ - result = nss_load_module(&trust_module, trust_library, "trust"); - infof(data, "%s %s", (result) ? "failed to load" : "loaded", - trust_library); - if(result == CURLE_FAILED_INIT) - /* If libnssckbi.so is not available (or fails to load), one can still - use CA certificates stored in NSS database. Ignore the failure. */ - result = CURLE_OK; - } - else if(!use_trust_module && trust_module) { - /* libnssckbi.so not needed but already loaded --> unload it! */ - infof(data, "unloading %s", trust_library); - nss_unload_module(&trust_module); - } - PR_Unlock(nss_trustload_lock); - - if(cafile) - result = nss_load_cert(connssl, cafile, PR_TRUE); - - if(result) - return result; - - if(capath) { - struct_stat st; - if(stat(capath, &st) == -1) - return CURLE_SSL_CACERT_BADFILE; - - if(S_ISDIR(st.st_mode)) { - PRDirEntry *entry; - PRDir *dir = PR_OpenDir(capath); - if(!dir) - return CURLE_SSL_CACERT_BADFILE; - - while((entry = - PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) { - char *fullpath = aprintf("%s/%s", capath, entry->name); - if(!fullpath) { - PR_CloseDir(dir); - return CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK != nss_load_cert(connssl, fullpath, PR_TRUE)) - /* This is purposefully tolerant of errors so non-PEM files can - * be in the same directory */ - infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath); - - free(fullpath); - } - - PR_CloseDir(dir); - } - else - infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath); - } - - return CURLE_OK; -} - -static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version) -{ - switch(version) { - case CURL_SSLVERSION_SSLv2: - *nssver = SSL_LIBRARY_VERSION_2; - return CURLE_OK; - - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; - - case CURL_SSLVERSION_TLSv1_0: - *nssver = SSL_LIBRARY_VERSION_TLS_1_0; - return CURLE_OK; - - case CURL_SSLVERSION_TLSv1_1: -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - *nssver = SSL_LIBRARY_VERSION_TLS_1_1; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_2: -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - *nssver = SSL_LIBRARY_VERSION_TLS_1_2; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_3: -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - *nssver = SSL_LIBRARY_VERSION_TLS_1_3; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - default: - return CURLE_SSL_CONNECT_ERROR; - } -} - -static CURLcode nss_init_sslver(SSLVersionRange *sslver, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - CURLcode result; - const long min = conn_config->version; - const long max = conn_config->version_max; - SSLVersionRange vrange; - - switch(min) { - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_DEFAULT: - /* Bump our minimum TLS version if NSS has stricter requirements. */ - if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) - return CURLE_SSL_CONNECT_ERROR; - if(sslver->min < vrange.min) - sslver->min = vrange.min; - break; - default: - result = nss_sslver_from_curl(&sslver->min, min); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - switch(max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - break; - default: - result = nss_sslver_from_curl(&sslver->max, max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - return CURLE_OK; -} - -static CURLcode nss_fail_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode curlerr) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - - DEBUGASSERT(backend); - - if(is_nss_error(curlerr)) { - /* read NSPR error code */ - PRErrorCode err = PR_GetError(); - if(is_cc_error(err)) - curlerr = CURLE_SSL_CERTPROBLEM; - - /* print the error number and error string */ - infof(data, "NSS error %d (%s)", err, nss_error_to_name(err)); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - } - - /* cleanup on connection failure */ - Curl_llist_destroy(&backend->obj_list, NULL); - - return curlerr; -} - -/* Switch the SSL socket into blocking or non-blocking mode. */ -static CURLcode nss_set_blocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool blocking) -{ - struct ssl_connect_data *connssl = cf->ctx; - PRSocketOptionData sock_opt; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - - DEBUGASSERT(backend); - - sock_opt.option = PR_SockOpt_Nonblocking; - sock_opt.value.non_blocking = !blocking; - - if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS) - return nss_fail_connect(cf, data, CURLE_SSL_CONNECT_ERROR); - - return CURLE_OK; -} - -static CURLcode nss_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - PRFileDesc *model = NULL; - PRFileDesc *nspr_io = NULL; - PRFileDesc *nspr_io_stub = NULL; - PRBool ssl_no_cache; - PRBool ssl_cbc_random_iv; - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - 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 Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); - struct ssl_connect_data *connssl_next = cf_ssl_next? - cf_ssl_next->ctx : NULL; - CURLcode result; - bool second_layer = FALSE; - SSLVersionRange sslver_supported; - SSLVersionRange sslver = { - SSL_LIBRARY_VERSION_TLS_1_0, /* min */ -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - SSL_LIBRARY_VERSION_TLS_1_3 /* max */ -#elif defined SSL_LIBRARY_VERSION_TLS_1_2 - SSL_LIBRARY_VERSION_TLS_1_2 -#elif defined SSL_LIBRARY_VERSION_TLS_1_1 - SSL_LIBRARY_VERSION_TLS_1_1 -#else - SSL_LIBRARY_VERSION_TLS_1_0 -#endif - }; - const char *hostname = connssl->hostname; - char *snihost; - - snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGASSERT(backend); - - backend->data = data; - - /* list of all NSS objects we need to destroy in nss_do_close() */ - Curl_llist_init(&backend->obj_list, nss_destroy_object); - - PR_Lock(nss_initlock); - result = nss_setup(data); - if(result) { - PR_Unlock(nss_initlock); - goto error; - } - - PK11_SetPasswordFunc(nss_get_password); - - result = nss_load_module(&pem_module, pem_library, "PEM"); - PR_Unlock(nss_initlock); - if(result == CURLE_FAILED_INIT) - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.", pem_library); - else if(result) - goto error; - - result = CURLE_SSL_CONNECT_ERROR; - - model = PR_NewTCPSocket(); - if(!model) - goto error; - model = SSL_ImportFD(NULL, model); - - if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) - goto error; - - /* do not use SSL cache if disabled or we are not going to verify peer */ - ssl_no_cache = (ssl_config->primary.sessionid - && conn_config->verifypeer) ? PR_FALSE : PR_TRUE; - if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) - goto error; - - /* enable/disable the requested SSL version(s) */ - if(nss_init_sslver(&sslver, cf, data) != CURLE_OK) - goto error; - if(SSL_VersionRangeGetSupported(ssl_variant_stream, - &sslver_supported) != SECSuccess) - goto error; - if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { - char *sslver_req_str, *sslver_supp_str; - sslver_req_str = nss_sslver_to_name(sslver.max); - sslver_supp_str = nss_sslver_to_name(sslver_supported.max); - if(sslver_req_str && sslver_supp_str) - infof(data, "Falling back from %s to max supported SSL version (%s)", - sslver_req_str, sslver_supp_str); - free(sslver_req_str); - free(sslver_supp_str); - sslver.max = sslver_supported.max; - } - if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) - goto error; - - ssl_cbc_random_iv = !ssl_config->enable_beast; -#ifdef SSL_CBC_RANDOM_IV - /* unless the user explicitly asks to allow the protocol vulnerability, we - use the work-around */ - if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) - infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", - ssl_cbc_random_iv); -#else - if(ssl_cbc_random_iv) - infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); -#endif - - if(conn_config->cipher_list) { - if(set_ciphers(data, model, conn_config->cipher_list) != SECSuccess) { - result = CURLE_SSL_CIPHER; - goto error; - } - } - - if(!conn_config->verifypeer && conn_config->verifyhost) - infof(data, "WARNING: ignoring value of ssl.verifyhost"); - - /* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ - if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, cf) != SECSuccess) - goto error; - - /* not checked yet */ - ssl_config->certverifyresult = 0; - - if(SSL_BadCertHook(model, BadCertHandler, cf) != SECSuccess) - goto error; - - if(SSL_HandshakeCallback(model, HandshakeCallback, cf) != SECSuccess) - goto error; - - { - const CURLcode rv = nss_load_ca_certificates(cf, data); - if((rv == CURLE_SSL_CACERT_BADFILE) && !conn_config->verifypeer) - /* not a fatal error because we are not going to verify the peer */ - infof(data, "WARNING: CA certificates failed to load"); - else if(rv) { - result = rv; - goto error; - } - } - - if(ssl_config->primary.CRLfile) { - const CURLcode rv = nss_load_crl(ssl_config->primary.CRLfile); - if(rv) { - result = rv; - goto error; - } - infof(data, " CRLfile: %s", ssl_config->primary.CRLfile); - } - - if(ssl_config->primary.clientcert) { - char *nickname = dup_nickname(data, ssl_config->primary.clientcert); - if(nickname) { - /* we are not going to use libnsspem.so to read the client cert */ - backend->obj_clicert = NULL; - } - else { - CURLcode rv = cert_stuff(cf, data, - ssl_config->primary.clientcert, - ssl_config->key); - if(rv) { - /* failf() is already done in cert_stuff() */ - result = rv; - goto error; - } - } - - /* store the nickname for SelectClientCert() called during handshake */ - backend->client_nickname = nickname; - } - else - backend->client_nickname = NULL; - - if(SSL_GetClientAuthDataHook(model, SelectClientCert, - (void *)connssl) != SECSuccess) { - result = CURLE_SSL_CERTPROBLEM; - goto error; - } - - /* Is there an SSL filter "in front" of us or are we writing directly - * to the socket? */ - if(connssl_next) { - struct nss_ssl_backend_data *backend_next = - (struct nss_ssl_backend_data *)connssl_next->backend; - /* The filter should be connected by now, with full handshake */ - DEBUGASSERT(backend_next->handle); - DEBUGASSERT(ssl_connection_complete == connssl_next->state); - /* We tell our NSS instance to use do IO with the 'next' NSS - * instance. This NSS instance will take ownership of the next - * one, including its destruction. We therefore need to `disown` - * the next filter's handle, once import succeeds. */ - nspr_io = backend->handle; - second_layer = TRUE; - } - else { - /* wrap OS file descriptor by NSPR's file descriptor abstraction */ - nspr_io = PR_ImportTCPSocket(sockfd); - if(!nspr_io) - goto error; - } - - /* create our own NSPR I/O layer */ - nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); - if(!nspr_io_stub) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - /* make the per-connection data accessible from NSPR I/O callbacks */ - nspr_io_stub->secret = (void *)connssl; - - /* push our new layer to the NSPR I/O stack */ - if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { - if(!second_layer) - PR_Close(nspr_io); - PR_Close(nspr_io_stub); - goto error; - } - - /* import our model socket onto the current I/O stack */ - backend->handle = SSL_ImportFD(model, nspr_io); - if(!backend->handle) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - PR_Close(model); /* We don't need this any more */ - model = NULL; - if(connssl_next) { /* steal the NSS handle we just imported successfully */ - struct nss_ssl_backend_data *backend_next = - (struct nss_ssl_backend_data *)connssl_next->backend; - backend_next->handle = NULL; - } - - /* This is the password associated with the cert that we're using */ - if(ssl_config->key_passwd) { - SSL_SetPKCS11PinArg(backend->handle, ssl_config->key_passwd); - } - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(conn_config->verifystatus) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) - != SECSuccess) - goto error; - } -#endif - -#ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, - connssl->alpn ? PR_TRUE : PR_FALSE) - != SECSuccess) - goto error; -#endif - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - if(data->set.ssl.falsestart) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) - != SECSuccess) - goto error; - - if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, - data) != SECSuccess) - goto error; - } -#endif - -#if defined(SSL_ENABLE_ALPN) - if(connssl->alpn) { - struct alpn_proto_buf proto; - - result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); - if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len) - != SECSuccess) { - failf(data, "Error setting ALPN"); - goto error; - } - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); - } -#endif - - - /* Force handshake on next I/O */ - if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) - != SECSuccess) - goto error; - - /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, snihost) != SECSuccess) - goto error; - - /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) - goto error; - - return CURLE_OK; - -error: - if(model) - PR_Close(model); - - return nss_fail_connect(cf, data, result); -} - -static CURLcode nss_do_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - 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_SSL_CONNECT_ERROR; - PRUint32 timeout; - - /* check timeout situation */ - const timediff_t time_left = Curl_timeleft(data, NULL, TRUE); - if(time_left < 0) { - failf(data, "timed out before SSL handshake"); - result = CURLE_OPERATION_TIMEDOUT; - goto error; - } - - DEBUGASSERT(backend); - - /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32) time_left); - if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { - if(PR_GetError() == PR_WOULD_BLOCK_ERROR) - /* blocking direction is updated by nss_update_connecting_state() */ - return CURLE_AGAIN; - else if(ssl_config->certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) - result = CURLE_PEER_FAILED_VERIFICATION; - else if(ssl_config->certverifyresult) - result = CURLE_PEER_FAILED_VERIFICATION; - goto error; - } - - result = display_conn_info(data, backend->handle); - if(result) - goto error; - - if(conn_config->issuercert) { - SECStatus ret = SECFailure; - char *nickname = dup_nickname(data, conn_config->issuercert); - if(nickname) { - /* we support only nicknames in case of issuercert for now */ - ret = check_issuer_cert(backend->handle, nickname); - free(nickname); - } - - if(SECFailure == ret) { - infof(data, "SSL certificate issuer check failed"); - result = CURLE_SSL_ISSUER_ERROR; - goto error; - } - else { - infof(data, "SSL certificate issuer check ok"); - } - } - - result = cmp_peer_pubkey(connssl, Curl_ssl_cf_is_proxy(cf)? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) - /* status already printed */ - goto error; - - return CURLE_OK; - -error: - return nss_fail_connect(cf, data, result); -} - -static CURLcode nss_connect_common(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - const bool blocking = (done == NULL); - CURLcode result; - - if(connssl->state == ssl_connection_complete) { - if(!blocking) - *done = TRUE; - return CURLE_OK; - } - - if(connssl->connecting_state == ssl_connect_1) { - result = nss_setup_connect(cf, data); - if(result) - /* we do not expect CURLE_AGAIN from nss_setup_connect() */ - return result; - - connssl->connecting_state = ssl_connect_2; - } - - /* enable/disable blocking mode before handshake */ - result = nss_set_blocking(cf, data, blocking); - if(result) - return result; - - result = nss_do_connect(cf, data); - switch(result) { - case CURLE_OK: - break; - case CURLE_AGAIN: - /* CURLE_AGAIN in non-blocking mode is not an error */ - if(!blocking) - return CURLE_OK; - else - return result; - default: - return result; - } - - if(blocking) { - /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ - result = nss_set_blocking(cf, data, /* blocking */ FALSE); - if(result) - return result; - } - else - /* signal completed SSL handshake */ - *done = TRUE; - - connssl->state = ssl_connection_complete; - - /* ssl_connect_done is never used outside, go back to the initial state */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode nss_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - return nss_connect_common(cf, data, /* blocking */ NULL); -} - -static CURLcode nss_connect_nonblocking(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - return nss_connect_common(cf, data, done); -} - -static ssize_t nss_send(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - ssize_t rc; - - (void)data; - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); - if(rc < 0) { - PRInt32 err = PR_GetError(); - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL write: error %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_SEND_ERROR; - } - - return -1; - } - - return rc; /* number of bytes */ -} - -static bool -nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - PRFileDesc *fd = backend->handle->lower; - char buf; - - (void) data; - - /* Returns true in case of error to force reading. */ - return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0; -} - -static ssize_t nss_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - ssize_t nread; - - (void)data; - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - nread = PR_Recv(backend->handle, buf, (int)buffersize, 0, - PR_INTERVAL_NO_WAIT); - if(nread < 0) { - /* failed SSL read */ - PRInt32 err = PR_GetError(); - - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL read: errno %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_RECV_ERROR; - } - - return -1; - } - - return nread; -} - -static size_t nss_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion()); -} - -/* data might be NULL */ -static int Curl_nss_seed(struct Curl_easy *data) -{ - /* make sure that NSS is initialized */ - return !!Curl_nss_force_init(data); -} - -/* data might be NULL */ -static CURLcode nss_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) -{ - Curl_nss_seed(data); /* Initiate the seed if not already done */ - - if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) - /* signal a failure */ - return CURLE_FAILED_INIT; - - return CURLE_OK; -} - -static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); - unsigned int SHA256out; - - if(!SHA256pw) - return CURLE_NOT_BUILT_IN; - - PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); - PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); - PK11_DestroyContext(SHA256pw, PR_TRUE); - - return CURLE_OK; -} - -static bool nss_cert_status_request(void) -{ -#ifdef SSL_ENABLE_OCSP_STAPLING - return TRUE; -#else - return FALSE; -#endif -} - -static bool nss_false_start(void) -{ -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - return TRUE; -#else - return FALSE; -#endif -} - -static void *nss_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->handle; -} - -static bool nss_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - - if(!backend->data) - backend->data = data; - return TRUE; -} - -static void nss_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct nss_ssl_backend_data *backend = - (struct nss_ssl_backend_data *)connssl->backend; - - if(backend->data == data) - backend->data = NULL; -} - -const struct Curl_ssl Curl_ssl_nss = { - { CURLSSLBACKEND_NSS, "nss" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct nss_ssl_backend_data), - - nss_init, /* init */ - nss_cleanup, /* cleanup */ - nss_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - /* NSS has no shutdown function provided and thus always fail */ - Curl_none_shutdown, /* shutdown */ - nss_data_pending, /* data_pending */ - nss_random, /* random */ - nss_cert_status_request, /* cert_status_request */ - nss_connect, /* connect */ - nss_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_get_select_socks, /* getsock */ - nss_get_internals, /* get_internals */ - nss_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* NSS has its own session ID cache */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - nss_false_start, /* false_start */ - nss_sha256sum, /* sha256sum */ - nss_attach_data, /* associate_connection */ - nss_detach_data, /* disassociate_connection */ - NULL, /* free_multi_ssl_backend_data */ - nss_recv, /* recv decrypted data */ - nss_send, /* send data to encrypt */ -}; - -#endif /* USE_NSS */ diff --git a/Utilities/cmcurl/lib/vtls/nssg.h b/Utilities/cmcurl/lib/vtls/nssg.h deleted file mode 100644 index ad7eef5..0000000 --- a/Utilities/cmcurl/lib/vtls/nssg.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef HEADER_CURL_NSSG_H -#define HEADER_CURL_NSSG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#ifdef USE_NSS -/* - * This header should only be needed to get included by vtls.c and nss.c - */ - -#include "urldata.h" - -/* initialize NSS library if not already */ -CURLcode Curl_nss_force_init(struct Curl_easy *data); - -extern const struct Curl_ssl Curl_ssl_nss; - -#endif /* USE_NSS */ -#endif /* HEADER_CURL_NSSG_H */ diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index e6dd398..ee3a714 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -190,11 +190,12 @@ * Whether SSL_CTX_set_keylog_callback is available. * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) - * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it - * lies and pretends to be OpenSSL 2.0.0). + * LibreSSL: supported since 3.5.0 (released 2022-02-24) */ #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3050000fL) || \ defined(OPENSSL_IS_BORINGSSL) #define HAVE_KEYLOG_CALLBACK #endif @@ -202,11 +203,13 @@ /* Whether SSL_CTX_set_ciphersuites is available. * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no - * LibreSSL: no + * LibreSSL: supported since 3.4.1 (released 2021-10-14) */ -#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL)) +#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \ + !defined(OPENSSL_IS_BORINGSSL) #define HAVE_SSL_CTX_SET_CIPHERSUITES #if !defined(OPENSSL_IS_AWSLC) #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH @@ -268,7 +271,7 @@ #define HAVE_OPENSSL_VERSION #endif -#ifdef OPENSSL_IS_BORINGSSL +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) typedef uint32_t sslerr_t; #else typedef unsigned long sslerr_t; @@ -730,8 +733,8 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen) DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", - blen, (int)nwritten, result)); + CURL_TRC_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", + blen, (int)nwritten, result); BIO_clear_retry_flags(bio); backend->io_result = result; if(nwritten < 0) { @@ -757,8 +760,8 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", - blen, (int)nread, result)); + CURL_TRC_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", + blen, (int)nread, result); BIO_clear_retry_flags(bio); backend->io_result = result; if(nread < 0) { @@ -1004,20 +1007,6 @@ static CURLcode ossl_seed(struct Curl_easy *data) return CURLE_OK; #endif -#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET) - /* available in OpenSSL 0.9.5 and later */ - /* EGD_SOCKET is set at configure time or not at all */ - { - /* If there's an option and a define, the option overrides the - define */ - int ret = RAND_egd(EGD_SOCKET); - if(-1 != ret) { - if(rand_enough()) - return CURLE_OK; - } - } -#endif - /* fallback to a custom seeding of the PRNG using a hash based on a current time */ do { @@ -1713,7 +1702,7 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) static int ossl_init(void) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) + (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL) const uint64_t flags = #ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN /* not present in BoringSSL */ @@ -1903,6 +1892,9 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); (void)SSL_shutdown(backend->handle); + + ERR_clear_error(); + SSL_set_connect_state(backend->handle); } @@ -2328,7 +2320,11 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; int i, ocsp_status; +#if defined(OPENSSL_IS_AWSLC) + const uint8_t *status; +#else unsigned char *status; +#endif const unsigned char *p; CURLcode result = CURLE_OK; OCSP_RESPONSE *rsp = NULL; @@ -2426,7 +2422,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, goto end; } - for(i = 0; i < sk_X509_num(ch); i++) { + for(i = 0; i < (int)sk_X509_num(ch); i++) { X509 *issuer = sk_X509_value(ch, i); if(X509_check_issued(issuer, cert) == X509_V_OK) { id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); @@ -3732,6 +3728,15 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + /* When a user callback is installed to modify the SSL_CTX, + * we need to do the full initialization before calling it. + * See: #11800 */ + if(!backend->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(result) + return result; + backend->x509_store_setup = TRUE; + } Curl_set_in_callback(data, true); result = (*data->set.ssl.fsslctx)(data, backend->ctx, data->set.ssl.fsslctxp); @@ -3795,7 +3800,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -3880,7 +3885,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; } #endif - else if(backend->io_result == CURLE_AGAIN) { +#ifdef SSL_ERROR_WANT_RETRY_VERIFY + if(SSL_ERROR_WANT_RETRY_VERIFY == detail) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } +#endif + if(backend->io_result == CURLE_AGAIN) { return CURLE_OK; } else { @@ -3920,11 +3931,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, error_buffer */ strcpy(error_buffer, "SSL certificate verification failed"); } -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_IS_AWSLC)) - +#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ else if((lib == ERR_LIB_SSL) && @@ -4750,7 +4757,10 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ mdctx = EVP_MD_CTX_create(); if(!mdctx) return CURLE_OUT_OF_MEMORY; - EVP_DigestInit(mdctx, EVP_sha256()); + if(!EVP_DigestInit(mdctx, EVP_sha256())) { + EVP_MD_CTX_destroy(mdctx); + return CURLE_FAILED_INIT; + } EVP_DigestUpdate(mdctx, tmp, tmplen); EVP_DigestFinal_ex(mdctx, sha256sum, &len); EVP_MD_CTX_destroy(mdctx); diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index 76d3e24..a3e9d96 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c @@ -104,10 +104,6 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) ret = EINVAL; } *out_n = (int)nread; - /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", - len, nread, result)); - */ return ret; } @@ -128,7 +124,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) } *out_n = (int)nwritten; /* - DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", + CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", len, nwritten, result)); */ return ret; @@ -267,8 +263,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", - plainlen, nread, *err)); + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", + plainlen, nread, *err); return nread; } @@ -302,7 +298,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); rconn = backend->conn; - DEBUGF(LOG_CF(data, cf, "cf_send: %ld plain bytes", plainlen)); + CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes", plainlen); io_ctx.cf = cf; io_ctx.data = data; @@ -327,8 +323,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, &tlswritten); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - DEBUGF(LOG_CF(data, cf, "cf_send: EAGAIN after %zu bytes", - tlswritten_total)); + CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", + tlswritten_total); *err = CURLE_AGAIN; return -1; } @@ -344,7 +340,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, *err = CURLE_WRITE_ERROR; return -1; } - DEBUGF(LOG_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten)); + CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten); tlswritten_total += tlswritten; } diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 5dcf5ba..f6a5d44 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c @@ -793,8 +793,11 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, backend->cred->client_cert_store = client_cert_store; #endif - /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */ - if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, + /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as + long as the user did not set a legacy algorithm list + (CURLOPT_SSL_CIPHER_LIST). */ + if(!conn_config->cipher_list && + curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { char *ciphers13 = 0; @@ -807,9 +810,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, SCH_CREDENTIALS credentials = { 0 }; TLS_PARAMETERS tls_parameters = { 0 }; - CRYPTO_SETTINGS crypto_settings[4] = { 0 }; - UNICODE_STRING blocked_ccm_modes[1] = { 0 }; - UNICODE_STRING blocked_gcm_modes[1] = { 0 }; + CRYPTO_SETTINGS crypto_settings[4] = { { 0 } }; + UNICODE_STRING blocked_ccm_modes[1] = { { 0 } }; + UNICODE_STRING blocked_gcm_modes[1] = { { 0 } }; int crypto_settings_idx = 0; @@ -844,7 +847,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, /* reject too-long cipher names */ if(n > (LONGEST_ALG_ID - 1)) { - failf(data, "Cipher name too long, not checked."); + failf(data, "schannel: Cipher name too long, not checked"); return CURLE_SSL_CIPHER; } @@ -872,7 +875,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, disable_aes_ccm_sha256 = FALSE; } else { - failf(data, "Passed in an unknown TLS 1.3 cipher."); + failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp); return CURLE_SSL_CIPHER; } @@ -887,7 +890,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256 && disable_chacha_poly && disable_aes_ccm_8_sha256 && disable_aes_ccm_sha256) { - failf(data, "All available TLS 1.3 ciphers were disabled."); + failf(data, "schannel: All available TLS 1.3 ciphers were disabled"); return CURLE_SSL_CIPHER; } @@ -1010,7 +1013,9 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, &backend->cred->time_stamp); } else { - /* Pre-Windows 10 1809 */ + /* 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 + SCHANNEL_CRED is used. */ ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; SCHANNEL_CRED schannel_cred = { 0 }; @@ -1019,9 +1024,20 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, schannel_cred.grbitEnabledProtocols = enabled_protocols; if(ciphers) { + if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + infof(data, "schannel: WARNING: This version of Schannel may " + "negotiate a less-secure TLS version than TLS 1.3 because the " + "user set an algorithm cipher list."); + } + if(conn_config->cipher_list13) { + failf(data, "schannel: This version of Schannel does not support " + "setting an algorithm cipher list and TLS 1.3 cipher list at " + "the same time"); + return CURLE_SSL_CIPHER; + } result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to from connection ssl config"); + failf(data, "schannel: Failed setting algorithm cipher list"); return result; } } @@ -1158,7 +1174,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) { backend->cred = old_cred; - DEBUGF(infof(data, "schannel: re-using existing credential handle")); + DEBUGF(infof(data, "schannel: reusing existing credential handle")); /* increment the reference counter of the credential/session handle */ backend->cred->refcount++; @@ -1618,10 +1634,16 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef HAS_MANUAL_VERIFY_API if(conn_config->verifypeer && backend->use_manual_cred_validation) { + /* Certificate verification also verifies the hostname if verifyhost */ return Curl_verify_certificate(cf, data); } #endif + /* Verify the hostname manually when certificate verification is disabled, + because in that case Schannel won't verify it. */ + if(!conn_config->verifypeer && conn_config->verifyhost) + return Curl_verify_host(cf, data); + return CURLE_OK; } @@ -1634,7 +1656,8 @@ valid_cert_encoding(const CERT_CONTEXT *cert_context) (cert_context->cbCertEncoded > 0); } -typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg); +typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *arg); static void traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, @@ -1642,19 +1665,32 @@ traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, { const CERT_CONTEXT *current_context = NULL; bool should_continue = true; + bool first = true; + bool reverse_order = false; while(should_continue && (current_context = CertEnumCertificatesInStore( context->hCertStore, - current_context)) != NULL) - should_continue = func(current_context, arg); + current_context)) != NULL) { + /* Windows 11 22H2 OS Build 22621.674 or higher enumerates certificates in + leaf-to-root order while all previous versions of Windows enumerate + certificates in root-to-leaf order. Determine the order of enumeration + by comparing SECPKG_ATTR_REMOTE_CERT_CONTEXT's pbCertContext with the + first certificate's pbCertContext. */ + if(first && context->pbCertEncoded != current_context->pbCertEncoded) + reverse_order = true; + should_continue = func(current_context, reverse_order, arg); + first = false; + } if(current_context) CertFreeCertificateContext(current_context); } static bool -cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count) +cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *certs_count) { + (void)reverse_order; /* unused */ if(valid_cert_encoding(ccert_context)) (*(int *)certs_count)++; return true; @@ -1669,14 +1705,16 @@ struct Adder_args }; static bool -add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) +add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, + void *raw_arg) { struct Adder_args *args = (struct Adder_args*)raw_arg; args->result = CURLE_OK; if(valid_cert_encoding(ccert_context)) { const char *beg = (const char *) ccert_context->pbCertEncoded; const char *end = beg + ccert_context->cbCertEncoded; - int insert_index = (args->certs_count - 1) - args->idx; + int insert_index = reverse_order ? (args->certs_count - 1) - args->idx : + args->idx; args->result = Curl_extract_certinfo(args->data, insert_index, beg, end); args->idx++; @@ -1758,7 +1796,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif - /* save the current session data for possible re-use */ + /* save the current session data for possible reuse */ if(ssl_config->primary.sessionid) { bool incache; bool added = FALSE; diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h index b8cb494..be23567 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.h +++ b/Utilities/cmcurl/lib/vtls/schannel.h @@ -76,6 +76,9 @@ extern const struct Curl_ssl Curl_ssl_schannel; +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data); + CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, struct Curl_easy *data); diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h index d8b6cce..edb20bc 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_int.h +++ b/Utilities/cmcurl/lib/vtls/schannel_int.h @@ -43,6 +43,58 @@ #define HAS_CLIENT_CERT_PATH #endif +#ifndef CRYPT_DECODE_NOCOPY_FLAG +#define CRYPT_DECODE_NOCOPY_FLAG 0x1 +#endif + +#ifndef CRYPT_DECODE_ALLOC_FLAG +#define CRYPT_DECODE_ALLOC_FLAG 0x8000 +#endif + +#ifndef CERT_ALT_NAME_DNS_NAME +#define CERT_ALT_NAME_DNS_NAME 3 +#endif + +#ifndef CERT_ALT_NAME_IP_ADDRESS +#define CERT_ALT_NAME_IP_ADDRESS 8 +#endif + + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +/* Original mingw is missing CERT structs or they're disabled. + Refer to w32api-5.0.2-mingw32-dev\include\wincrypt.h. */ + +/* !checksrc! disable TYPEDEFSTRUCT 4 */ +typedef struct _CERT_OTHER_NAME { + LPSTR pszObjId; + CRYPT_OBJID_BLOB Value; +} CERT_OTHER_NAME, *PCERT_OTHER_NAME; + +typedef struct _CERT_ALT_NAME_ENTRY { + DWORD dwAltNameChoice; + union { + PCERT_OTHER_NAME pOtherName; + LPWSTR pwszRfc822Name; + LPWSTR pwszDNSName; + CERT_NAME_BLOB DirectoryName; + LPWSTR pwszURL; + CRYPT_DATA_BLOB IPAddress; + LPSTR pszRegisteredID; + }; +} CERT_ALT_NAME_ENTRY, *PCERT_ALT_NAME_ENTRY; + +typedef struct _CERT_ALT_NAME_INFO { + DWORD cAltEntry; + PCERT_ALT_NAME_ENTRY rgAltEntry; +} CERT_ALT_NAME_INFO, *PCERT_ALT_NAME_INFO; + +typedef struct _CRYPT_DECODE_PARA { + DWORD cbSize; + PFN_CRYPT_ALLOC pfnAlloc; + PFN_CRYPT_FREE pfnFree; +} CRYPT_DECODE_PARA, *PCRYPT_DECODE_PARA; +#endif + #ifndef SCH_CREDENTIALS_VERSION #define SCH_CREDENTIALS_VERSION 0x00000005 diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index c582ee4..a5d5c98 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c @@ -39,8 +39,6 @@ #include "schannel.h" #include "schannel_int.h" -#ifdef HAS_MANUAL_VERIFY_API - #include "vtls.h" #include "vtls_int.h" #include "sendf.h" @@ -56,6 +54,9 @@ #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) + +#ifdef HAS_MANUAL_VERIFY_API + #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" #define END_CERT "\n-----END CERTIFICATE-----" @@ -330,6 +331,8 @@ cleanup: return result; } +#endif /* HAS_MANUAL_VERIFY_API */ + /* * 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 @@ -353,10 +356,10 @@ static DWORD cert_get_name_string(struct Curl_easy *data, LPTSTR current_pos = NULL; DWORD i; +#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CertGetNameString will provide the 8-bit character string without * any decoding */ DWORD name_flags = @@ -368,8 +371,8 @@ static DWORD cert_get_name_string(struct Curl_easy *data, host_names, length); return actual_length; -#endif } +#endif compute_content = host_names != NULL && length != 0; @@ -457,17 +460,34 @@ static DWORD cert_get_name_string(struct Curl_easy *data, return actual_length; } -static CURLcode verify_host(struct Curl_easy *data, - CERT_CONTEXT *pCertContextServer, - const char *conn_hostname) +/* Verify the server's hostname */ +CURLcode Curl_verify_host(struct Curl_cfilter *cf, + struct Curl_easy *data) { + struct ssl_connect_data *connssl = cf->ctx; + SECURITY_STATUS sspi_status; CURLcode result = CURLE_PEER_FAILED_VERIFICATION; + CERT_CONTEXT *pCertContextServer = NULL; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; + const char *conn_hostname = connssl->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; + sspi_status = + s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); + + if((sspi_status != SEC_E_OK) || !pCertContextServer) { + char buffer[STRERROR_LEN]; + failf(data, "schannel: Failed to read remote certificate context: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + result = CURLE_PEER_FAILED_VERIFICATION; + goto cleanup; + } + /* Determine the size of the string needed for the cert hostname */ len = cert_get_name_string(data, pCertContextServer, NULL, 0); if(len == 0) { @@ -498,10 +518,9 @@ static CURLcode verify_host(struct Curl_easy *data, goto cleanup; } - /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output - * will contain all DNS names, where each name is null-terminated - * and the last DNS name is double null-terminated. Due to this - * encoding, use the length of the buffer to iterate over all names. + /* cert_hostname_buff contains all DNS names, where each name is + * null-terminated and the last DNS name is double null-terminated. Due to + * this encoding, use the length of the buffer to iterate over all names. */ result = CURLE_PEER_FAILED_VERIFICATION; while(cert_hostname_buff_index < len && @@ -560,9 +579,15 @@ static CURLcode verify_host(struct Curl_easy *data, cleanup: Curl_safefree(cert_hostname_buff); + if(pCertContextServer) + CertFreeCertificateContext(pCertContextServer); + return result; } + +#ifdef HAS_MANUAL_VERIFY_API +/* Verify the server's certificate and hostname */ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -721,7 +746,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(result == CURLE_OK) { if(conn_config->verifyhost) { - result = verify_host(data, pCertContextServer, connssl->hostname); + result = Curl_verify_host(cf, data); } } diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c index 32bb3a5..e6a114a 100644 --- a/Utilities/cmcurl/lib/vtls/sectransp.c +++ b/Utilities/cmcurl/lib/vtls/sectransp.c @@ -845,8 +845,8 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection, DEBUGASSERT(data); nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", - *dataLength, nread, result)); + CURL_TRC_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", + *dataLength, nread, result); if(nread < 0) { switch(result) { case CURLE_OK: @@ -885,8 +885,8 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection, DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result); - DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", - *dataLength, nwritten, result)); + CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", + *dataLength, nwritten, result); if(nwritten <= 0) { if(result == CURLE_AGAIN) { rtn = errSSLWouldBlock; @@ -1086,7 +1086,6 @@ static OSStatus CopyIdentityWithLabel(char *label, CFArrayRef keys_list; CFIndex keys_list_count; CFIndex i; - CFStringRef common_name; /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. kSecClassIdentity was introduced in Lion. If both exist, let's use them @@ -1134,6 +1133,7 @@ static OSStatus CopyIdentityWithLabel(char *label, (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i); err = SecIdentityCopyCertificate(identity, &cert); if(err == noErr) { + CFStringRef common_name = NULL; OSStatus copy_status = noErr; #if CURL_BUILD_IOS common_name = SecCertificateCopySubjectSummary(cert); @@ -1149,7 +1149,8 @@ static OSStatus CopyIdentityWithLabel(char *label, status = noErr; break; } - CFRelease(common_name); + if(common_name) + CFRelease(common_name); } CFRelease(cert); } @@ -1293,7 +1294,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath, /* This code was borrowed from nss.c, with some modifications: * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. + * be loaded as a PEM or a nickname. * * returns 1 for a file * returns 0 for not a file @@ -1611,7 +1612,7 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, 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 %d and ending position %d", + " starting position %zd and ending position %zd", ciphers, cipher_start - ciphers, cipher_end - ciphers); @@ -1662,7 +1663,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step1")); + CURL_TRC_CF(data, cf, "connect_step1"); GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -2069,7 +2070,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } /* Informational message */ - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } /* If there isn't one, then let's make one up! This has to be done prior to starting the handshake. */ @@ -2293,7 +2294,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); + CURL_TRC_CF(data, cf, "append_cert for CA failed"); result = rc; goto out; } @@ -2307,7 +2308,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - DEBUGF(LOG_CF(data, cf, "append_cert for CA failed")); + CURL_TRC_CF(data, cf, "append_cert for CA failed"); result = rc; goto out; } @@ -2323,7 +2324,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, goto out; } - DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n)); + CURL_TRC_CF(data, cf, "setting %d trust anchors", n); ret = SecTrustSetAnchorCertificates(trust, array); if(ret != noErr) { failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); @@ -2345,11 +2346,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf, switch(trust_eval) { case kSecTrustResultUnspecified: /* what does this really mean? */ - DEBUGF(LOG_CF(data, cf, "trust result: Unspecified")); + CURL_TRC_CF(data, cf, "trust result: Unspecified"); result = CURLE_OK; goto out; case kSecTrustResultProceed: - DEBUGF(LOG_CF(data, cf, "trust result: Proceed")); + CURL_TRC_CF(data, cf, "trust result: Proceed"); result = CURLE_OK; goto out; @@ -2382,7 +2383,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, size_t buflen; if(ca_info_blob) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob")); + CURL_TRC_CF(data, cf, "verify_peer, CA from config blob"); certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); if(!certbuf) { return CURLE_OUT_OF_MEMORY; @@ -2392,7 +2393,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf, certbuf[ca_info_blob->len]='\0'; } else if(cafile) { - DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile)); + CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile); if(read_cert(cafile, &certbuf, &buflen) < 0) { failf(data, "SSL: failed to read or invalid CA certificate"); return CURLE_SSL_CACERT_BADFILE; @@ -2432,7 +2433,6 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, SecTrustRef trust; OSStatus ret; SecKeyRef keyRef; - OSStatus success; ret = SSLCopyPeerTrust(ctx, &trust); if(ret != noErr || !trust) @@ -2452,11 +2452,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, #elif SECTRANSP_PINNEDPUBKEY_V2 - 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 */ @@ -2484,7 +2487,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, spkiHeaderLength = 23; break; default: - infof(data, "SSL: unhandled public key length: %d", pubkeylen); + infof(data, "SSL: unhandled public key length: %zu", pubkeylen); #elif SECTRANSP_PINNEDPUBKEY_V2 default: /* ecDSA secp256r1 pubkeylen == 91 header already included? @@ -2533,7 +2536,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); DEBUGASSERT(backend); - DEBUGF(LOG_CF(data, cf, "connect_step2")); + CURL_TRC_CF(data, cf, "connect_step2"); /* Here goes nothing: */ check_handshake: @@ -3000,7 +3003,7 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, struct ssl_connect_data *connssl = cf->ctx; CURLcode result; - DEBUGF(LOG_CF(data, cf, "connect_step3")); + CURL_TRC_CF(data, cf, "connect_step3"); /* There is no step 3! * Well, okay, let's collect server certificates, and if verbose mode is on, * let's print the details of the server certificates. */ @@ -3109,7 +3112,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } if(ssl_connect_done == connssl->connecting_state) { - DEBUGF(LOG_CF(data, cf, "connected")); + CURL_TRC_CF(data, cf, "connected"); connssl->state = ssl_connection_complete; *done = TRUE; } @@ -3156,7 +3159,7 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); if(backend->ssl_ctx) { - DEBUGF(LOG_CF(data, cf, "close")); + CURL_TRC_CF(data, cf, "close"); (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS if(SSLCreateContext) @@ -3202,7 +3205,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf, what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); - DEBUGF(LOG_CF(data, cf, "shutdown")); + CURL_TRC_CF(data, cf, "shutdown"); while(loop--) { if(what < 0) { /* anything that gets here is fatally bad */ @@ -3264,7 +3267,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend); if(backend->ssl_ctx) { /* SSL is in use */ - DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending")); + CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending"); err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) return buffer > 0UL; @@ -3299,6 +3302,7 @@ static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ unsigned char *sha256sum, /* output */ size_t sha256len) { + (void)sha256len; assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); return CURLE_OK; diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index 510bcfe..38a20e8 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c @@ -417,7 +417,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, DEBUGASSERT(ssl_config->primary.sessionid); if(!ssl_config->primary.sessionid || !data->state.session) - /* session ID re-use is disabled or the session cache has not been + /* session ID reuse is disabled or the session cache has not been setup */ return TRUE; @@ -635,19 +635,16 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_connect_data *connssl = cf->ctx; 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) { - /* write mode */ - socks[0] = sock; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = sock; - return GETSOCK_READSOCK(0); - } + if(sock == CURL_SOCKET_BAD) + return GETSOCK_BLANK; + + if(connssl->connecting_state == ssl_connect_2_writing) { + /* we are only interested in writing */ + socks[0] = sock; + return GETSOCK_WRITESOCK(0); } - return GETSOCK_BLANK; + socks[0] = sock; + return GETSOCK_READSOCK(0); } /* Selects an SSL crypto engine @@ -883,6 +880,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, FILE *fp; unsigned char *buf = NULL, *pem_ptr = NULL; CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; +#ifdef CURL_DISABLE_VERBOSE_STRINGS + (void)data; +#endif /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) @@ -1240,12 +1240,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_sectransp; #elif defined(USE_GNUTLS) &Curl_ssl_gnutls; -#elif defined(USE_GSKIT) - &Curl_ssl_gskit; #elif defined(USE_MBEDTLS) &Curl_ssl_mbedtls; -#elif defined(USE_NSS) - &Curl_ssl_nss; #elif defined(USE_RUSTLS) &Curl_ssl_rustls; #elif defined(USE_OPENSSL) @@ -1268,15 +1264,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_GNUTLS) &Curl_ssl_gnutls, #endif -#if defined(USE_GSKIT) - &Curl_ssl_gskit, -#endif #if defined(USE_MBEDTLS) &Curl_ssl_mbedtls, #endif -#if defined(USE_NSS) - &Curl_ssl_nss, -#endif #if defined(USE_OPENSSL) &Curl_ssl_openssl, #endif @@ -1522,6 +1512,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, } CF_DATA_SAVE(save, cf, data); + CURL_TRC_CF(data, cf, "cf_connect()"); (void)connssl; DEBUGASSERT(data->conn); DEBUGASSERT(data->conn == cf->conn); @@ -1551,6 +1542,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, DEBUGASSERT(connssl->state == ssl_connection_complete); } out: + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1601,7 +1593,7 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, /* eof */ *err = CURLE_OK; } - DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err)); + CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err); CF_DATA_RESTORE(cf, save); return nread; } @@ -1611,12 +1603,17 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf, curl_socket_t *socks) { struct cf_call_data save; - int result; + int fds = GETSOCK_BLANK; - CF_DATA_SAVE(save, cf, data); - result = Curl_ssl->get_select_socks(cf, data, socks); - CF_DATA_RESTORE(cf, save); - return result; + if(!cf->next->connected) { + fds = cf->next->cft->get_select_socks(cf->next, data, socks); + } + else if(!cf->connected) { + CF_DATA_SAVE(save, cf, data); + fds = Curl_ssl->get_select_socks(cf, data, socks); + CF_DATA_RESTORE(cf, save); + } + return fds; } static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf, @@ -1702,7 +1699,7 @@ static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, struct Curl_cftype Curl_cft_ssl = { "SSL", CF_TYPE_SSL, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, @@ -1720,7 +1717,7 @@ struct Curl_cftype Curl_cft_ssl = { struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", CF_TYPE_SSL, - CURL_LOG_DEFAULT, + CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, ssl_cf_close, diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index 3516247..8ad1cf6 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h @@ -43,7 +43,7 @@ struct Curl_ssl_session; #define VTLS_INFOF_NO_ALPN \ "ALPN: server did not agree on a protocol. Uses default." #define VTLS_INFOF_ALPN_OFFER_1STR \ - "ALPN: offers %s" + "ALPN: curl offers %s" #define VTLS_INFOF_ALPN_ACCEPTED_1STR \ ALPN_ACCEPTED "%s" #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h index fe0115c..a6e4544 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_int.h +++ b/Utilities/cmcurl/lib/vtls/vtls_int.h @@ -217,8 +217,6 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf, #include "openssl.h" /* OpenSSL versions */ #include "gtls.h" /* GnuTLS versions */ -#include "nssg.h" /* NSS versions */ -#include "gskit.h" /* Global Secure ToolKit versions */ #include "wolfssl.h" /* wolfSSL versions */ #include "schannel.h" /* Schannel SSPI version */ #include "sectransp.h" /* SecureTransport (Darwin) version */ diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index 6cfc201..5f15720 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c @@ -92,9 +92,9 @@ #endif struct wolfssl_ssl_backend_data { - SSL_CTX* ctx; - SSL* handle; - CURLcode io_result; /* result of last BIO cfilter operation */ + WOLFSSL_CTX *ctx; + WOLFSSL *handle; + CURLcode io_result; /* result of last BIO cfilter operation */ }; #ifdef OPENSSL_EXTRA @@ -180,7 +180,8 @@ wolfssl_log_tls12_secret(SSL *ssl) } #endif - if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) { + if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != + SSL_SUCCESS) { return; } @@ -290,11 +291,11 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d", - blen, nwritten, 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) - BIO_set_retry_read(bio); + BIO_set_retry_write(bio); return (int)nwritten; } @@ -315,8 +316,7 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); backend->io_result = result; - DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d", - blen, nread, result)); + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); wolfSSL_BIO_clear_retry_flags(bio); if(nread < 0 && CURLE_AGAIN == result) BIO_set_retry_read(bio); @@ -327,17 +327,17 @@ static WOLFSSL_BIO_METHOD *bio_cf_method = NULL; static void bio_cf_init_methods(void) { - bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); - wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); - wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); - wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); - wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); - wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); + bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); + wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); + wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); + wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create); + wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); } static void bio_cf_free_methods(void) { - wolfSSL_BIO_meth_free(bio_cf_method); + wolfSSL_BIO_meth_free(bio_cf_method); } #else /* USE_BIO_CHAIN */ @@ -361,7 +361,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) 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); - SSL_METHOD* req_method = NULL; + WOLFSSL_METHOD* req_method = NULL; #ifdef HAVE_LIBOQS word16 oqsAlg = 0; size_t idx = 0; @@ -372,6 +372,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #else #define use_sni(x) Curl_nop_stmt #endif + bool imported_native_ca = false; bool imported_ca_info_blob = false; DEBUGASSERT(backend); @@ -445,8 +446,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } if(backend->ctx) - SSL_CTX_free(backend->ctx); - backend->ctx = SSL_CTX_new(req_method); + wolfSSL_CTX_free(backend->ctx); + backend->ctx = wolfSSL_CTX_new(req_method); if(!backend->ctx) { failf(data, "SSL: couldn't create a context"); @@ -507,13 +508,31 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } } +#ifndef NO_FILESYSTEM + /* 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 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) { - failf(data, "error importing CA certificate blob"); - return CURLE_SSL_CACERT_BADFILE; + 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; + } } else { imported_ca_info_blob = true; @@ -524,10 +543,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #ifndef NO_FILESYSTEM /* load trusted cacert */ if(conn_config->CAfile) { - if(1 != SSL_CTX_load_verify_locations(backend->ctx, - conn_config->CAfile, - conn_config->CApath)) { - if(conn_config->verifypeer && !imported_ca_info_blob) { + if(1 != wolfSSL_CTX_load_verify_locations(backend->ctx, + conn_config->CAfile, + conn_config->CApath)) { + 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", @@ -558,17 +578,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ssl_config->primary.clientcert && ssl_config->key) { int file_type = do_file_type(ssl_config->cert_type); - if(SSL_CTX_use_certificate_file(backend->ctx, - ssl_config->primary.clientcert, - file_type) != 1) { + if(wolfSSL_CTX_use_certificate_file(backend->ctx, + ssl_config->primary.clientcert, + file_type) != 1) { failf(data, "unable to use client certificate (no key or wrong pass" " phrase?)"); return CURLE_SSL_CONNECT_ERROR; } file_type = do_file_type(ssl_config->key_type); - if(SSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, - file_type) != 1) { + if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key, + file_type) != 1) { failf(data, "unable to set private key"); return CURLE_SSL_CONNECT_ERROR; } @@ -579,10 +599,9 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, - conn_config->verifypeer?SSL_VERIFY_PEER: - SSL_VERIFY_NONE, - NULL); + wolfSSL_CTX_set_verify(backend->ctx, + conn_config->verifypeer?SSL_VERIFY_PEER: + SSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(sni) { @@ -631,8 +650,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Let's make an SSL structure */ if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); + wolfSSL_free(backend->handle); + backend->handle = wolfSSL_new(backend->ctx); if(!backend->handle) { failf(data, "SSL: couldn't create a handle"); return CURLE_OUT_OF_MEMORY; @@ -692,7 +711,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) infof(data, "Can't use session ID, going on without"); } else - infof(data, "SSL re-using session ID"); + infof(data, "SSL reusing session ID"); } Curl_ssl_sessionid_unlock(data); } @@ -710,7 +729,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #else /* USE_BIO_CHAIN */ /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) { + if(!wolfSSL_set_fd(backend->handle, + (int)Curl_conn_cf_get_socket(cf, data))) { failf(data, "SSL: SSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } @@ -735,7 +755,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); /* Enable RFC2818 checks */ if(conn_config->verifyhost) { @@ -745,7 +765,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } - ret = SSL_connect(backend->handle); + ret = wolfSSL_connect(backend->handle); #ifdef OPENSSL_EXTRA if(Curl_tls_keylog_enabled()) { @@ -773,7 +793,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret != 1) { char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int detail = SSL_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; @@ -831,7 +851,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } else { failf(data, "SSL_connect failed with error %d: %s", detail, - ERR_error_string(detail, error_buffer)); + wolfSSL_ERR_error_string(detail, error_buffer)); return CURLE_SSL_CONNECT_ERROR; } } @@ -845,7 +865,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_asn1Element *pubkey; CURLcode result; - x509 = SSL_get_peer_certificate(backend->handle); + x509 = wolfSSL_get_peer_certificate(backend->handle); if(!x509) { failf(data, "SSL: failed retrieving server certificate"); return CURLE_SSL_PINNEDPUBKEYNOTMATCH; @@ -931,8 +951,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) bool incache; bool added = FALSE; void *old_ssl_sessionid = NULL; - /* SSL_get1_session allocates memory that has to be freed. */ - SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle); + /* wolfSSL_get1_session allocates memory that has to be freed. */ + WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); if(our_ssl_sessionid) { Curl_ssl_sessionid_lock(data); @@ -949,7 +969,7 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL); if(result) { Curl_ssl_sessionid_unlock(data); - SSL_SESSION_free(our_ssl_sessionid); + wolfSSL_SESSION_free(our_ssl_sessionid); failf(data, "failed to store ssl session"); return result; } @@ -961,7 +981,7 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) if(!added) { /* If the session info wasn't added to the cache, free our copy. */ - SSL_SESSION_free(our_ssl_sessionid); + wolfSSL_SESSION_free(our_ssl_sessionid); } } } @@ -987,35 +1007,34 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); - rc = SSL_write(backend->handle, mem, memlen); + rc = wolfSSL_write(backend->handle, mem, memlen); if(rc <= 0) { - int err = SSL_get_error(backend->handle, rc); + int err = wolfSSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_write() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; default: if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; return -1; } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", - len, rc, err)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err); failf(data, "SSL write: %s, errno %d", - ERR_error_string(err, error_buffer), + wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_SEND_ERROR; return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc)); + CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc); return rc; } @@ -1033,13 +1052,13 @@ static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) char buf[32]; /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)SSL_shutdown(backend->handle); - SSL_free(backend->handle); + (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf)); + (void)wolfSSL_shutdown(backend->handle); + wolfSSL_free(backend->handle); backend->handle = NULL; } if(backend->ctx) { - SSL_CTX_free(backend->ctx); + wolfSSL_CTX_free(backend->ctx); backend->ctx = NULL; } } @@ -1058,17 +1077,17 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, DEBUGASSERT(backend); - ERR_clear_error(); + wolfSSL_ERR_clear_error(); *curlcode = CURLE_OK; - nread = SSL_read(backend->handle, buf, buffsize); + nread = wolfSSL_read(backend->handle, buf, buffsize); if(nread <= 0) { - int err = SSL_get_error(backend->handle, nread); + int err = wolfSSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); *curlcode = CURLE_OK; return 0; case SSL_ERROR_NONE: @@ -1076,30 +1095,30 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, case SSL_ERROR_WANT_READ: /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); + /* there's data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; default: if(backend->io_result == CURLE_AGAIN) { - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; return -1; } failf(data, "SSL read: %s, errno %d", - ERR_error_string(err, error_buffer), SOCKERRNO); + wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO); *curlcode = CURLE_RECV_ERROR; return -1; } } - DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread)); + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread); return nread; } static void wolfssl_session_free(void *ptr) { - SSL_SESSION_free(ptr); + wolfSSL_SESSION_free(ptr); } @@ -1147,7 +1166,7 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, backend = (struct wolfssl_ssl_backend_data *)ctx->backend; if(backend->handle) /* SSL is in use */ - return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; + return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; else return FALSE; } @@ -1169,8 +1188,8 @@ static int wolfssl_shutdown(struct Curl_cfilter *cf, backend = (struct wolfssl_ssl_backend_data *)ctx->backend; if(backend->handle) { - ERR_clear_error(); - SSL_free(backend->handle); + wolfSSL_ERR_clear_error(); + wolfSSL_free(backend->handle); backend->handle = NULL; } return retval; @@ -1333,7 +1352,8 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ { wc_Sha256 SHA256pw; (void)unused; - wc_InitSha256(&SHA256pw); + if(wc_InitSha256(&SHA256pw)) + return CURLE_FAILED_INIT; wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); wc_Sha256Final(&SHA256pw, sha256sum); return CURLE_OK; diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index acf8bdb..c3fd3a3 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c @@ -24,24 +24,18 @@ #include "curl_setup.h" -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) -#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) +#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL) #define WANT_PARSEX509 /* uses Curl_parseX509() */ #endif -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #define WANT_PARSEX509 /* ... uses Curl_parseX509() */ #endif -#if defined(USE_GSKIT) -#define WANT_VERIFYHOST /* uses Curl_verifyhost () */ -#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ -#endif - #include <curl/curl.h> #include "urldata.h" #include "strcase.h" @@ -973,7 +967,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, infof(data, " ECC Public Key (%lu bits)", len); if(data->set.ssl.certinfo) { char q[sizeof(len) * 8 / 3 + 1]; - (void)msnprintf(q, sizeof(q), "%lu", len); + (void)msnprintf(q, sizeof(q), "%zu", len); if(ssl_push_certinfo(data, certnum, "ECC Public Key", q)) return 1; } @@ -1007,7 +1001,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, infof(data, " RSA Public Key (%lu bits)", len); if(data->set.ssl.certinfo) { char r[sizeof(len) * 8 / 3 + 1]; - msnprintf(r, sizeof(r), "%lu", len); + msnprintf(r, sizeof(r), "%zu", len); if(ssl_push_certinfo(data, certnum, "RSA Public Key", r)) return 1; } @@ -1261,8 +1255,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, #endif /* WANT_EXTRACT_CERTINFO */ -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ #ifdef WANT_VERIFYHOST diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h index 5496de4..23a67b8 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.h +++ b/Utilities/cmcurl/lib/vtls/x509asn1.h @@ -27,8 +27,8 @@ #include "curl_setup.h" -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) #include "cfilters.h" #include "urldata.h" @@ -76,6 +76,5 @@ 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); -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */ #endif /* HEADER_CURL_X509ASN1_H */ @@ -508,6 +508,7 @@ CMAKE_CXX_SOURCES="\ cmake \ cmakemain \ cmcmd \ + cm_fileno \ " if ${cmake_system_mingw}; then |